mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-23 18:16:50 +08:00
Compare commits
21 Commits
v1.250726.
...
a3e347b27e
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a3e347b27e | ||
![]() |
cb72a79f5b | ||
![]() |
90e2efc4ed | ||
![]() |
a15f47d5d0 | ||
![]() |
efd1538a29 | ||
![]() |
d6f1ec481e | ||
![]() |
442f6b7239 | ||
![]() |
e2fe7fe59d | ||
![]() |
7d516ee81f | ||
![]() |
afdcad533c | ||
![]() |
f099e2611d | ||
![]() |
ff4b10e802 | ||
![]() |
2417c281c0 | ||
![]() |
b21630c38c | ||
![]() |
e3bd972d03 | ||
![]() |
ebf4d19c7d | ||
![]() |
7bbf7a702c | ||
![]() |
5bae374f61 | ||
![]() |
79c39ad56f | ||
![]() |
88b942e836 | ||
![]() |
caa1a5513b |
3
.github/workflows/docker.yml
vendored
3
.github/workflows/docker.yml
vendored
@@ -38,9 +38,6 @@ jobs:
|
|||||||
if [[ -z "$SOURCE_TAG" ]]; then
|
if [[ -z "$SOURCE_TAG" ]]; then
|
||||||
SOURCE_TAG="${{ github.ref_name }}"
|
SOURCE_TAG="${{ github.ref_name }}"
|
||||||
fi
|
fi
|
||||||
if [[ -z "$SOURCE_TAG" ]]; then
|
|
||||||
SOURCE_TAG="${{ github.event.release.tag_name }}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "$SOURCE_TAG" ]]; then
|
if [[ -z "$SOURCE_TAG" ]]; then
|
||||||
echo "Error: Could not determine a valid tag source. Input tag and context tag (github.ref_name) are both empty."
|
echo "Error: Could not determine a valid tag source. Input tag and context tag (github.ref_name) are both empty."
|
||||||
|
@@ -19,7 +19,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
Version_x byte = 25
|
Version_x byte = 25
|
||||||
Version_y byte = 7
|
Version_y byte = 7
|
||||||
Version_z byte = 26
|
Version_z byte = 24
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -413,7 +413,7 @@ type TLSConfig struct {
|
|||||||
ServerNameToVerify string `json:"serverNameToVerify"`
|
ServerNameToVerify string `json:"serverNameToVerify"`
|
||||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||||
ECHConfigList string `json:"echConfigList"`
|
ECHConfigList string `json:"echConfigList"`
|
||||||
ECHServerKeys string `json:"echServerKeys"`
|
ECHSeverKeys string `json:"echSeverKeys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -487,12 +487,12 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
|
|
||||||
config.EchConfigList = c.ECHConfigList
|
config.EchConfigList = c.ECHConfigList
|
||||||
|
|
||||||
if c.ECHServerKeys != "" {
|
if c.ECHSeverKeys != "" {
|
||||||
EchPrivateKey, err := base64.StdEncoding.DecodeString(c.ECHServerKeys)
|
EchPrivateKey, err := base64.StdEncoding.DecodeString(c.ECHSeverKeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("invalid ECH Config", c.ECHServerKeys)
|
return nil, errors.New("invalid ECH Config", c.ECHSeverKeys)
|
||||||
}
|
}
|
||||||
config.EchServerKeys = EchPrivateKey
|
config.EchSeverKeys = EchPrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
@@ -625,9 +625,6 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
|||||||
config.MaxTimeDiff = c.MaxTimeDiff
|
config.MaxTimeDiff = c.MaxTimeDiff
|
||||||
|
|
||||||
if c.Mldsa65Seed != "" {
|
if c.Mldsa65Seed != "" {
|
||||||
if c.Mldsa65Seed == c.PrivateKey {
|
|
||||||
return nil, errors.New(`"mldsa65Seed" and "privateKey" can not be the same value: `, c.Mldsa65Seed)
|
|
||||||
}
|
|
||||||
if config.Mldsa65Seed, err = base64.RawURLEncoding.DecodeString(c.Mldsa65Seed); err != nil || len(config.Mldsa65Seed) != 32 {
|
if config.Mldsa65Seed, err = base64.RawURLEncoding.DecodeString(c.Mldsa65Seed); err != nil || len(config.Mldsa65Seed) != 32 {
|
||||||
return nil, errors.New(`invalid "mldsa65Seed": `, c.Mldsa65Seed)
|
return nil, errors.New(`invalid "mldsa65Seed": `, c.Mldsa65Seed)
|
||||||
}
|
}
|
||||||
|
@@ -23,8 +23,6 @@ var CmdAPI = &base.Command{
|
|||||||
cmdRemoveOutbounds,
|
cmdRemoveOutbounds,
|
||||||
cmdListInbounds,
|
cmdListInbounds,
|
||||||
cmdListOutbounds,
|
cmdListOutbounds,
|
||||||
cmdAddInboundUsers,
|
|
||||||
cmdRemoveInboundUsers,
|
|
||||||
cmdInboundUser,
|
cmdInboundUser,
|
||||||
cmdInboundUserCount,
|
cmdInboundUserCount,
|
||||||
cmdAddRules,
|
cmdAddRules,
|
||||||
|
@@ -1,144 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
|
||||||
|
|
||||||
handlerService "github.com/xtls/xray-core/app/proxyman/command"
|
|
||||||
cserial "github.com/xtls/xray-core/common/serial"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/core"
|
|
||||||
"github.com/xtls/xray-core/infra/conf"
|
|
||||||
"github.com/xtls/xray-core/infra/conf/serial"
|
|
||||||
"github.com/xtls/xray-core/proxy/shadowsocks"
|
|
||||||
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
|
|
||||||
"github.com/xtls/xray-core/proxy/trojan"
|
|
||||||
vlessin "github.com/xtls/xray-core/proxy/vless/inbound"
|
|
||||||
vmessin "github.com/xtls/xray-core/proxy/vmess/inbound"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/main/commands/base"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cmdAddInboundUsers = &base.Command{
|
|
||||||
CustomFlags: true,
|
|
||||||
UsageLine: "{{.Exec}} api adu [--server=127.0.0.1:8080] <c1.json> [c2.json]...",
|
|
||||||
Short: "Add users to inbounds",
|
|
||||||
Long: `
|
|
||||||
Add users to inbounds.
|
|
||||||
Arguments:
|
|
||||||
-s, -server
|
|
||||||
The API server address. Default 127.0.0.1:8080
|
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
|
||||||
Example:
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
|
||||||
`,
|
|
||||||
Run: executeAddInboundUsers,
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeAddInboundUsers(cmd *base.Command, args []string) {
|
|
||||||
setSharedFlags(cmd)
|
|
||||||
cmd.Flag.Parse(args)
|
|
||||||
unnamedArgs := cmd.Flag.Args()
|
|
||||||
inbs := extractInboundsConfig(unnamedArgs)
|
|
||||||
|
|
||||||
conn, ctx, close := dialAPIServer()
|
|
||||||
defer close()
|
|
||||||
client := handlerService.NewHandlerServiceClient(conn)
|
|
||||||
|
|
||||||
success := 0
|
|
||||||
for _, inb := range inbs {
|
|
||||||
success += executeInboundUserAction(ctx, client, inb, addInboundUserAction)
|
|
||||||
}
|
|
||||||
fmt.Println("Added", success, "user(s) in total.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func addInboundUserAction(ctx context.Context, client handlerService.HandlerServiceClient, tag string, user *protocol.User) error {
|
|
||||||
fmt.Println("add user:", user.Email)
|
|
||||||
_, err := client.AlterInbound(ctx, &handlerService.AlterInboundRequest{
|
|
||||||
Tag: tag,
|
|
||||||
Operation: cserial.ToTypedMessage(
|
|
||||||
&handlerService.AddUserOperation{
|
|
||||||
User: user,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractInboundUsers(inb *core.InboundHandlerConfig) []*protocol.User {
|
|
||||||
if inb == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
inst, err := inb.ProxySettings.GetInstance()
|
|
||||||
if err != nil || inst == nil {
|
|
||||||
fmt.Println("failed to get inbound instance:", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
switch ty := inst.(type) {
|
|
||||||
case *vmessin.Config:
|
|
||||||
return ty.User
|
|
||||||
case *vlessin.Config:
|
|
||||||
return ty.Clients
|
|
||||||
case *trojan.ServerConfig:
|
|
||||||
return ty.Users
|
|
||||||
case *shadowsocks.ServerConfig:
|
|
||||||
return ty.Users
|
|
||||||
case *shadowsocks_2022.MultiUserServerConfig:
|
|
||||||
return ty.Users
|
|
||||||
default:
|
|
||||||
fmt.Println("unsupported inbound type")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractInboundsConfig(unnamedArgs []string) []conf.InboundDetourConfig {
|
|
||||||
ins := make([]conf.InboundDetourConfig, 0)
|
|
||||||
for _, arg := range unnamedArgs {
|
|
||||||
r, err := loadArg(arg)
|
|
||||||
if err != nil {
|
|
||||||
base.Fatalf("failed to load %s: %s", arg, err)
|
|
||||||
}
|
|
||||||
conf, err := serial.DecodeJSONConfig(r)
|
|
||||||
if err != nil {
|
|
||||||
base.Fatalf("failed to decode %s: %s", arg, err)
|
|
||||||
}
|
|
||||||
ins = append(ins, conf.InboundConfigs...)
|
|
||||||
}
|
|
||||||
return ins
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeInboundUserAction(ctx context.Context, client handlerService.HandlerServiceClient, inb conf.InboundDetourConfig, action func(ctx context.Context, client handlerService.HandlerServiceClient, tag string, user *protocol.User) error) int {
|
|
||||||
success := 0
|
|
||||||
|
|
||||||
tag := inb.Tag
|
|
||||||
if len(tag) < 1 {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("processing inbound:", tag)
|
|
||||||
built, err := inb.Build()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("failed to build config:", err)
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
users := extractInboundUsers(built)
|
|
||||||
if users == nil {
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, user := range users {
|
|
||||||
if len(user.Email) < 1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := action(ctx, client, inb.Tag, user); err == nil {
|
|
||||||
fmt.Println("result: ok")
|
|
||||||
success += 1
|
|
||||||
} else {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
@@ -1,62 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
handlerService "github.com/xtls/xray-core/app/proxyman/command"
|
|
||||||
cserial "github.com/xtls/xray-core/common/serial"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/main/commands/base"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cmdRemoveInboundUsers = &base.Command{
|
|
||||||
CustomFlags: true,
|
|
||||||
UsageLine: "{{.Exec}} api rmu [--server=127.0.0.1:8080] -tag=tag <email1> [email2]...",
|
|
||||||
Short: "Remove users from inbounds",
|
|
||||||
Long: `
|
|
||||||
Remove users from inbounds.
|
|
||||||
Arguments:
|
|
||||||
-s, -server
|
|
||||||
The API server address. Default 127.0.0.1:8080
|
|
||||||
-t, -timeout
|
|
||||||
Timeout seconds to call API. Default 3
|
|
||||||
-tag
|
|
||||||
Inbound tag
|
|
||||||
Example:
|
|
||||||
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="vless-in" "xray@love.com" ...
|
|
||||||
`,
|
|
||||||
Run: executeRemoveUsers,
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeRemoveUsers(cmd *base.Command, args []string) {
|
|
||||||
setSharedFlags(cmd)
|
|
||||||
var tag string
|
|
||||||
cmd.Flag.StringVar(&tag, "tag", "", "")
|
|
||||||
cmd.Flag.Parse(args)
|
|
||||||
emails := cmd.Flag.Args()
|
|
||||||
if len(tag) < 1 {
|
|
||||||
base.Fatalf("inbound tag not specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, ctx, close := dialAPIServer()
|
|
||||||
defer close()
|
|
||||||
client := handlerService.NewHandlerServiceClient(conn)
|
|
||||||
|
|
||||||
success := 0
|
|
||||||
for _, email := range emails {
|
|
||||||
fmt.Println("remove user:", email)
|
|
||||||
_, err := client.AlterInbound(ctx, &handlerService.AlterInboundRequest{
|
|
||||||
Tag: tag,
|
|
||||||
Operation: cserial.ToTypedMessage(
|
|
||||||
&handlerService.RemoveUserOperation{
|
|
||||||
Email: email,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
success += 1
|
|
||||||
} else {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println("Removed", success, "user(s) in total.")
|
|
||||||
}
|
|
@@ -13,14 +13,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var cmdECH = &base.Command{
|
var cmdECH = &base.Command{
|
||||||
UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--pem] [-i "ECHServerKeys (base64.StdEncoding)"]`,
|
UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--pem] [-i "ECHSeverKeys (base64.StdEncoding)"]`,
|
||||||
Short: `Generate TLS-ECH certificates`,
|
Short: `Generate TLS-ECH certificates`,
|
||||||
Long: `
|
Long: `
|
||||||
Generate TLS-ECH certificates.
|
Generate TLS-ECH certificates.
|
||||||
|
|
||||||
Set serverName to your custom string: {{.Exec}} tls ech --serverName (string)
|
Set serverName to your custom string: {{.Exec}} tls ech --serverName (string)
|
||||||
Generate into pem format: {{.Exec}} tls ech --pem
|
Generate into pem format: {{.Exec}} tls ech --pem
|
||||||
Restore ECHConfigs from ECHServerKeys: {{.Exec}} tls ech -i "ECHServerKeys (base64.StdEncoding)"
|
Restore ECHConfigs from ECHSeverKeys: {{.Exec}} tls ech -i "ECHSeverKeys (base64.StdEncoding)"
|
||||||
`, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled
|
`, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ func init() {
|
|||||||
cmdECH.Run = executeECH
|
cmdECH.Run = executeECH
|
||||||
}
|
}
|
||||||
|
|
||||||
var input_echServerKeys = cmdECH.Flag.String("i", "", "ECHServerKeys (base64.StdEncoding)")
|
var input_echSeverKeys = cmdECH.Flag.String("i", "", "ECHSeverKeys (base64.StdEncoding)")
|
||||||
|
|
||||||
// var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
|
// var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
|
||||||
var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
|
var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
|
||||||
@@ -47,7 +47,7 @@ func executeECH(cmd *base.Command, args []string) {
|
|||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
var configBuffer, keyBuffer []byte
|
var configBuffer, keyBuffer []byte
|
||||||
if *input_echServerKeys == "" {
|
if *input_echSeverKeys == "" {
|
||||||
configBytes, _ := tls.MarshalBinary(echConfig)
|
configBytes, _ := tls.MarshalBinary(echConfig)
|
||||||
var b cryptobyte.Builder
|
var b cryptobyte.Builder
|
||||||
b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) {
|
b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) {
|
||||||
@@ -61,15 +61,15 @@ func executeECH(cmd *base.Command, args []string) {
|
|||||||
b2.AddBytes(configBytes)
|
b2.AddBytes(configBytes)
|
||||||
keyBuffer, _ = b2.Bytes()
|
keyBuffer, _ = b2.Bytes()
|
||||||
} else {
|
} else {
|
||||||
keySetsByte, err := base64.StdEncoding.DecodeString(*input_echServerKeys)
|
keySetsByte, err := base64.StdEncoding.DecodeString(*input_echSeverKeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Stdout.WriteString("Failed to decode ECHServerKeys: " + err.Error() + "\n")
|
os.Stdout.WriteString("Failed to decode ECHSeverKeys: " + err.Error() + "\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
keyBuffer = keySetsByte
|
keyBuffer = keySetsByte
|
||||||
KeySets, err := tls.ConvertToGoECHKeys(keySetsByte)
|
KeySets, err := tls.ConvertToGoECHKeys(keySetsByte)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Stdout.WriteString("Failed to decode ECHServerKeys: " + err.Error() + "\n")
|
os.Stdout.WriteString("Failed to decode ECHSeverKeys: " + err.Error() + "\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var b cryptobyte.Builder
|
var b cryptobyte.Builder
|
||||||
@@ -88,6 +88,6 @@ func executeECH(cmd *base.Command, args []string) {
|
|||||||
os.Stdout.WriteString(keyPEM)
|
os.Stdout.WriteString(keyPEM)
|
||||||
} else {
|
} else {
|
||||||
os.Stdout.WriteString("ECH config list: \n" + base64.StdEncoding.EncodeToString(configBuffer) + "\n")
|
os.Stdout.WriteString("ECH config list: \n" + base64.StdEncoding.EncodeToString(configBuffer) + "\n")
|
||||||
os.Stdout.WriteString("ECH server keys: \n" + base64.StdEncoding.EncodeToString(keyBuffer) + "\n")
|
os.Stdout.WriteString("ECH Key sets: \n" + base64.StdEncoding.EncodeToString(keyBuffer) + "\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -285,18 +285,14 @@ func NewPacketReader(conn net.Conn, UDPOverride net.Destination, DialDest net.De
|
|||||||
counter = statConn.ReadCounter
|
counter = statConn.ReadCounter
|
||||||
}
|
}
|
||||||
if c, ok := iConn.(*internet.PacketConnWrapper); ok {
|
if c, ok := iConn.(*internet.PacketConnWrapper); ok {
|
||||||
isOverridden := false
|
isAddrChanged := false
|
||||||
if UDPOverride.Address != nil || UDPOverride.Port != 0 {
|
if UDPOverride.Address != nil || UDPOverride.Port != 0 || DialDest.Address.Family().IsDomain() {
|
||||||
isOverridden = true
|
isAddrChanged = true
|
||||||
}
|
}
|
||||||
changedAddress, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
|
||||||
|
|
||||||
return &PacketReader{
|
return &PacketReader{
|
||||||
PacketConnWrapper: c,
|
PacketConnWrapper: c,
|
||||||
Counter: counter,
|
Counter: counter,
|
||||||
IsOverridden: isOverridden,
|
IsAddrChanged: isAddrChanged,
|
||||||
InitUnchangedAddr: DialDest.Address,
|
|
||||||
InitChangedAddr: net.ParseAddress(changedAddress),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &buf.PacketReader{Reader: conn}
|
return &buf.PacketReader{Reader: conn}
|
||||||
@@ -305,9 +301,7 @@ func NewPacketReader(conn net.Conn, UDPOverride net.Destination, DialDest net.De
|
|||||||
type PacketReader struct {
|
type PacketReader struct {
|
||||||
*internet.PacketConnWrapper
|
*internet.PacketConnWrapper
|
||||||
stats.Counter
|
stats.Counter
|
||||||
IsOverridden bool
|
IsAddrChanged bool
|
||||||
InitUnchangedAddr net.Address
|
|
||||||
InitChangedAddr net.Address
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||||
@@ -321,13 +315,9 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
b.Resize(0, int32(n))
|
b.Resize(0, int32(n))
|
||||||
// if udp dest addr is changed, we are unable to get the correct src addr
|
// if udp dest addr is changed, we are unable to get the correct src addr
|
||||||
// so we don't attach src info to udp packet, break cone behavior, assuming the dial dest is the expected scr addr
|
// so we don't attach src info to udp packet, break cone behavior, assuming the dial dest is the expected scr addr
|
||||||
if !r.IsOverridden {
|
if !r.IsAddrChanged {
|
||||||
address := net.IPAddress(d.(*net.UDPAddr).IP)
|
|
||||||
if r.InitChangedAddr == address {
|
|
||||||
address = r.InitUnchangedAddr
|
|
||||||
}
|
|
||||||
b.UDP = &net.Destination{
|
b.UDP = &net.Destination{
|
||||||
Address: address,
|
Address: net.IPAddress(d.(*net.UDPAddr).IP),
|
||||||
Port: net.Port(d.(*net.UDPAddr).Port),
|
Port: net.Port(d.(*net.UDPAddr).Port),
|
||||||
Network: net.Network_UDP,
|
Network: net.Network_UDP,
|
||||||
}
|
}
|
||||||
|
@@ -444,7 +444,7 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
|||||||
config.KeyLogWriter = writer
|
config.KeyLogWriter = writer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(c.EchConfigList) > 0 || len(c.EchServerKeys) > 0 {
|
if len(c.EchConfigList) > 0 || len(c.EchSeverKeys) > 0 {
|
||||||
err := ApplyECH(c, config)
|
err := ApplyECH(c, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogError(context.Background(), err)
|
errors.LogError(context.Background(), err)
|
||||||
|
@@ -218,7 +218,7 @@ type Config struct {
|
|||||||
// @Critical
|
// @Critical
|
||||||
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
|
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
|
||||||
EchConfigList string `protobuf:"bytes,18,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"`
|
EchConfigList string `protobuf:"bytes,18,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"`
|
||||||
EchServerKeys []byte `protobuf:"bytes,19,opt,name=ech_server_keys,json=echServerKeys,proto3" json:"ech_server_keys,omitempty"`
|
EchSeverKeys []byte `protobuf:"bytes,19,opt,name=ech_sever_keys,json=echSeverKeys,proto3" json:"ech_sever_keys,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -370,9 +370,9 @@ func (x *Config) GetEchConfigList() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetEchServerKeys() []byte {
|
func (x *Config) GetEchSeverKeys() []byte {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.EchServerKeys
|
return x.EchSeverKeys
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -408,7 +408,7 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
|||||||
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
|
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
|
||||||
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
|
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
|
||||||
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
|
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
|
||||||
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xea, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
|
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xe8, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
|
||||||
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
|
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
|
||||||
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
|
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
|
||||||
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
|
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
|
||||||
@@ -460,18 +460,17 @@ var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
|||||||
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49, 0x6e,
|
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49, 0x6e,
|
||||||
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x6e,
|
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x6e,
|
||||||
0x66, 0x69, 0x67, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
|
0x66, 0x69, 0x67, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
|
||||||
0x65, 0x63, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x0a,
|
0x65, 0x63, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a,
|
||||||
0x0f, 0x65, 0x63, 0x68, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73,
|
0x0e, 0x65, 0x63, 0x68, 0x5f, 0x73, 0x65, 0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18,
|
||||||
0x18, 0x13, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x63, 0x68, 0x53, 0x65, 0x72, 0x76, 0x65,
|
0x13, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x63, 0x68, 0x53, 0x65, 0x76, 0x65, 0x72, 0x4b,
|
||||||
0x72, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
|
0x65, 0x79, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
|
0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
|
||||||
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
|
0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
|
||||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58,
|
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x1b, 0x58, 0x72, 0x61,
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
|
0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65,
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -94,5 +94,5 @@ message Config {
|
|||||||
|
|
||||||
string ech_config_list = 18;
|
string ech_config_list = 18;
|
||||||
|
|
||||||
bytes ech_server_keys = 19;
|
bytes ech_sever_keys = 19;
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/xtls/reality/hpke"
|
"github.com/xtls/reality/hpke"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/utils"
|
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"golang.org/x/crypto/cryptobyte"
|
"golang.org/x/crypto/cryptobyte"
|
||||||
)
|
)
|
||||||
@@ -66,8 +65,8 @@ func ApplyECH(c *Config, config *tls.Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// for server
|
// for server
|
||||||
if len(c.EchServerKeys) != 0 {
|
if len(c.EchSeverKeys) != 0 {
|
||||||
KeySets, err := ConvertToGoECHKeys(c.EchServerKeys)
|
KeySets, err := ConvertToGoECHKeys(c.EchSeverKeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Failed to unmarshal ECHKeySetList: ", err)
|
return errors.New("Failed to unmarshal ECHKeySetList: ", err)
|
||||||
}
|
}
|
||||||
@@ -78,34 +77,19 @@ func ApplyECH(c *Config, config *tls.Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ECHConfigCache struct {
|
type ECHConfigCache struct {
|
||||||
configRecord atomic.Pointer[echConfigRecord]
|
echConfig atomic.Pointer[[]byte]
|
||||||
|
expire atomic.Pointer[time.Time]
|
||||||
// updateLock is not for preventing concurrent read/write, but for preventing concurrent update
|
// updateLock is not for preventing concurrent read/write, but for preventing concurrent update
|
||||||
UpdateLock sync.Mutex
|
updateLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type echConfigRecord struct {
|
func (c *ECHConfigCache) update(domain string, server string) ([]byte, error) {
|
||||||
config []byte
|
c.updateLock.Lock()
|
||||||
expire time.Time
|
defer c.updateLock.Unlock()
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
GlobalECHConfigCache = utils.NewTypedSyncMap[string, *ECHConfigCache]()
|
|
||||||
clientForECHDOH = utils.NewTypedSyncMap[string, *http.Client]()
|
|
||||||
)
|
|
||||||
|
|
||||||
// Update updates the ECH config for given domain and server.
|
|
||||||
// this method is concurrent safe, only one update request will be sent, others get the cache.
|
|
||||||
// if isLockedUpdate is true, it will not try to acquire the lock.
|
|
||||||
func (c *ECHConfigCache) Update(domain string, server string, isLockedUpdate bool) ([]byte, error) {
|
|
||||||
if !isLockedUpdate {
|
|
||||||
c.UpdateLock.Lock()
|
|
||||||
defer c.UpdateLock.Unlock()
|
|
||||||
}
|
|
||||||
// Double check cache after acquiring lock
|
// Double check cache after acquiring lock
|
||||||
configRecord := c.configRecord.Load()
|
if c.expire.Load().After(time.Now()) {
|
||||||
if configRecord.expire.After(time.Now()) {
|
|
||||||
errors.LogDebug(context.Background(), "Cache hit for domain after double check: ", domain)
|
errors.LogDebug(context.Background(), "Cache hit for domain after double check: ", domain)
|
||||||
return configRecord.config, nil
|
return *c.echConfig.Load(), nil
|
||||||
}
|
}
|
||||||
// Query ECH config from DNS server
|
// Query ECH config from DNS server
|
||||||
errors.LogDebug(context.Background(), "Trying to query ECH config for domain: ", domain, " with ECH server: ", server)
|
errors.LogDebug(context.Background(), "Trying to query ECH config for domain: ", domain, " with ECH server: ", server)
|
||||||
@@ -113,43 +97,49 @@ func (c *ECHConfigCache) Update(domain string, server string, isLockedUpdate boo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
configRecord = &echConfigRecord{
|
c.echConfig.Store(&echConfig)
|
||||||
config: echConfig,
|
expire := time.Now().Add(time.Duration(ttl) * time.Second)
|
||||||
expire: time.Now().Add(time.Duration(ttl) * time.Second),
|
c.expire.Store(&expire)
|
||||||
}
|
return *c.echConfig.Load(), nil
|
||||||
c.configRecord.Store(configRecord)
|
|
||||||
return configRecord.config, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
GlobalECHConfigCache map[string]*ECHConfigCache
|
||||||
|
GlobalECHConfigCacheAccess sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
// QueryRecord returns the ECH config for given domain.
|
// QueryRecord returns the ECH config for given domain.
|
||||||
// If the record is not in cache or expired, it will query the DNS server and update the cache.
|
// If the record is not in cache or expired, it will query the DNS server and update the cache.
|
||||||
func QueryRecord(domain string, server string) ([]byte, error) {
|
func QueryRecord(domain string, server string) ([]byte, error) {
|
||||||
echConfigCache, ok := GlobalECHConfigCache.Load(domain)
|
// Global cache init
|
||||||
if !ok {
|
GlobalECHConfigCacheAccess.Lock()
|
||||||
|
if GlobalECHConfigCache == nil {
|
||||||
|
GlobalECHConfigCache = make(map[string]*ECHConfigCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
echConfigCache := GlobalECHConfigCache[domain]
|
||||||
|
if echConfigCache == nil {
|
||||||
echConfigCache = &ECHConfigCache{}
|
echConfigCache = &ECHConfigCache{}
|
||||||
echConfigCache.configRecord.Store(&echConfigRecord{})
|
echConfigCache.expire.Store(&time.Time{}) // zero value means initial state
|
||||||
echConfigCache, _ = GlobalECHConfigCache.LoadOrStore(domain, echConfigCache)
|
GlobalECHConfigCache[domain] = echConfigCache
|
||||||
}
|
}
|
||||||
configRecord := echConfigCache.configRecord.Load()
|
if echConfigCache != nil && echConfigCache.expire.Load().After(time.Now()) {
|
||||||
if configRecord.expire.After(time.Now()) {
|
|
||||||
errors.LogDebug(context.Background(), "Cache hit for domain: ", domain)
|
errors.LogDebug(context.Background(), "Cache hit for domain: ", domain)
|
||||||
return configRecord.config, nil
|
GlobalECHConfigCacheAccess.Unlock()
|
||||||
|
return *echConfigCache.echConfig.Load(), nil
|
||||||
}
|
}
|
||||||
|
GlobalECHConfigCacheAccess.Unlock()
|
||||||
|
|
||||||
// If expire is zero value, it means we are in initial state, wait for the query to finish
|
// If expire is zero value, it means we are in initial state, wait for the query to finish
|
||||||
// otherwise return old value immediately and update in a goroutine
|
// otherwise return old value immediately and update in a goroutine
|
||||||
// but if the cache is too old, wait for update
|
if *echConfigCache.expire.Load() == (time.Time{}) {
|
||||||
if configRecord.expire == (time.Time{}) || configRecord.expire.Add(time.Hour*6).Before(time.Now()) {
|
return echConfigCache.update(domain, server)
|
||||||
return echConfigCache.Update(domain, server, false)
|
|
||||||
} else {
|
} else {
|
||||||
// If someone already acquired the lock, it means it is updating, do not start another update goroutine
|
// If someone already acquired the lock, it means it is updating, do not start another update goroutine
|
||||||
if echConfigCache.UpdateLock.TryLock() {
|
if echConfigCache.updateLock.TryLock() {
|
||||||
go func() {
|
go echConfigCache.update(domain, server)
|
||||||
defer echConfigCache.UpdateLock.Unlock()
|
|
||||||
echConfigCache.Update(domain, server, true)
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
return configRecord.config, nil
|
return *echConfigCache.echConfig.Load(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,30 +157,26 @@ func dnsQuery(server string, domain string) ([]byte, uint32, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, 0, err
|
return []byte{}, 0, err
|
||||||
}
|
}
|
||||||
var client *http.Client
|
// All traffic sent by core should via xray's internet.DialSystem
|
||||||
if client, _ = clientForECHDOH.Load(server); client == nil {
|
// This involves the behavior of some Android VPN GUI clients
|
||||||
// All traffic sent by core should via xray's internet.DialSystem
|
tr := &http.Transport{
|
||||||
// This involves the behavior of some Android VPN GUI clients
|
IdleConnTimeout: 90 * time.Second,
|
||||||
tr := &http.Transport{
|
ForceAttemptHTTP2: true,
|
||||||
IdleConnTimeout: 90 * time.Second,
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
ForceAttemptHTTP2: true,
|
dest, err := net.ParseDestination(network + ":" + addr)
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
if err != nil {
|
||||||
dest, err := net.ParseDestination(network + ":" + addr)
|
return nil, err
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
conn, err := internet.DialSystem(ctx, dest, nil)
|
||||||
}
|
if err != nil {
|
||||||
conn, err := internet.DialSystem(ctx, dest, nil)
|
return nil, err
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
return conn, nil
|
||||||
}
|
},
|
||||||
return conn, nil
|
}
|
||||||
},
|
client := &http.Client{
|
||||||
}
|
Timeout: 5 * time.Second,
|
||||||
c := &http.Client{
|
Transport: tr,
|
||||||
Timeout: 5 * time.Second,
|
|
||||||
Transport: tr,
|
|
||||||
}
|
|
||||||
client, _ = clientForECHDOH.LoadOrStore(server, c)
|
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest("POST", server, bytes.NewReader(msg))
|
req, err := http.NewRequest("POST", server, bytes.NewReader(msg))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -224,12 +210,7 @@ func dnsQuery(server string, domain string) ([]byte, uint32, error) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
// use xray's internet.DialSystem as mentioned above
|
// use xray's internet.DialSystem as mentioned above
|
||||||
conn, err := internet.DialSystem(dnsTimeoutCtx, dest, nil)
|
conn, err := internet.DialSystem(dnsTimeoutCtx, dest, nil)
|
||||||
defer func() {
|
defer conn.Close()
|
||||||
err := conn.Close()
|
|
||||||
if err != nil {
|
|
||||||
errors.LogDebug(context.Background(), "Failed to close connection: ", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, 0, err
|
return []byte{}, 0, err
|
||||||
}
|
}
|
||||||
|
@@ -70,10 +70,11 @@ func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) (*
|
|||||||
removeRay := func() {
|
removeRay := func() {
|
||||||
v.Lock()
|
v.Lock()
|
||||||
defer v.Unlock()
|
defer v.Unlock()
|
||||||
// sometimes the entry is already removed by others, don't close again
|
|
||||||
if entry == v.conn {
|
if entry == v.conn {
|
||||||
cancel()
|
cancel()
|
||||||
v.removeRay()
|
v.removeRay()
|
||||||
|
} else {
|
||||||
|
errors.LogError(ctx, "removeRay trying to remove a conn that not belongs to it, canceling.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timer := signal.CancelAfterInactivity(ctx, removeRay, time.Minute)
|
timer := signal.CancelAfterInactivity(ctx, removeRay, time.Minute)
|
||||||
|
@@ -40,14 +40,6 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe
|
|||||||
opt(hub)
|
opt(hub)
|
||||||
}
|
}
|
||||||
|
|
||||||
if address.Family().IsDomain() && address.Domain() == "localhost" {
|
|
||||||
address = net.LocalHostIP
|
|
||||||
}
|
|
||||||
|
|
||||||
if address.Family().IsDomain() {
|
|
||||||
return nil, errors.New("domain address is not allowed for listening: ", address.Domain())
|
|
||||||
}
|
|
||||||
|
|
||||||
var sockopt *internet.SocketConfig
|
var sockopt *internet.SocketConfig
|
||||||
if streamSettings != nil {
|
if streamSettings != nil {
|
||||||
sockopt = streamSettings.SocketSettings
|
sockopt = streamSettings.SocketSettings
|
||||||
|
Reference in New Issue
Block a user