Compare commits

..
4 Commits
Author SHA1 Message Date
0f3d884786 WireGuard outbound: Keep the device when its setup fails (#7151)
https://github.com/XTLS/Xray-core/pull/7151#issuecomment-6186150131

Fixes https://github.com/XTLS/Xray-core/pull/7151#issuecomment-6190189547

---------

Co-authored-by: LjhAUMEM <llnu14702@gmail.com>
2026-10-16 09:50:47 +00:00
da2248c516 Hysteria client: Reset stalled writes when the connection is closed (#7172)
https://github.com/XTLS/Xray-core/pull/7172#issuecomment-6208589708

Fixes https://github.com/XTLS/Xray-core/issues/7169#issuecomment-6204450876

---------

Co-authored-by: LjhAUMEM <llnu14702@gmail.com>
2026-10-16 15:25:17 +08:00
fe2ae23f42 Sockopt: Cancel dialing after failed to bind interface, external controllers included such as TUN inbound's (#7173)
https://github.com/XTLS/Xray-core/pull/7161#issuecomment-6185101741

Fixes https://github.com/XTLS/Xray-core/issues/7160#issuecomment-6200788453

---------

Co-authored-by: Gleb <a1212gleb@gmail.com>
2026-10-16 06:37:52 +00:00
d15966c76c Fixes: Regard int() as int32() by default, which should be a coding standard (#7155)
https://github.com/XTLS/Xray-core/pull/7155#issuecomment-6208455069

Fixes https://github.com/XTLS/Xray-core/pull/7148#issuecomment-6173633056

---------

Co-authored-by: Denis Orlov <freshcrowd@gmail.com>
Co-authored-by: Milomilo777 <117558067+Milomilo777@users.noreply.github.com>
2026-10-16 05:12:52 +00:00
21 changed files with 309 additions and 243 deletions
+1 -1
View File
@@ -109,7 +109,7 @@ func (h *HealthPingRTTS) getStatistics() *HealthPingStats {
stats.Min = 0
return stats
}
stats.Average = time.Duration(int(sum) / cnt)
stats.Average = sum / time.Duration(cnt)
var std float64
if cnt < 2 {
// no enough data for standard deviation, we assume it's half of the average rtt
+6 -9
View File
@@ -180,14 +180,14 @@ type VisionReader struct {
isUplink bool
conn net.Conn
input *bytes.Reader
rawInput **bytes.Buffer
rawInput *bytes.Buffer
ob *session.Outbound
// internal
directReadCounter stats.Counter
}
func NewVisionReader(reader buf.Reader, trafficState *TrafficState, isUplink bool, ctx context.Context, conn net.Conn, input *bytes.Reader, rawInput **bytes.Buffer, ob *session.Outbound) *VisionReader {
func NewVisionReader(reader buf.Reader, trafficState *TrafficState, isUplink bool, ctx context.Context, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, ob *session.Outbound) *VisionReader {
return &VisionReader{
Reader: reader,
trafficState: trafficState,
@@ -261,15 +261,12 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
if inputBuffer, err := buf.ReadFrom(w.input); err == nil && !inputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, inputBuffer)
}
if rawInputBuffer, err := buf.ReadFrom(w.rawInput); err == nil && !rawInputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
}
*w.input = bytes.Reader{} // release memory
w.input = nil
if raw := *w.rawInput; raw != nil {
if rawInputBuffer, err := buf.ReadFrom(raw); err == nil && !rawInputBuffer.IsEmpty() {
buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
}
*raw = bytes.Buffer{} // release memory
}
*w.rawInput = bytes.Buffer{} // release memory
w.rawInput = nil
if inbound := session.InboundFromContext(w.ctx); inbound != nil && inbound.Conn != nil {
+1 -1
View File
@@ -153,7 +153,7 @@ func parsePlainUDPPacket(sessionID, packetID uint64, bodyPlain []byte) (DecodedU
return DecodedUDPPacket{}, ErrBadHeaderType
}
epoch := binary.BigEndian.Uint64(bodyPlain[1:9])
diff := int(math.Abs(float64(time.Now().Unix() - int64(epoch))))
diff := math.Abs(float64(time.Now().Unix() - int64(epoch)))
if diff > 30 {
return DecodedUDPPacket{}, ErrBadTimestamp
}
+2 -2
View File
@@ -49,12 +49,12 @@ func (f *SlidingWindow) Add(counter uint64) {
if counter > f.last {
lastBlockIndex := f.last >> swBlockBitLog
diff := int(blockIndex - lastBlockIndex)
diff := blockIndex - lastBlockIndex
if diff > swRingBlocks {
diff = swRingBlocks
}
for i := 0; i < diff; i++ {
for i := 0; i < int(diff); i++ {
lastBlockIndex = (lastBlockIndex + 1) & swBlockMask
f.ring[lastBlockIndex] = 0
}
+2 -2
View File
@@ -254,7 +254,7 @@ func ReadClientRequestHeaderWithFixed(reader *StreamReader, fixedChunk []byte) (
}
epoch := binary.BigEndian.Uint64(plainFixed[1:9])
diff := int(math.Abs(float64(time.Now().Unix() - int64(epoch))))
diff := math.Abs(float64(time.Now().Unix() - int64(epoch)))
if diff > 30 {
return nil, ErrBadTimestamp
}
@@ -431,7 +431,7 @@ func ReadTCPResponse(r io.Reader, method *CipherMethod, psk []byte, clientSalt [
}
serverEpoch := binary.BigEndian.Uint64(decryptedFixed[1:9])
diff := int(math.Abs(float64(time.Now().Unix() - int64(serverEpoch))))
diff := math.Abs(float64(time.Now().Unix() - int64(serverEpoch)))
if diff > 30 {
return nil, ErrBadTimestamp
}
+7 -2
View File
@@ -144,7 +144,8 @@ func (t *Handler) Start() error {
errors.LogInfo(context.Background(), "[tun] falied to set interface > iface == nil")
return errors.New("iface not found")
}
return c.Control(func(fd uintptr) {
var controlErr, sockErr error
controlErr = c.Control(func(fd uintptr) {
addrPort, _ := netip.ParseAddrPort(address)
// skip loopback
if addrPort.Addr().IsLoopback() || strings.HasPrefix(strings.ToLower(address), "localhost:") {
@@ -152,9 +153,13 @@ func (t *Handler) Start() error {
}
err := setinterface(network, address, fd, iface)
if err != nil {
errors.LogInfoInner(context.Background(), err, "[tun] falied to set interface")
sockErr = errors.New("[tun] falied to set interface").Base(err)
}
})
if sockErr != nil {
return sockErr
}
return controlErr
})
}
+15 -6
View File
@@ -6,9 +6,11 @@ import (
gotls "crypto/tls"
"encoding/base64"
"io"
"reflect"
"strconv"
"strings"
"time"
"unsafe"
"github.com/xtls/xray-core/app/dispatcher"
"github.com/xtls/xray-core/app/reverse"
@@ -546,7 +548,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
}
var input *bytes.Reader
var rawInput **bytes.Buffer
var rawInput *bytes.Buffer
switch requestAddons.Flow {
case vless.XRV:
if account.Flow == requestAddons.Flow {
@@ -558,23 +560,30 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
inbound.CanSpliceCopy = 3
fallthrough // we will break Mux connections that contain TCP requests
case protocol.RequestCommandTCP:
var target net.Conn
var t reflect.Type
var p uintptr
if commonConn, ok := connection.(*encryption.CommonConn); ok {
if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransportWithoutSecurity(iConn) {
inbound.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport / another securityConn should not be penetrated
}
target = commonConn
t = reflect.TypeOf(commonConn).Elem()
p = uintptr(unsafe.Pointer(commonConn))
} else if tlsConn, ok := iConn.(*tls.Conn); ok {
if tlsConn.ConnectionState().Version != gotls.VersionTLS13 {
return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version)
}
target = tlsConn.Conn
t = reflect.TypeOf(tlsConn.Conn).Elem()
p = uintptr(unsafe.Pointer(tlsConn.Conn))
} else if realityConn, ok := iConn.(*reality.Conn); ok {
target = realityConn.Conn
t = reflect.TypeOf(realityConn.Conn).Elem()
p = uintptr(unsafe.Pointer(realityConn.Conn))
} else {
return errors.New("XTLS only supports TLS and REALITY directly for now.")
}
input, rawInput = vless.AccessTLSBuffers(target)
i, _ := t.FieldByName("input")
r, _ := t.FieldByName("rawInput")
input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset))
rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset))
}
} else {
return errors.New("account " + account.ID.String() + " is not able to use the flow " + requestAddons.Flow)
+17 -7
View File
@@ -5,9 +5,11 @@ import (
"context"
gotls "crypto/tls"
"encoding/base64"
"reflect"
"strings"
"sync"
"time"
"unsafe"
utls "github.com/refraction-networking/utls"
proxymanConfig "github.com/xtls/xray-core/app/proxyman"
@@ -242,7 +244,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
}
var input *bytes.Reader
var rawInput **bytes.Buffer
var rawInput *bytes.Buffer
allowUDP443 := false
switch requestAddons.Flow {
case vless.XRV + "-udp443":
@@ -259,22 +261,30 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
case protocol.RequestCommandMux:
fallthrough // let server break Mux connections that contain TCP requests
case protocol.RequestCommandTCP, protocol.RequestCommandRvs:
var target net.Conn
var t reflect.Type
var p uintptr
if commonConn, ok := conn.(*encryption.CommonConn); ok {
if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransportWithoutSecurity(iConn) {
ob.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport / another securityConn should not be penetrated
}
target = commonConn
t = reflect.TypeOf(commonConn).Elem()
p = uintptr(unsafe.Pointer(commonConn))
} else if tlsConn, ok := iConn.(*tls.Conn); ok {
target = tlsConn.Conn
t = reflect.TypeOf(tlsConn.Conn).Elem()
p = uintptr(unsafe.Pointer(tlsConn.Conn))
} else if utlsConn, ok := iConn.(*tls.UConn); ok {
target = utlsConn.Conn
t = reflect.TypeOf(utlsConn.Conn).Elem()
p = uintptr(unsafe.Pointer(utlsConn.Conn))
} else if realityConn, ok := iConn.(*reality.UConn); ok {
target = realityConn.Conn
t = reflect.TypeOf(realityConn.Conn).Elem()
p = uintptr(unsafe.Pointer(realityConn.Conn))
} else {
return errors.New("XTLS only supports TLS and REALITY directly for now.")
}
input, rawInput = vless.AccessTLSBuffers(target)
i, _ := t.FieldByName("input")
r, _ := t.FieldByName("rawInput")
input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset))
rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset))
default:
panic("unknown VLESS request command")
}
-21
View File
@@ -5,28 +5,7 @@
// clients with 'socks' for proxying.
package vless
import (
"bytes"
"net"
"reflect"
"github.com/xtls/xray-core/common/utils"
)
const (
None = "none"
XRV = "xtls-rprx-vision"
)
// Old TLS lib holds bytes.Buffer, but new TLS lib holds *bytes.Buffer
// Since Xray has 3 TLS libraries (std, utls, REALITY server)
// we detect the type of rawInput and return the appropriate pointer
func AccessTLSBuffers(conn net.Conn) (*bytes.Reader, **bytes.Buffer) {
input := utils.AccessField[bytes.Reader](conn, "input")
field := reflect.ValueOf(conn).Elem().FieldByName("rawInput")
if field.Kind() == reflect.Pointer {
return input, utils.AccessField[*bytes.Buffer](conn, "rawInput")
}
raw := utils.AccessField[bytes.Buffer](conn, "rawInput")
return input, &raw
}
+11 -14
View File
@@ -16,8 +16,9 @@ import (
)
type bind struct {
ctx context.Context
resolveFunc func(host string) (net.IP, error)
listenFunc func() (net.PacketConn, error)
listenFunc func(ctx context.Context) (net.PacketConn, error)
downFunc func() error
reserved []byte
@@ -26,6 +27,12 @@ type bind struct {
mu sync.Mutex
}
func (b *bind) SetCtx(ctx context.Context) {
b.mu.Lock()
defer b.mu.Unlock()
b.ctx = ctx
}
func (b *bind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err error) {
b.mu.Lock()
defer b.mu.Unlock()
@@ -34,7 +41,7 @@ func (b *bind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err
return nil, 0, conn.ErrBindAlreadyOpen
}
c, err := b.listenFunc()
c, err := b.listenFunc(b.ctx)
if err != nil {
return nil, 0, err
}
@@ -52,12 +59,9 @@ func (b *bind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err
case <-ch:
default:
errors.LogErrorInner(context.Background(), err, "unexpected closed")
b.mu.Lock()
downFunc := b.downFunc
b.mu.Unlock()
if downFunc != nil {
if b.downFunc != nil {
go func() {
common.Must(downFunc())
common.Must(b.downFunc())
}()
}
}
@@ -79,13 +83,6 @@ func (b *bind) Open(port uint16) (fns []conn.ReceiveFunc, actualPort uint16, err
}, uint16(c.LocalAddr().(*net.UDPAddr).Port), nil
}
// setDownFunc sets downFunc after the device is created, since the device may already be using the bind.
func (b *bind) setDownFunc(f func() error) {
b.mu.Lock()
defer b.mu.Unlock()
b.downFunc = f
}
func (b *bind) Close() error {
b.mu.Lock()
defer b.mu.Unlock()
+165 -149
View File
@@ -7,6 +7,7 @@ import (
"reflect"
"strings"
"sync"
"sync/atomic"
"time"
"golang.zx2c4.com/wireguard/tun"
@@ -31,18 +32,14 @@ import (
)
type Handler struct {
conf *DeviceConfig
policyManager policy.Manager
dns dns.Client
p policy.Manager
streamSettings *internet.MemoryStreamConfig
uplinkCounter stats.Counter
downlinkCounter stats.Counter
tun tun.Device
tnet *Net
dev *device.Device
mu sync.Mutex
canup *atomic.Bool
tnet *Net
bind *bind
dev *device.Device
ipc string
mu sync.Mutex
}
func NewClient(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
@@ -124,17 +121,86 @@ func NewClient(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
return nil, err
}
blockup := newblockup(tun)
bind := &bind{}
logger := &device.Logger{
Verbosef: func(format string, args ...any) {
log.Record(&log.GeneralMessage{
Severity: log.Severity_Debug,
Content: fmt.Sprintf(format, args...),
})
},
Errorf: func(format string, args ...any) {
log.Record(&log.GeneralMessage{
Severity: log.Severity_Error,
Content: fmt.Sprintf(format, args...),
})
},
}
dev := device.NewDevice(blockup, bind, logger)
bind.ctx = context.Background()
bind.resolveFunc = MakeResolveLocal(d, streamSettings)
bind.listenFunc = func(ctx context.Context) (net.PacketConn, error) {
dest, err := net.ParseDestination("udp:" + conf.Peers[0].Endpoint)
if err != nil {
return nil, err
}
var pktConn net.PacketConn
if streamSettings.FinalMask != nil {
conn, err := streamSettings.FinalMask.DialUDP(ctx, dest)
if err != nil {
return nil, errors.New("failed to dial to dest").Base(err)
}
pktConn = conn.(*net.PacketConnWrapper).PacketConn
} else {
conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
if err != nil {
return nil, errors.New("failed to dial to dest").Base(err)
}
switch c := conn.(type) {
case *net.PacketConnWrapper:
pktConn = c.PacketConn
case *cnc.Connection:
pktConn = &internet.FakePacketConn{Conn: c}
default:
panic(reflect.TypeOf(c))
}
}
if uplinkCounter != nil || downlinkCounter != nil {
pktConn = &PacketCounterConnection{
PacketConn: pktConn,
ReadCounter: downlinkCounter,
WriteCounter: uplinkCounter,
}
}
return pktConn, nil
}
bind.downFunc = dev.Down
bind.reserved = conf.Reserved
var cfg strings.Builder
cfg.WriteString("private_key=" + conf.SecretKey + "\n")
for _, peer := range conf.Peers {
cfg.WriteString("public_key=" + peer.PublicKey + "\n")
if peer.PreSharedKey != "" {
cfg.WriteString("preshared_key=" + peer.PreSharedKey + "\n")
}
cfg.WriteString("endpoint=" + peer.Endpoint + "\n")
for _, ip := range peer.AllowedIps {
cfg.WriteString("allowed_ip=" + ip + "\n")
}
if peer.KeepAlive != "" {
cfg.WriteString("persistent_keepalive_interval=" + peer.KeepAlive + "\n")
}
}
return &Handler{
conf: conf,
policyManager: p,
dns: d,
p: p,
streamSettings: streamSettings,
uplinkCounter: uplinkCounter,
downlinkCounter: downlinkCounter,
tun: tun,
tnet: tnet,
canup: blockup.canup,
tnet: tnet,
bind: bind,
dev: dev,
ipc: cfg.String(),
}, nil
}
@@ -149,7 +215,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
ob.CanSpliceCopy = 3
dialer.SetOutboundGateway(ctx, ob)
if err := h.init(ctx); err != nil {
if err := h.up(ctx); err != nil {
return err
}
@@ -159,7 +225,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
newCtx, newCancel = context.WithCancel(context.Background())
}
sessionPolicy := h.policyManager.ForLevel(0)
sessionPolicy := h.p.ForLevel(0)
ctx, cancel := context.WithCancel(ctx)
timer := signal.CancelAfterInactivity(ctx, func() {
cancel()
@@ -234,150 +300,72 @@ func (h *Handler) Close() (err error) {
if h.dev != nil {
h.dev.Close()
h.dev = nil
h.tun = nil
} else if h.tun != nil {
h.tun.Close()
h.tun = nil
}
return nil
}
func (h *Handler) init(ctx context.Context) error {
func (h *Handler) up(ctx context.Context) error {
h.mu.Lock()
defer h.mu.Unlock()
if h.tun == nil {
if h.dev == nil {
return errors.New("closed")
}
if h.dev != nil {
return h.dev.Up()
h.bind.SetCtx(ctx)
if !h.canup.Load() {
h.canup.Store(true)
}
resolveFunc := h.resolveLocal
listenFunc := func() (net.PacketConn, error) {
dest, err := net.ParseDestination("udp:" + h.conf.Peers[0].Endpoint)
if len(h.ipc) > 0 {
if err := h.dev.IpcSet(h.ipc); err != nil {
return err
}
h.ipc = ""
}
return h.dev.Up()
}
func MakeResolveLocal(d dns.Client, streamSettings *internet.MemoryStreamConfig) func(host string) (net.IP, error) {
return func(host string) (net.IP, error) {
if ip := net.ParseIP(host); ip != nil {
return ip, nil
}
ips, _, err := d.LookupIP(host, dns.IPOption{IPv4Enable: true, IPv6Enable: true})
if err != nil {
return nil, err
}
var pktConn net.PacketConn
if h.streamSettings.FinalMask != nil {
conn, err := h.streamSettings.FinalMask.DialUDP(ctx, dest)
if err != nil {
return nil, errors.New("failed to dial to dest").Base(err)
got := ips
if streamSettings.SocketSettings != nil {
var got4, got6 []net.IP
for _, ip := range ips {
if ip.To4() != nil {
got4 = append(got4, ip)
} else {
got6 = append(got6, ip)
}
}
pktConn = conn.(*net.PacketConnWrapper).PacketConn
} else {
conn, err := internet.DialSystem(ctx, dest, h.streamSettings.SocketSettings)
if err != nil {
return nil, errors.New("failed to dial to dest").Base(err)
}
switch c := conn.(type) {
case *net.PacketConnWrapper:
pktConn = c.PacketConn
case *cnc.Connection:
pktConn = &internet.FakePacketConn{Conn: c}
default:
panic(reflect.TypeOf(c))
}
}
if h.uplinkCounter != nil || h.downlinkCounter != nil {
pktConn = &PacketCounterConnection{
PacketConn: pktConn,
ReadCounter: h.downlinkCounter,
WriteCounter: h.uplinkCounter,
}
}
return pktConn, nil
}
// device.NewDevice may use the bind right away (Up -> BindUpdate -> Open),
// so everything it reads must be set before creating the device.
bind := &bind{
resolveFunc: resolveFunc,
listenFunc: listenFunc,
reserved: h.conf.Reserved,
}
logger := &device.Logger{
Verbosef: func(format string, args ...any) {
log.Record(&log.GeneralMessage{
Severity: log.Severity_Debug,
Content: fmt.Sprintf(format, args...),
})
},
Errorf: func(format string, args ...any) {
log.Record(&log.GeneralMessage{
Severity: log.Severity_Error,
Content: fmt.Sprintf(format, args...),
})
},
}
dev := device.NewDevice(h.tun, bind, logger)
bind.setDownFunc(dev.Down)
var cfg strings.Builder
cfg.WriteString("private_key=" + h.conf.SecretKey + "\n")
for _, peer := range h.conf.Peers {
cfg.WriteString("public_key=" + peer.PublicKey + "\n")
if peer.PreSharedKey != "" {
cfg.WriteString("preshared_key=" + peer.PreSharedKey + "\n")
}
cfg.WriteString("endpoint=" + peer.Endpoint + "\n")
for _, ip := range peer.AllowedIps {
cfg.WriteString("allowed_ip=" + ip + "\n")
}
if peer.KeepAlive != "" {
cfg.WriteString("persistent_keepalive_interval=" + peer.KeepAlive + "\n")
}
}
err := dev.IpcSet(cfg.String())
if err != nil {
return err
}
err = dev.Up()
if err != nil {
return err
}
h.dev = dev
return nil
}
func (h *Handler) resolveLocal(host string) (net.IP, error) {
if ip := net.ParseIP(host); ip != nil {
return ip, nil
}
ips, _, err := h.dns.LookupIP(host, dns.IPOption{IPv4Enable: true, IPv6Enable: true})
if err != nil {
return nil, err
}
got := ips
if h.streamSettings.SocketSettings != nil {
var got4, got6 []net.IP
for _, ip := range ips {
if ip.To4() != nil {
got4 = append(got4, ip)
} else {
got6 = append(got6, ip)
}
}
switch h.streamSettings.SocketSettings.DomainStrategy {
case internet.DomainStrategy_AS_IS, internet.DomainStrategy_USE_IP, internet.DomainStrategy_FORCE_IP:
got = ips
case internet.DomainStrategy_USE_IP4, internet.DomainStrategy_FORCE_IP4:
got = got4
case internet.DomainStrategy_USE_IP6, internet.DomainStrategy_FORCE_IP6:
got = got6
case internet.DomainStrategy_USE_IP46, internet.DomainStrategy_FORCE_IP46:
got = got4
if len(got) == 0 {
got = got6
}
case internet.DomainStrategy_USE_IP64, internet.DomainStrategy_FORCE_IP64:
got = got6
if len(got) == 0 {
switch streamSettings.SocketSettings.DomainStrategy {
case internet.DomainStrategy_AS_IS, internet.DomainStrategy_USE_IP, internet.DomainStrategy_FORCE_IP:
got = ips
case internet.DomainStrategy_USE_IP4, internet.DomainStrategy_FORCE_IP4:
got = got4
case internet.DomainStrategy_USE_IP6, internet.DomainStrategy_FORCE_IP6:
got = got6
case internet.DomainStrategy_USE_IP46, internet.DomainStrategy_FORCE_IP46:
got = got4
if len(got) == 0 {
got = got6
}
case internet.DomainStrategy_USE_IP64, internet.DomainStrategy_FORCE_IP64:
got = got6
if len(got) == 0 {
got = got4
}
}
if len(got) == 0 {
return nil, dns.ErrEmptyResponse
}
}
if len(got) == 0 {
return nil, dns.ErrEmptyResponse
}
return got[dice.Roll(len(got))], nil
}
return got[dice.Roll(len(got))], nil
}
type UDPConnClient struct {
@@ -510,3 +498,31 @@ func (c *cache) Cache(host string, saddr []string, ttl uint32) {
deadline: time.Now().Add(time.Second * time.Duration(ttl)),
}
}
type blockup struct {
tun.Device
ch chan tun.Event
canup *atomic.Bool
}
func newblockup(t tun.Device) *blockup {
ch := make(chan tun.Event, cap(t.Events()))
canup := &atomic.Bool{}
go func() {
for e := range t.Events() {
if e&tun.EventUp != 0 && !canup.Load() {
continue
}
select {
case ch <- e:
default:
}
}
close(ch)
}()
return &blockup{Device: t, ch: ch, canup: canup}
}
func (b *blockup) Events() <-chan tun.Event {
return b.ch
}
+1 -1
View File
@@ -255,7 +255,7 @@ func (s *Server) Start() error {
if s.src.Address.Family().IsDomain() {
return errors.New("address is domain")
}
listenFunc := func() (net.PacketConn, error) {
listenFunc := func(context.Context) (net.PacketConn, error) {
var pktConn net.PacketConn
var err error
if s.streamSettings.FinalMask != nil {
+19 -1
View File
@@ -22,6 +22,12 @@ type interConn struct {
client bool
user *protocol.MemoryUser
lock chan struct{}
mu sync.Mutex
}
func (c *interConn) unlock() {
<-c.lock
}
func (c *interConn) User() *protocol.MemoryUser {
@@ -33,6 +39,9 @@ func (c *interConn) Read(b []byte) (int, error) {
}
func (c *interConn) Write(b []byte) (int, error) {
c.lock <- struct{}{}
defer c.unlock()
if c.client {
c.client = false
if _, err := c.stream.Write(append(quicvarint.Append(nil, FrameTypeTCPRequest), b...)); err != nil {
@@ -40,11 +49,20 @@ func (c *interConn) Write(b []byte) (int, error) {
}
return len(b), nil
}
return c.stream.Write(b)
}
func (c *interConn) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
select {
case c.lock <- struct{}{}:
defer c.unlock()
default:
c.stream.CancelRead(0)
c.stream.CancelWrite(0)
return nil
}
c.stream.CancelRead(0)
return c.stream.Close()
}
+1
View File
@@ -257,6 +257,7 @@ func (c *client) tcp(ctx context.Context) (stat.Connection, error) {
remote: c.conn.RemoteAddr(),
client: true,
lock: make(chan struct{}, 1),
}, nil
}
+1
View File
@@ -134,6 +134,7 @@ func (h *httpHandler) StreamDispatcher(ft http3.FrameType, stream *quic.Stream,
remote: h.conn.RemoteAddr(),
user: h.user,
lock: make(chan struct{}, 1),
})
return true, nil
default:
+19
View File
@@ -28,3 +28,22 @@ func (v *SocketConfig) ParseTFOValue() int {
}
return tfo
}
// Dial should fail on bind errors, return this to indicate such errors.
type BindError struct {
err error
}
func (e *BindError) Unwrap() error {
return e.err
}
func (e *BindError) Error() string {
return "failed to bind to interface: " + e.err.Error()
}
func NewBindError(err error) *BindError {
return &BindError{
err: err,
}
}
+3 -3
View File
@@ -136,15 +136,15 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
if config.Interface != "" {
iface, err := net.InterfaceByName(config.Interface)
if err != nil {
return errors.New("failed to get interface ", config.Interface).Base(err)
return NewBindError(errors.New("failed to get interface ", config.Interface).Base(err))
}
if network == "tcp6" || network == "udp6" {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index); err != nil {
return errors.New("failed to set IPV6_BOUND_IF").Base(err)
return NewBindError(errors.New("failed to set IPV6_BOUND_IF").Base(err))
}
} else {
if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index); err != nil {
return errors.New("failed to set IP_BOUND_IF").Base(err)
return NewBindError(errors.New("failed to set IP_BOUND_IF").Base(err))
}
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
if config.Interface != "" {
if err := syscall.BindToDevice(int(fd), config.Interface); err != nil {
return errors.New("failed to set Interface").Base(err)
return NewBindError(errors.New("failed to set Interface").Base(err))
}
}
+5 -5
View File
@@ -35,7 +35,7 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
if config.Interface != "" {
inf, err := net.InterfaceByName(config.Interface)
if err != nil {
return errors.New("failed to find the interface").Base(err)
return NewBindError(errors.New("failed to find the interface").Base(err))
}
// easy way to check if the address is ipv4
isV4 := strings.Contains(address, ".")
@@ -47,20 +47,20 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
binary.BigEndian.PutUint32(bytes[:], uint32(inf.Index))
idx := *(*uint32)(unsafe.Pointer(&bytes[0]))
if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx)); err != nil {
return errors.New("failed to set IP_UNICAST_IF").Base(err)
return NewBindError(errors.New("failed to set IP_UNICAST_IF").Base(err))
}
if ip := net.ParseIP(host); ip != nil && ip.IsMulticast() && isUDPSocket(network) {
if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, int(idx)); err != nil {
return errors.New("failed to set IP_MULTICAST_IF").Base(err)
return NewBindError(errors.New("failed to set IP_MULTICAST_IF").Base(err))
}
}
} else {
if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, inf.Index); err != nil {
return errors.New("failed to set IPV6_UNICAST_IF").Base(err)
return NewBindError(errors.New("failed to set IPV6_UNICAST_IF").Base(err))
}
if ip := net.ParseIP(host); ip != nil && ip.IsMulticast() && isUDPSocket(network) {
if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, inf.Index); err != nil {
return errors.New("failed to set IPV6_MULTICAST_IF").Base(err)
return NewBindError(errors.New("failed to set IPV6_MULTICAST_IF").Base(err))
}
}
}
+31 -17
View File
@@ -68,19 +68,27 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
}
}
var lc net.ListenConfig
lc.Control = func(network, address string, c syscall.RawConn) error {
for _, ctl := range Controllers {
if err := ctl(network, address, c); err != nil {
errors.LogInfoInner(ctx, err, "failed to apply external controller")
}
}
return c.Control(func(fd uintptr) {
if sockopt != nil {
if err := applyOutboundSocketOptions(network, destAddr.String(), fd, sockopt); err != nil {
errors.LogInfo(ctx, err, "failed to apply socket options")
if sockopt != nil || len(Controllers) > 0 {
lc.Control = func(network, address string, c syscall.RawConn) error {
for _, ctl := range Controllers {
if err := ctl(network, address, c); err != nil {
return errors.New("failed to apply external controller").Base(err)
}
}
})
if sockopt != nil {
var sockErr, controlErr error
controlErr = c.Control(func(fd uintptr) {
if sockErr = applyOutboundSocketOptions(network, destAddr.String(), fd, sockopt); sockErr != nil {
errors.LogInfoInner(ctx, sockErr, "failed to apply socket options")
}
})
if _, ok := sockErr.(*BindError); ok {
return sockErr
}
return controlErr
}
return nil
}
}
packetConn, err := lc.ListenPacket(ctx, srcAddr.Network(), srcAddr.String())
if err != nil {
@@ -128,16 +136,22 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
dialer.Control = func(network, address string, c syscall.RawConn) error {
for _, ctl := range Controllers {
if err := ctl(network, address, c); err != nil {
errors.LogInfoInner(ctx, err, "failed to apply external controller")
return errors.New("failed to apply external controller").Base(err)
}
}
return c.Control(func(fd uintptr) {
if sockopt != nil {
if err := applyOutboundSocketOptions(network, address, fd, sockopt); err != nil {
errors.LogInfoInner(ctx, err, "failed to apply socket options")
if sockopt != nil {
var sockErr, controlErr error
controlErr = c.Control(func(fd uintptr) {
if sockErr = applyOutboundSocketOptions(network, address, fd, sockopt); sockErr != nil {
errors.LogInfoInner(ctx, sockErr, "failed to apply socket options")
}
})
if _, ok := sockErr.(*BindError); ok {
return sockErr
}
})
return controlErr
}
return nil
}
}
+1 -1
View File
@@ -153,7 +153,7 @@ func jitter(backoff time.Duration) time.Duration {
if half <= 0 {
return backoff
}
return half + time.Duration(dice.Roll(int(half)))
return half + time.Duration(dice.RollInt63n(int64(half)))
}
func rateLimited(payload []byte) bool {