Finalmask: Add XDNS (relies mKCP, like DNSTT), header-*, mkcp-* (#5560)
And https://github.com/XTLS/Xray-core/pull/5560#issuecomment-3825430761
This commit is contained in:
1 parent
5b849d51a9
commit
c180c5980c
92 files changed
+5549
-2060
No files matched your search
@@ -4,72 +4,17 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/dns"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/http"
|
"github.com/xtls/xray-core/transport/internet/headers/http"
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/noop"
|
"github.com/xtls/xray-core/transport/internet/headers/noop"
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/srtp"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/tls"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/utp"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/wechat"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/wireguard"
|
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NoOpAuthenticator struct{}
|
|
||||||
|
|
||||||
func (NoOpAuthenticator) Build() (proto.Message, error) {
|
|
||||||
return new(noop.Config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type NoOpConnectionAuthenticator struct{}
|
type NoOpConnectionAuthenticator struct{}
|
||||||
|
|
||||||
func (NoOpConnectionAuthenticator) Build() (proto.Message, error) {
|
func (NoOpConnectionAuthenticator) Build() (proto.Message, error) {
|
||||||
return new(noop.ConnectionConfig), nil
|
return new(noop.ConnectionConfig), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type SRTPAuthenticator struct{}
|
|
||||||
|
|
||||||
func (SRTPAuthenticator) Build() (proto.Message, error) {
|
|
||||||
return new(srtp.Config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type UTPAuthenticator struct{}
|
|
||||||
|
|
||||||
func (UTPAuthenticator) Build() (proto.Message, error) {
|
|
||||||
return new(utp.Config), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type WechatVideoAuthenticator struct{}
|
|
||||||
|
|
||||||
func (WechatVideoAuthenticator) Build() (proto.Message, error) {
|
|
||||||
return new(wechat.VideoConfig), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type WireguardAuthenticator struct{}
|
|
||||||
|
|
||||||
func (WireguardAuthenticator) Build() (proto.Message, error) {
|
|
||||||
return new(wireguard.WireguardConfig), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type DNSAuthenticator struct {
|
|
||||||
Domain string `json:"domain"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *DNSAuthenticator) Build() (proto.Message, error) {
|
|
||||||
config := new(dns.Config)
|
|
||||||
config.Domain = "www.baidu.com"
|
|
||||||
if len(v.Domain) > 0 {
|
|
||||||
config.Domain = v.Domain
|
|
||||||
}
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type DTLSAuthenticator struct{}
|
|
||||||
|
|
||||||
func (DTLSAuthenticator) Build() (proto.Message, error) {
|
|
||||||
return new(tls.PacketConfig), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthenticatorRequest struct {
|
type AuthenticatorRequest struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
|
|||||||
@@ -16,7 +16,16 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||||
"github.com/xtls/xray-core/common/serial"
|
"github.com/xtls/xray-core/common/serial"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/dns"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/dtls"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/srtp"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/utp"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/wechat"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/wireguard"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
|
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/xdns"
|
||||||
"github.com/xtls/xray-core/transport/internet/httpupgrade"
|
"github.com/xtls/xray-core/transport/internet/httpupgrade"
|
||||||
"github.com/xtls/xray-core/transport/internet/hysteria"
|
"github.com/xtls/xray-core/transport/internet/hysteria"
|
||||||
"github.com/xtls/xray-core/transport/internet/kcp"
|
"github.com/xtls/xray-core/transport/internet/kcp"
|
||||||
@@ -29,16 +38,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
kcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
|
||||||
"none": func() interface{} { return new(NoOpAuthenticator) },
|
|
||||||
"srtp": func() interface{} { return new(SRTPAuthenticator) },
|
|
||||||
"utp": func() interface{} { return new(UTPAuthenticator) },
|
|
||||||
"wechat-video": func() interface{} { return new(WechatVideoAuthenticator) },
|
|
||||||
"dtls": func() interface{} { return new(DTLSAuthenticator) },
|
|
||||||
"wireguard": func() interface{} { return new(WireguardAuthenticator) },
|
|
||||||
"dns": func() interface{} { return new(DNSAuthenticator) },
|
|
||||||
}, "type", "")
|
|
||||||
|
|
||||||
tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||||
"none": func() interface{} { return new(NoOpConnectionAuthenticator) },
|
"none": func() interface{} { return new(NoOpConnectionAuthenticator) },
|
||||||
"http": func() interface{} { return new(Authenticator) },
|
"http": func() interface{} { return new(Authenticator) },
|
||||||
@@ -63,9 +62,9 @@ func (c *KCPConfig) Build() (proto.Message, error) {
|
|||||||
|
|
||||||
if c.Mtu != nil {
|
if c.Mtu != nil {
|
||||||
mtu := *c.Mtu
|
mtu := *c.Mtu
|
||||||
if mtu < 576 || mtu > 1460 {
|
// if mtu < 576 || mtu > 1460 {
|
||||||
return nil, errors.New("invalid mKCP MTU size: ", mtu).AtError()
|
// return nil, errors.New("invalid mKCP MTU size: ", mtu).AtError()
|
||||||
}
|
// }
|
||||||
config.Mtu = &kcp.MTU{Value: mtu}
|
config.Mtu = &kcp.MTU{Value: mtu}
|
||||||
}
|
}
|
||||||
if c.Tti != nil {
|
if c.Tti != nil {
|
||||||
@@ -100,20 +99,8 @@ func (c *KCPConfig) Build() (proto.Message, error) {
|
|||||||
config.WriteBuffer = &kcp.WriteBuffer{Size: 512 * 1024}
|
config.WriteBuffer = &kcp.WriteBuffer{Size: 512 * 1024}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(c.HeaderConfig) > 0 {
|
if c.HeaderConfig != nil || c.Seed != nil {
|
||||||
headerConfig, _, err := kcpHeaderLoader.Load(c.HeaderConfig)
|
return nil, errors.PrintRemovedFeatureError("mkcp header & seed", "finalmask/udp header-* & mkcp-original & mkcp-aes128gcm")
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("invalid mKCP header config.").Base(err).AtError()
|
|
||||||
}
|
|
||||||
ts, err := headerConfig.(Buildable).Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("invalid mKCP header config").Base(err).AtError()
|
|
||||||
}
|
|
||||||
config.HeaderConfig = serial.ToTypedMessage(ts)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Seed != nil {
|
|
||||||
config.Seed = &kcp.EncryptionSeed{Seed: *c.Seed}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
@@ -1242,10 +1229,80 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
udpmaskLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||||
"salamander": func() interface{} { return new(Salamander) },
|
"header-dns": func() interface{} { return new(Dns) },
|
||||||
|
"header-dtls": func() interface{} { return new(Dtls) },
|
||||||
|
"header-srtp": func() interface{} { return new(Srtp) },
|
||||||
|
"header-utp": func() interface{} { return new(Utp) },
|
||||||
|
"header-wechat": func() interface{} { return new(Wechat) },
|
||||||
|
"header-wireguard": func() interface{} { return new(Wireguard) },
|
||||||
|
"mkcp-original": func() interface{} { return new(Original) },
|
||||||
|
"mkcp-aes128gcm": func() interface{} { return new(Aes128Gcm) },
|
||||||
|
"salamander": func() interface{} { return new(Salamander) },
|
||||||
|
"xdns": func() interface{} { return new(Xdns) },
|
||||||
}, "type", "settings")
|
}, "type", "settings")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Dns struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Dns) Build() (proto.Message, error) {
|
||||||
|
config := &dns.Config{}
|
||||||
|
config.Domain = "www.baidu.com"
|
||||||
|
|
||||||
|
if len(c.Domain) > 0 {
|
||||||
|
config.Domain = c.Domain
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dtls struct{}
|
||||||
|
|
||||||
|
func (c *Dtls) Build() (proto.Message, error) {
|
||||||
|
return &dtls.Config{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Srtp struct{}
|
||||||
|
|
||||||
|
func (c *Srtp) Build() (proto.Message, error) {
|
||||||
|
return &srtp.Config{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Utp struct{}
|
||||||
|
|
||||||
|
func (c *Utp) Build() (proto.Message, error) {
|
||||||
|
return &utp.Config{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Wechat struct{}
|
||||||
|
|
||||||
|
func (c *Wechat) Build() (proto.Message, error) {
|
||||||
|
return &wechat.Config{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Wireguard struct{}
|
||||||
|
|
||||||
|
func (c *Wireguard) Build() (proto.Message, error) {
|
||||||
|
return &wireguard.Config{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Original struct{}
|
||||||
|
|
||||||
|
func (c *Original) Build() (proto.Message, error) {
|
||||||
|
return &original.Config{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Aes128Gcm struct {
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Aes128Gcm) Build() (proto.Message, error) {
|
||||||
|
return &aes128gcm.Config{
|
||||||
|
Password: c.Password,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
type Salamander struct {
|
type Salamander struct {
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
}
|
}
|
||||||
@@ -1256,14 +1313,28 @@ func (c *Salamander) Build() (proto.Message, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type FinalMask struct {
|
type Xdns struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Xdns) Build() (proto.Message, error) {
|
||||||
|
if c.Domain == "" {
|
||||||
|
return nil, errors.New("empty domain")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &xdns.Config{
|
||||||
|
Domain: c.Domain,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mask struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Settings *json.RawMessage `json:"settings"`
|
Settings *json.RawMessage `json:"settings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FinalMask) Build(tcpmaskLoader bool) (proto.Message, error) {
|
func (c *Mask) Build(tcp bool) (proto.Message, error) {
|
||||||
loader := udpmaskLoader
|
loader := udpmaskLoader
|
||||||
if tcpmaskLoader {
|
if tcp {
|
||||||
return nil, errors.New("")
|
return nil, errors.New("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1282,12 +1353,17 @@ func (c *FinalMask) Build(tcpmaskLoader bool) (proto.Message, error) {
|
|||||||
return ts, nil
|
return ts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FinalMask struct {
|
||||||
|
Tcp []Mask `json:"tcp"`
|
||||||
|
Udp []Mask `json:"udp"`
|
||||||
|
}
|
||||||
|
|
||||||
type StreamConfig struct {
|
type StreamConfig struct {
|
||||||
Address *Address `json:"address"`
|
Address *Address `json:"address"`
|
||||||
Port uint16 `json:"port"`
|
Port uint16 `json:"port"`
|
||||||
Network *TransportProtocol `json:"network"`
|
Network *TransportProtocol `json:"network"`
|
||||||
Security string `json:"security"`
|
Security string `json:"security"`
|
||||||
Udpmasks []*FinalMask `json:"udpmasks"`
|
FinalMask *FinalMask `json:"finalmask"`
|
||||||
TLSSettings *TLSConfig `json:"tlsSettings"`
|
TLSSettings *TLSConfig `json:"tlsSettings"`
|
||||||
REALITYSettings *REALITYConfig `json:"realitySettings"`
|
REALITYSettings *REALITYConfig `json:"realitySettings"`
|
||||||
RAWSettings *TCPConfig `json:"rawSettings"`
|
RAWSettings *TCPConfig `json:"rawSettings"`
|
||||||
@@ -1437,12 +1513,21 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
|
|||||||
config.SocketSettings = ss
|
config.SocketSettings = ss
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mask := range c.Udpmasks {
|
if c.FinalMask != nil {
|
||||||
u, err := mask.Build(false)
|
for _, mask := range c.FinalMask.Tcp {
|
||||||
if err != nil {
|
u, err := mask.Build(true)
|
||||||
return nil, errors.New("failed to build mask with type ", mask.Type).Base(err)
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to build mask with type ", mask.Type).Base(err)
|
||||||
|
}
|
||||||
|
config.Tcpmasks = append(config.Tcpmasks, serial.ToTypedMessage(u))
|
||||||
|
}
|
||||||
|
for _, mask := range c.FinalMask.Udp {
|
||||||
|
u, err := mask.Build(false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to build mask with type ", mask.Type).Base(err)
|
||||||
|
}
|
||||||
|
config.Udpmasks = append(config.Udpmasks, serial.ToTypedMessage(u))
|
||||||
}
|
}
|
||||||
config.Udpmasks = append(config.Udpmasks, serial.ToTypedMessage(u))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
|
|||||||
@@ -63,11 +63,6 @@ import (
|
|||||||
// Transport headers
|
// Transport headers
|
||||||
_ "github.com/xtls/xray-core/transport/internet/headers/http"
|
_ "github.com/xtls/xray-core/transport/internet/headers/http"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/headers/noop"
|
_ "github.com/xtls/xray-core/transport/internet/headers/noop"
|
||||||
_ "github.com/xtls/xray-core/transport/internet/headers/srtp"
|
|
||||||
_ "github.com/xtls/xray-core/transport/internet/headers/tls"
|
|
||||||
_ "github.com/xtls/xray-core/transport/internet/headers/utp"
|
|
||||||
_ "github.com/xtls/xray-core/transport/internet/headers/wechat"
|
|
||||||
_ "github.com/xtls/xray-core/transport/internet/headers/wireguard"
|
|
||||||
|
|
||||||
// JSON & TOML & YAML
|
// JSON & TOML & YAML
|
||||||
_ "github.com/xtls/xray-core/main/json"
|
_ "github.com/xtls/xray-core/main/json"
|
||||||
|
|||||||
@@ -4,17 +4,15 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ConnSize interface {
|
||||||
|
Size() int32
|
||||||
|
}
|
||||||
|
|
||||||
type Udpmask interface {
|
type Udpmask interface {
|
||||||
UDP()
|
UDP()
|
||||||
|
|
||||||
WrapConnClient(net.Conn) (net.Conn, error)
|
WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error)
|
||||||
WrapConnServer(net.Conn) (net.Conn, error)
|
WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error)
|
||||||
|
|
||||||
WrapPacketConnClient(net.PacketConn) (net.PacketConn, error)
|
|
||||||
WrapPacketConnServer(net.PacketConn) (net.PacketConn, error)
|
|
||||||
|
|
||||||
Size() int
|
|
||||||
Serialize([]byte)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type UdpmaskManager struct {
|
type UdpmaskManager struct {
|
||||||
@@ -27,66 +25,32 @@ func NewUdpmaskManager(udpmasks []Udpmask) *UdpmaskManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UdpmaskManager) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
|
||||||
var err error
|
|
||||||
for _, mask := range m.udpmasks {
|
|
||||||
raw, err = mask.WrapConnClient(raw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return raw, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *UdpmaskManager) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
|
||||||
var err error
|
|
||||||
for _, mask := range m.udpmasks {
|
|
||||||
raw, err = mask.WrapConnServer(raw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return raw, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) {
|
func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) {
|
||||||
|
leaveSize := int32(0)
|
||||||
var err error
|
var err error
|
||||||
for _, mask := range m.udpmasks {
|
for i, mask := range m.udpmasks {
|
||||||
raw, err = mask.WrapPacketConnClient(raw)
|
raw, err = mask.WrapPacketConnClient(raw, i == len(m.udpmasks)-1, leaveSize, i == 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
leaveSize += raw.(ConnSize).Size()
|
||||||
}
|
}
|
||||||
return raw, nil
|
return raw, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UdpmaskManager) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) {
|
func (m *UdpmaskManager) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) {
|
||||||
|
leaveSize := int32(0)
|
||||||
var err error
|
var err error
|
||||||
for _, mask := range m.udpmasks {
|
for i, mask := range m.udpmasks {
|
||||||
raw, err = mask.WrapPacketConnServer(raw)
|
raw, err = mask.WrapPacketConnServer(raw, i == len(m.udpmasks)-1, leaveSize, i == 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
leaveSize += raw.(ConnSize).Size()
|
||||||
}
|
}
|
||||||
return raw, nil
|
return raw, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UdpmaskManager) Size() int {
|
|
||||||
size := 0
|
|
||||||
for _, mask := range m.udpmasks {
|
|
||||||
size += mask.Size()
|
|
||||||
}
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *UdpmaskManager) Serialize(b []byte) {
|
|
||||||
index := 0
|
|
||||||
for _, mask := range m.udpmasks {
|
|
||||||
mask.Serialize(b[index:])
|
|
||||||
index += mask.Size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tcpmask interface {
|
type Tcpmask interface {
|
||||||
TCP()
|
TCP()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Config) UDP() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnServer(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.10
|
||||||
|
// protoc v6.33.1
|
||||||
|
// source: transport/internet/finalmask/header/dns/config.proto
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_transport_internet_finalmask_header_dns_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_finalmask_header_dns_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_header_dns_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetDomain() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Domain
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_finalmask_header_dns_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_transport_internet_finalmask_header_dns_config_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"4transport/internet/finalmask/header/dns/config.proto\x12,xray.transport.internet.finalmask.header.dns\" \n" +
|
||||||
|
"\x06Config\x12\x16\n" +
|
||||||
|
"\x06domain\x18\x01 \x01(\tR\x06domainB\xa6\x01\n" +
|
||||||
|
"0com.xray.transport.internet.finalmask.header.dnsP\x01ZAgithub.com/xtls/xray-core/transport/internet/finalmask/header/dns\xaa\x02,Xray.Transport.Internet.Finalmask.Header.Dnsb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_finalmask_header_dns_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_finalmask_header_dns_config_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_finalmask_header_dns_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_finalmask_header_dns_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_finalmask_header_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_dns_config_proto_rawDesc), len(file_transport_internet_finalmask_header_dns_config_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_transport_internet_finalmask_header_dns_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_finalmask_header_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_transport_internet_finalmask_header_dns_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.transport.internet.finalmask.header.dns.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_finalmask_header_dns_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_finalmask_header_dns_config_proto_init() }
|
||||||
|
func file_transport_internet_finalmask_header_dns_config_proto_init() {
|
||||||
|
if File_transport_internet_finalmask_header_dns_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_dns_config_proto_rawDesc), len(file_transport_internet_finalmask_header_dns_config_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_finalmask_header_dns_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_finalmask_header_dns_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_finalmask_header_dns_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_finalmask_header_dns_config_proto = out.File
|
||||||
|
file_transport_internet_finalmask_header_dns_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_finalmask_header_dns_config_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.transport.internet.finalmask.header.dns;
|
||||||
|
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Dns";
|
||||||
|
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/dns";
|
||||||
|
option java_package = "com.xray.transport.internet.finalmask.header.dns";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {
|
||||||
|
string domain = 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,241 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
sync "sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/dice"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func packDomainName(s string, msg []byte) (off1 int, err error) {
|
||||||
|
off := 0
|
||||||
|
ls := len(s)
|
||||||
|
// Each dot ends a segment of the name.
|
||||||
|
// We trade each dot byte for a length byte.
|
||||||
|
// Except for escaped dots (\.), which are normal dots.
|
||||||
|
// There is also a trailing zero.
|
||||||
|
|
||||||
|
// Emit sequence of counted strings, chopping at dots.
|
||||||
|
var (
|
||||||
|
begin int
|
||||||
|
bs []byte
|
||||||
|
)
|
||||||
|
for i := 0; i < ls; i++ {
|
||||||
|
var c byte
|
||||||
|
if bs == nil {
|
||||||
|
c = s[i]
|
||||||
|
} else {
|
||||||
|
c = bs[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c {
|
||||||
|
case '\\':
|
||||||
|
if off+1 > len(msg) {
|
||||||
|
return len(msg), errors.New("buffer size too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
if bs == nil {
|
||||||
|
bs = []byte(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(bs[i:ls-1], bs[i+1:])
|
||||||
|
ls--
|
||||||
|
case '.':
|
||||||
|
labelLen := i - begin
|
||||||
|
if labelLen >= 1<<6 { // top two bits of length must be clear
|
||||||
|
return len(msg), errors.New("bad rdata")
|
||||||
|
}
|
||||||
|
|
||||||
|
// off can already (we're in a loop) be bigger than len(msg)
|
||||||
|
// this happens when a name isn't fully qualified
|
||||||
|
if off+1+labelLen > len(msg) {
|
||||||
|
return len(msg), errors.New("buffer size too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following is covered by the length check above.
|
||||||
|
msg[off] = byte(labelLen)
|
||||||
|
|
||||||
|
if bs == nil {
|
||||||
|
copy(msg[off+1:], s[begin:i])
|
||||||
|
} else {
|
||||||
|
copy(msg[off+1:], bs[begin:i])
|
||||||
|
}
|
||||||
|
off += 1 + labelLen
|
||||||
|
begin = i + 1
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if off < len(msg) {
|
||||||
|
msg[off] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return off + 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type dns struct {
|
||||||
|
header []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *dns) Size() int32 {
|
||||||
|
return int32(len(h.header))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *dns) Serialize(b []byte) {
|
||||||
|
copy(b, h.header)
|
||||||
|
binary.BigEndian.PutUint16(b[0:], dice.RollUint16())
|
||||||
|
}
|
||||||
|
|
||||||
|
type dnsConn struct {
|
||||||
|
first bool
|
||||||
|
leaveSize int32
|
||||||
|
|
||||||
|
conn net.PacketConn
|
||||||
|
header *dns
|
||||||
|
|
||||||
|
readBuf []byte
|
||||||
|
readMutex sync.Mutex
|
||||||
|
writeBuf []byte
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
var header []byte
|
||||||
|
header = binary.BigEndian.AppendUint16(header, 0x0000) // Transaction ID
|
||||||
|
header = binary.BigEndian.AppendUint16(header, 0x0100) // Flags: Standard query
|
||||||
|
header = binary.BigEndian.AppendUint16(header, 0x0001) // Questions
|
||||||
|
header = binary.BigEndian.AppendUint16(header, 0x0000) // Answer RRs
|
||||||
|
header = binary.BigEndian.AppendUint16(header, 0x0000) // Authority RRs
|
||||||
|
header = binary.BigEndian.AppendUint16(header, 0x0000) // Additional RRs
|
||||||
|
buf := make([]byte, 0x100)
|
||||||
|
off1, err := packDomainName(c.Domain+".", buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
header = append(header, buf[:off1]...)
|
||||||
|
header = binary.BigEndian.AppendUint16(header, 0x0001) // Type: A
|
||||||
|
header = binary.BigEndian.AppendUint16(header, 0x0001) // Class: IN
|
||||||
|
|
||||||
|
conn := &dnsConn{
|
||||||
|
first: first,
|
||||||
|
leaveSize: leaveSize,
|
||||||
|
|
||||||
|
conn: raw,
|
||||||
|
header: &dns{
|
||||||
|
header: header,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
conn.readBuf = make([]byte, 8192)
|
||||||
|
conn.writeBuf = make([]byte, 8192)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsConn) Size() int32 {
|
||||||
|
return c.header.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if c.first {
|
||||||
|
c.readMutex.Lock()
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) < n-int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, c.readBuf[c.Size():n])
|
||||||
|
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, p[c.Size():n])
|
||||||
|
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
if c.first {
|
||||||
|
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||||
|
return 0, errors.New("too many masks")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Lock()
|
||||||
|
|
||||||
|
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||||
|
n += int(c.leaveSize) + int(c.Size())
|
||||||
|
|
||||||
|
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn != n {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, errors.New("nn != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
return c.conn.WriteTo(p, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dnsConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Config) UDP() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnServer(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.10
|
||||||
|
// protoc v6.33.1
|
||||||
|
// source: transport/internet/finalmask/header/dtls/config.proto
|
||||||
|
|
||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_transport_internet_finalmask_header_dtls_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_finalmask_header_dtls_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_header_dtls_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_finalmask_header_dtls_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_transport_internet_finalmask_header_dtls_config_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"5transport/internet/finalmask/header/dtls/config.proto\x12-xray.transport.internet.finalmask.header.dtls\"\b\n" +
|
||||||
|
"\x06ConfigB\xa9\x01\n" +
|
||||||
|
"1com.xray.transport.internet.finalmask.header.dtlsP\x01ZBgithub.com/xtls/xray-core/transport/internet/finalmask/header/dtls\xaa\x02-Xray.Transport.Internet.Finalmask.Header.Dtlsb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_finalmask_header_dtls_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_finalmask_header_dtls_config_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_finalmask_header_dtls_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_finalmask_header_dtls_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_finalmask_header_dtls_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_dtls_config_proto_rawDesc), len(file_transport_internet_finalmask_header_dtls_config_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_transport_internet_finalmask_header_dtls_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_finalmask_header_dtls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_transport_internet_finalmask_header_dtls_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.transport.internet.finalmask.header.dtls.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_finalmask_header_dtls_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_finalmask_header_dtls_config_proto_init() }
|
||||||
|
func file_transport_internet_finalmask_header_dtls_config_proto_init() {
|
||||||
|
if File_transport_internet_finalmask_header_dtls_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_dtls_config_proto_rawDesc), len(file_transport_internet_finalmask_header_dtls_config_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_finalmask_header_dtls_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_finalmask_header_dtls_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_finalmask_header_dtls_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_finalmask_header_dtls_config_proto = out.File
|
||||||
|
file_transport_internet_finalmask_header_dtls_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_finalmask_header_dtls_config_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.transport.internet.finalmask.header.dtls;
|
||||||
|
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Dtls";
|
||||||
|
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/dtls";
|
||||||
|
option java_package = "com.xray.transport.internet.finalmask.header.dtls";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
package dtls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
sync "sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/dice"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dtls struct {
|
||||||
|
epoch uint16
|
||||||
|
length uint16
|
||||||
|
sequence uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*dtls) Size() int32 {
|
||||||
|
return 1 + 2 + 2 + 6 + 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *dtls) Serialize(b []byte) {
|
||||||
|
b[0] = 23
|
||||||
|
b[1] = 254
|
||||||
|
b[2] = 253
|
||||||
|
b[3] = byte(h.epoch >> 8)
|
||||||
|
b[4] = byte(h.epoch)
|
||||||
|
b[5] = 0
|
||||||
|
b[6] = 0
|
||||||
|
b[7] = byte(h.sequence >> 24)
|
||||||
|
b[8] = byte(h.sequence >> 16)
|
||||||
|
b[9] = byte(h.sequence >> 8)
|
||||||
|
b[10] = byte(h.sequence)
|
||||||
|
h.sequence++
|
||||||
|
b[11] = byte(h.length >> 8)
|
||||||
|
b[12] = byte(h.length)
|
||||||
|
h.length += 17
|
||||||
|
if h.length > 100 {
|
||||||
|
h.length -= 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type dtlsConn struct {
|
||||||
|
first bool
|
||||||
|
leaveSize int32
|
||||||
|
|
||||||
|
conn net.PacketConn
|
||||||
|
header *dtls
|
||||||
|
|
||||||
|
readBuf []byte
|
||||||
|
readMutex sync.Mutex
|
||||||
|
writeBuf []byte
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
conn := &dtlsConn{
|
||||||
|
first: first,
|
||||||
|
leaveSize: leaveSize,
|
||||||
|
|
||||||
|
conn: raw,
|
||||||
|
header: &dtls{
|
||||||
|
epoch: dice.RollUint16(),
|
||||||
|
sequence: 0,
|
||||||
|
length: 17,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
conn.readBuf = make([]byte, 8192)
|
||||||
|
conn.writeBuf = make([]byte, 8192)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dtlsConn) Size() int32 {
|
||||||
|
return c.header.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dtlsConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if c.first {
|
||||||
|
c.readMutex.Lock()
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) < n-int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, c.readBuf[c.Size():n])
|
||||||
|
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, p[c.Size():n])
|
||||||
|
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dtlsConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
if c.first {
|
||||||
|
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||||
|
return 0, errors.New("too many masks")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Lock()
|
||||||
|
|
||||||
|
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||||
|
n += int(c.leaveSize) + int(c.Size())
|
||||||
|
|
||||||
|
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn != n {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, errors.New("nn != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
return c.conn.WriteTo(p, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dtlsConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dtlsConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dtlsConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dtlsConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dtlsConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package srtp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Config) UDP() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnServer(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.10
|
||||||
|
// protoc v6.33.1
|
||||||
|
// source: transport/internet/finalmask/header/srtp/config.proto
|
||||||
|
|
||||||
|
package srtp
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_transport_internet_finalmask_header_srtp_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_finalmask_header_srtp_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_header_srtp_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_finalmask_header_srtp_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_transport_internet_finalmask_header_srtp_config_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"5transport/internet/finalmask/header/srtp/config.proto\x12-xray.transport.internet.finalmask.header.srtp\"\b\n" +
|
||||||
|
"\x06ConfigB\xa9\x01\n" +
|
||||||
|
"1com.xray.transport.internet.finalmask.header.srtpP\x01ZBgithub.com/xtls/xray-core/transport/internet/finalmask/header/srtp\xaa\x02-Xray.Transport.Internet.Finalmask.Header.Srtpb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_finalmask_header_srtp_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_finalmask_header_srtp_config_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_finalmask_header_srtp_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_finalmask_header_srtp_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_finalmask_header_srtp_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_srtp_config_proto_rawDesc), len(file_transport_internet_finalmask_header_srtp_config_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_transport_internet_finalmask_header_srtp_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_finalmask_header_srtp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_transport_internet_finalmask_header_srtp_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.transport.internet.finalmask.header.srtp.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_finalmask_header_srtp_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_finalmask_header_srtp_config_proto_init() }
|
||||||
|
func file_transport_internet_finalmask_header_srtp_config_proto_init() {
|
||||||
|
if File_transport_internet_finalmask_header_srtp_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_srtp_config_proto_rawDesc), len(file_transport_internet_finalmask_header_srtp_config_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_finalmask_header_srtp_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_finalmask_header_srtp_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_finalmask_header_srtp_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_finalmask_header_srtp_config_proto = out.File
|
||||||
|
file_transport_internet_finalmask_header_srtp_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_finalmask_header_srtp_config_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.transport.internet.finalmask.header.srtp;
|
||||||
|
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Srtp";
|
||||||
|
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/srtp";
|
||||||
|
option java_package = "com.xray.transport.internet.finalmask.header.srtp";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package srtp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
sync "sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/dice"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type srtp struct {
|
||||||
|
header uint16
|
||||||
|
number uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*srtp) Size() int32 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *srtp) Serialize(b []byte) {
|
||||||
|
h.number++
|
||||||
|
binary.BigEndian.PutUint16(b, h.header)
|
||||||
|
binary.BigEndian.PutUint16(b[2:], h.number)
|
||||||
|
}
|
||||||
|
|
||||||
|
type srtpConn struct {
|
||||||
|
first bool
|
||||||
|
leaveSize int32
|
||||||
|
|
||||||
|
conn net.PacketConn
|
||||||
|
header *srtp
|
||||||
|
|
||||||
|
readBuf []byte
|
||||||
|
readMutex sync.Mutex
|
||||||
|
writeBuf []byte
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
conn := &srtpConn{
|
||||||
|
first: first,
|
||||||
|
leaveSize: leaveSize,
|
||||||
|
|
||||||
|
conn: raw,
|
||||||
|
header: &srtp{
|
||||||
|
header: 0xB5E8,
|
||||||
|
number: dice.RollUint16(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
conn.readBuf = make([]byte, 8192)
|
||||||
|
conn.writeBuf = make([]byte, 8192)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *srtpConn) Size() int32 {
|
||||||
|
return c.header.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *srtpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if c.first {
|
||||||
|
c.readMutex.Lock()
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) < n-int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, c.readBuf[c.Size():n])
|
||||||
|
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, p[c.Size():n])
|
||||||
|
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *srtpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
if c.first {
|
||||||
|
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||||
|
return 0, errors.New("too many masks")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Lock()
|
||||||
|
|
||||||
|
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||||
|
n += int(c.leaveSize) + int(c.Size())
|
||||||
|
|
||||||
|
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn != n {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, errors.New("nn != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
return c.conn.WriteTo(p, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *srtpConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *srtpConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *srtpConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *srtpConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *srtpConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package utp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Config) UDP() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnServer(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.10
|
||||||
|
// protoc v6.33.1
|
||||||
|
// source: transport/internet/finalmask/header/utp/config.proto
|
||||||
|
|
||||||
|
package utp
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_transport_internet_finalmask_header_utp_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_finalmask_header_utp_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_header_utp_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_finalmask_header_utp_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_transport_internet_finalmask_header_utp_config_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"4transport/internet/finalmask/header/utp/config.proto\x12,xray.transport.internet.finalmask.header.utp\"\b\n" +
|
||||||
|
"\x06ConfigB\xa6\x01\n" +
|
||||||
|
"0com.xray.transport.internet.finalmask.header.utpP\x01ZAgithub.com/xtls/xray-core/transport/internet/finalmask/header/utp\xaa\x02,Xray.Transport.Internet.Finalmask.Header.Utpb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_finalmask_header_utp_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_finalmask_header_utp_config_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_finalmask_header_utp_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_finalmask_header_utp_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_finalmask_header_utp_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_utp_config_proto_rawDesc), len(file_transport_internet_finalmask_header_utp_config_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_transport_internet_finalmask_header_utp_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_finalmask_header_utp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_transport_internet_finalmask_header_utp_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.transport.internet.finalmask.header.utp.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_finalmask_header_utp_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_finalmask_header_utp_config_proto_init() }
|
||||||
|
func file_transport_internet_finalmask_header_utp_config_proto_init() {
|
||||||
|
if File_transport_internet_finalmask_header_utp_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_utp_config_proto_rawDesc), len(file_transport_internet_finalmask_header_utp_config_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_finalmask_header_utp_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_finalmask_header_utp_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_finalmask_header_utp_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_finalmask_header_utp_config_proto = out.File
|
||||||
|
file_transport_internet_finalmask_header_utp_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_finalmask_header_utp_config_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.transport.internet.finalmask.header.utp;
|
||||||
|
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Utp";
|
||||||
|
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/utp";
|
||||||
|
option java_package = "com.xray.transport.internet.finalmask.header.utp";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
package utp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
sync "sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/dice"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type utp struct {
|
||||||
|
header byte
|
||||||
|
extension byte
|
||||||
|
connectionID uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*utp) Size() int32 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *utp) Serialize(b []byte) {
|
||||||
|
binary.BigEndian.PutUint16(b, h.connectionID)
|
||||||
|
b[2] = h.header
|
||||||
|
b[3] = h.extension
|
||||||
|
}
|
||||||
|
|
||||||
|
type utpConn struct {
|
||||||
|
first bool
|
||||||
|
leaveSize int32
|
||||||
|
|
||||||
|
conn net.PacketConn
|
||||||
|
header *utp
|
||||||
|
|
||||||
|
readBuf []byte
|
||||||
|
readMutex sync.Mutex
|
||||||
|
writeBuf []byte
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
conn := &utpConn{
|
||||||
|
first: first,
|
||||||
|
leaveSize: leaveSize,
|
||||||
|
|
||||||
|
conn: raw,
|
||||||
|
header: &utp{
|
||||||
|
header: 1,
|
||||||
|
extension: 0,
|
||||||
|
connectionID: dice.RollUint16(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
conn.readBuf = make([]byte, 8192)
|
||||||
|
conn.writeBuf = make([]byte, 8192)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *utpConn) Size() int32 {
|
||||||
|
return c.header.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *utpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if c.first {
|
||||||
|
c.readMutex.Lock()
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) < n-int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, c.readBuf[c.Size():n])
|
||||||
|
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, p[c.Size():n])
|
||||||
|
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *utpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
if c.first {
|
||||||
|
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||||
|
return 0, errors.New("too many masks")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Lock()
|
||||||
|
|
||||||
|
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||||
|
n += int(c.leaveSize) + int(c.Size())
|
||||||
|
|
||||||
|
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn != n {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, errors.New("nn != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
return c.conn.WriteTo(p, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *utpConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *utpConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *utpConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *utpConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *utpConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package wechat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Config) UDP() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnServer(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.10
|
||||||
|
// protoc v6.33.1
|
||||||
|
// source: transport/internet/finalmask/header/wechat/config.proto
|
||||||
|
|
||||||
|
package wechat
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_transport_internet_finalmask_header_wechat_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_finalmask_header_wechat_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_header_wechat_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_finalmask_header_wechat_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_transport_internet_finalmask_header_wechat_config_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"7transport/internet/finalmask/header/wechat/config.proto\x12/xray.transport.internet.finalmask.header.wechat\"\b\n" +
|
||||||
|
"\x06ConfigB\xaf\x01\n" +
|
||||||
|
"3com.xray.transport.internet.finalmask.header.wechatP\x01ZDgithub.com/xtls/xray-core/transport/internet/finalmask/header/wechat\xaa\x02/Xray.Transport.Internet.Finalmask.Header.Wechatb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_finalmask_header_wechat_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_finalmask_header_wechat_config_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_finalmask_header_wechat_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_finalmask_header_wechat_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_finalmask_header_wechat_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_wechat_config_proto_rawDesc), len(file_transport_internet_finalmask_header_wechat_config_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_transport_internet_finalmask_header_wechat_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_finalmask_header_wechat_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_transport_internet_finalmask_header_wechat_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.transport.internet.finalmask.header.wechat.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_finalmask_header_wechat_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_finalmask_header_wechat_config_proto_init() }
|
||||||
|
func file_transport_internet_finalmask_header_wechat_config_proto_init() {
|
||||||
|
if File_transport_internet_finalmask_header_wechat_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_wechat_config_proto_rawDesc), len(file_transport_internet_finalmask_header_wechat_config_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_finalmask_header_wechat_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_finalmask_header_wechat_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_finalmask_header_wechat_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_finalmask_header_wechat_config_proto = out.File
|
||||||
|
file_transport_internet_finalmask_header_wechat_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_finalmask_header_wechat_config_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.transport.internet.finalmask.header.wechat;
|
||||||
|
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Wechat";
|
||||||
|
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/wechat";
|
||||||
|
option java_package = "com.xray.transport.internet.finalmask.header.wechat";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package wechat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
sync "sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/dice"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wechat struct {
|
||||||
|
sn uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*wechat) Size() int32 {
|
||||||
|
return 13
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *wechat) Serialize(b []byte) {
|
||||||
|
h.sn++
|
||||||
|
b[0] = 0xa1
|
||||||
|
b[1] = 0x08
|
||||||
|
binary.BigEndian.PutUint32(b[2:], h.sn)
|
||||||
|
b[6] = 0x00
|
||||||
|
b[7] = 0x10
|
||||||
|
b[8] = 0x11
|
||||||
|
b[9] = 0x18
|
||||||
|
b[10] = 0x30
|
||||||
|
b[11] = 0x22
|
||||||
|
b[12] = 0x30
|
||||||
|
}
|
||||||
|
|
||||||
|
type wechatConn struct {
|
||||||
|
first bool
|
||||||
|
leaveSize int32
|
||||||
|
|
||||||
|
conn net.PacketConn
|
||||||
|
header *wechat
|
||||||
|
|
||||||
|
readBuf []byte
|
||||||
|
readMutex sync.Mutex
|
||||||
|
writeBuf []byte
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
conn := &wechatConn{
|
||||||
|
first: first,
|
||||||
|
leaveSize: leaveSize,
|
||||||
|
|
||||||
|
conn: raw,
|
||||||
|
header: &wechat{
|
||||||
|
sn: uint32(dice.RollUint16()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
conn.readBuf = make([]byte, 8192)
|
||||||
|
conn.writeBuf = make([]byte, 8192)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wechatConn) Size() int32 {
|
||||||
|
return c.header.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wechatConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if c.first {
|
||||||
|
c.readMutex.Lock()
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) < n-int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, c.readBuf[c.Size():n])
|
||||||
|
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, p[c.Size():n])
|
||||||
|
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wechatConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
if c.first {
|
||||||
|
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||||
|
return 0, errors.New("too many masks")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Lock()
|
||||||
|
|
||||||
|
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||||
|
n += int(c.leaveSize) + int(c.Size())
|
||||||
|
|
||||||
|
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn != n {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, errors.New("nn != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
return c.conn.WriteTo(p, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wechatConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wechatConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wechatConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wechatConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wechatConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Config) UDP() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnServer(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.10
|
||||||
|
// protoc v6.33.1
|
||||||
|
// source: transport/internet/finalmask/header/wireguard/config.proto
|
||||||
|
|
||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_transport_internet_finalmask_header_wireguard_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_finalmask_header_wireguard_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_header_wireguard_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_finalmask_header_wireguard_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_transport_internet_finalmask_header_wireguard_config_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
":transport/internet/finalmask/header/wireguard/config.proto\x122xray.transport.internet.finalmask.header.wireguard\"\b\n" +
|
||||||
|
"\x06ConfigB\xb8\x01\n" +
|
||||||
|
"6com.xray.transport.internet.finalmask.header.wireguardP\x01ZGgithub.com/xtls/xray-core/transport/internet/finalmask/header/wireguard\xaa\x022Xray.Transport.Internet.Finalmask.Header.Wireguardb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_finalmask_header_wireguard_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_finalmask_header_wireguard_config_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_finalmask_header_wireguard_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_finalmask_header_wireguard_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_finalmask_header_wireguard_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_wireguard_config_proto_rawDesc), len(file_transport_internet_finalmask_header_wireguard_config_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_transport_internet_finalmask_header_wireguard_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_finalmask_header_wireguard_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_transport_internet_finalmask_header_wireguard_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.transport.internet.finalmask.header.wireguard.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_finalmask_header_wireguard_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_finalmask_header_wireguard_config_proto_init() }
|
||||||
|
func file_transport_internet_finalmask_header_wireguard_config_proto_init() {
|
||||||
|
if File_transport_internet_finalmask_header_wireguard_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_header_wireguard_config_proto_rawDesc), len(file_transport_internet_finalmask_header_wireguard_config_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_finalmask_header_wireguard_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_finalmask_header_wireguard_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_finalmask_header_wireguard_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_finalmask_header_wireguard_config_proto = out.File
|
||||||
|
file_transport_internet_finalmask_header_wireguard_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_finalmask_header_wireguard_config_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.transport.internet.finalmask.header.wireguard;
|
||||||
|
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Header.Wireguard";
|
||||||
|
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/header/wireguard";
|
||||||
|
option java_package = "com.xray.transport.internet.finalmask.header.wireguard";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package wireguard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
sync "sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wireguare struct{}
|
||||||
|
|
||||||
|
func (*wireguare) Size() int32 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *wireguare) Serialize(b []byte) {
|
||||||
|
b[0] = 0x04
|
||||||
|
b[1] = 0x00
|
||||||
|
b[2] = 0x00
|
||||||
|
b[3] = 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
type wireguareConn struct {
|
||||||
|
first bool
|
||||||
|
leaveSize int32
|
||||||
|
|
||||||
|
conn net.PacketConn
|
||||||
|
header *wireguare
|
||||||
|
|
||||||
|
readBuf []byte
|
||||||
|
readMutex sync.Mutex
|
||||||
|
writeBuf []byte
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
conn := &wireguareConn{
|
||||||
|
first: first,
|
||||||
|
leaveSize: leaveSize,
|
||||||
|
|
||||||
|
conn: raw,
|
||||||
|
header: &wireguare{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
conn.readBuf = make([]byte, 8192)
|
||||||
|
conn.writeBuf = make([]byte, 8192)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wireguareConn) Size() int32 {
|
||||||
|
return c.header.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wireguareConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if c.first {
|
||||||
|
c.readMutex.Lock()
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) < n-int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, c.readBuf[c.Size():n])
|
||||||
|
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
return 0, addr, errors.New("header").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, p[c.Size():n])
|
||||||
|
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wireguareConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
if c.first {
|
||||||
|
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||||
|
return 0, errors.New("too many masks")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Lock()
|
||||||
|
|
||||||
|
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||||
|
n += int(c.leaveSize) + int(c.Size())
|
||||||
|
|
||||||
|
c.header.Serialize(c.writeBuf[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn != n {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, errors.New("nn != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.header.Serialize(p[c.leaveSize : c.leaveSize+c.Size()])
|
||||||
|
|
||||||
|
return c.conn.WriteTo(p, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wireguareConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wireguareConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wireguareConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wireguareConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wireguareConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package aes128gcm_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAes128GcmSealInPlace(t *testing.T) {
|
||||||
|
hashedPsk := sha256.Sum256([]byte("psk"))
|
||||||
|
aead := crypto.NewAesGcm(hashedPsk[:16])
|
||||||
|
|
||||||
|
text := []byte("0123456789012")
|
||||||
|
buf := make([]byte, 8192)
|
||||||
|
|
||||||
|
nonceSize := aead.NonceSize()
|
||||||
|
nonce := buf[:nonceSize]
|
||||||
|
rand.Read(nonce)
|
||||||
|
copy(buf[nonceSize:], text)
|
||||||
|
plaintext := buf[nonceSize : nonceSize+len(text)]
|
||||||
|
|
||||||
|
sealed := aead.Seal(nil, nonce, plaintext, nil)
|
||||||
|
|
||||||
|
_ = aead.Seal(plaintext[:0], nonce, plaintext, nil)
|
||||||
|
|
||||||
|
assert.Equal(t, sealed, buf[nonceSize:nonceSize+aead.Overhead()+len(text)])
|
||||||
|
}
|
||||||
|
|
||||||
|
func encrypted(plain []byte) ([]byte, []byte) {
|
||||||
|
hashedPsk := sha256.Sum256([]byte("psk"))
|
||||||
|
aead := crypto.NewAesGcm(hashedPsk[:16])
|
||||||
|
|
||||||
|
nonce := make([]byte, 12)
|
||||||
|
rand.Read(nonce)
|
||||||
|
|
||||||
|
return nonce, aead.Seal(nil, nonce, plain, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAes128GcmOpenInPlace(t *testing.T) {
|
||||||
|
a, b := encrypted([]byte("0123456789012"))
|
||||||
|
buf := make([]byte, 8192)
|
||||||
|
copy(buf, a)
|
||||||
|
copy(buf[len(a):], b)
|
||||||
|
|
||||||
|
hashedPsk := sha256.Sum256([]byte("psk"))
|
||||||
|
aead := crypto.NewAesGcm(hashedPsk[:16])
|
||||||
|
|
||||||
|
nonceSize := aead.NonceSize()
|
||||||
|
nonce := buf[:nonceSize]
|
||||||
|
ciphertext := buf[nonceSize : nonceSize+len(b)]
|
||||||
|
|
||||||
|
opened, _ := aead.Open(nil, nonce, ciphertext, nil)
|
||||||
|
_, _ = aead.Open(ciphertext[:0], nonce, ciphertext, nil)
|
||||||
|
|
||||||
|
assert.Equal(t, opened, ciphertext[:len(ciphertext)-aead.Overhead()])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAes128GcmBounce(t *testing.T) {
|
||||||
|
hashedPsk := sha256.Sum256([]byte("psk"))
|
||||||
|
aead := crypto.NewAesGcm(hashedPsk[:16])
|
||||||
|
buf := make([]byte, aead.NonceSize()+aead.Overhead())
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
_, _ = rand.Read(buf)
|
||||||
|
_, err := aead.Open(buf[aead.NonceSize():aead.NonceSize()], buf[:aead.NonceSize()], buf[aead.NonceSize():], nil)
|
||||||
|
assert.NotEqual(t, err, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package aes128gcm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Config) UDP() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnServer(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.10
|
||||||
|
// protoc v6.33.1
|
||||||
|
// source: transport/internet/finalmask/mkcp/aes128gcm/config.proto
|
||||||
|
|
||||||
|
package aes128gcm
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetPassword() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Password
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_finalmask_mkcp_aes128gcm_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"8transport/internet/finalmask/mkcp/aes128gcm/config.proto\x120xray.transport.internet.finalmask.mkcp.aes128gcm\"$\n" +
|
||||||
|
"\x06Config\x12\x1a\n" +
|
||||||
|
"\bpassword\x18\x01 \x01(\tR\bpasswordB\xb2\x01\n" +
|
||||||
|
"4com.xray.transport.internet.finalmask.mkcp.aes128gcmP\x01ZEgithub.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm\xaa\x020Xray.Transport.Internet.Finalmask.Mkcp.Aes128Gcmb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDesc), len(file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.transport.internet.finalmask.mkcp.aes128gcm.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_init() }
|
||||||
|
func file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_init() {
|
||||||
|
if File_transport_internet_finalmask_mkcp_aes128gcm_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDesc), len(file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_finalmask_mkcp_aes128gcm_config_proto = out.File
|
||||||
|
file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_finalmask_mkcp_aes128gcm_config_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.transport.internet.finalmask.mkcp.aes128gcm;
|
||||||
|
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Mkcp.Aes128Gcm";
|
||||||
|
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm";
|
||||||
|
option java_package = "com.xray.transport.internet.finalmask.mkcp.aes128gcm";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {
|
||||||
|
string password = 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package aes128gcm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
sync "sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type aes128gcmConn struct {
|
||||||
|
first bool
|
||||||
|
leaveSize int32
|
||||||
|
|
||||||
|
conn net.PacketConn
|
||||||
|
aead cipher.AEAD
|
||||||
|
|
||||||
|
readBuf []byte
|
||||||
|
readMutex sync.Mutex
|
||||||
|
writeBuf []byte
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
hashedPsk := sha256.Sum256([]byte(c.Password))
|
||||||
|
|
||||||
|
conn := &aes128gcmConn{
|
||||||
|
first: first,
|
||||||
|
leaveSize: leaveSize,
|
||||||
|
|
||||||
|
conn: raw,
|
||||||
|
aead: crypto.NewAesGcm(hashedPsk[:16]),
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
conn.readBuf = make([]byte, 8192)
|
||||||
|
conn.writeBuf = make([]byte, 8192)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aes128gcmConn) Size() int32 {
|
||||||
|
return int32(c.aead.NonceSize()) + int32(c.aead.Overhead())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aes128gcmConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if c.first {
|
||||||
|
c.readMutex.Lock()
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) < n-int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
nonceSize := c.aead.NonceSize()
|
||||||
|
nonce := c.readBuf[:nonceSize]
|
||||||
|
ciphertext := c.readBuf[nonceSize:n]
|
||||||
|
_, err = c.aead.Open(p[:0], nonce, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("aead open").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n - int(c.Size()), addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
nonceSize := c.aead.NonceSize()
|
||||||
|
nonce := p[:nonceSize]
|
||||||
|
ciphertext := p[nonceSize:n]
|
||||||
|
_, err = c.aead.Open(ciphertext[:0], nonce, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, addr, errors.New("aead open").Base(err)
|
||||||
|
}
|
||||||
|
copy(p, p[nonceSize:n-c.aead.Overhead()])
|
||||||
|
|
||||||
|
return n - int(c.Size()), addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aes128gcmConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
if c.first {
|
||||||
|
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||||
|
return 0, errors.New("too many masks")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Lock()
|
||||||
|
|
||||||
|
n = copy(c.writeBuf[c.leaveSize+int32(c.aead.NonceSize()):], p)
|
||||||
|
// n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||||
|
n += int(c.leaveSize) + int(c.Size())
|
||||||
|
|
||||||
|
nonceSize := c.aead.NonceSize()
|
||||||
|
nonce := c.writeBuf[c.leaveSize : c.leaveSize+int32(nonceSize)]
|
||||||
|
common.Must2(rand.Read(nonce))
|
||||||
|
// copy(c.writeBuf[c.leaveSize+int32(nonceSize):], c.writeBuf[c.leaveSize+c.Size():n])
|
||||||
|
plaintext := c.writeBuf[c.leaveSize+int32(nonceSize) : n-c.aead.Overhead()]
|
||||||
|
_ = c.aead.Seal(plaintext[:0], nonce, plaintext, nil)
|
||||||
|
|
||||||
|
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn != n {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, errors.New("nn != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
nonceSize := c.aead.NonceSize()
|
||||||
|
nonce := p[c.leaveSize : c.leaveSize+int32(nonceSize)]
|
||||||
|
common.Must2(rand.Read(nonce))
|
||||||
|
copy(p[c.leaveSize+int32(nonceSize):], p[c.leaveSize+c.Size():])
|
||||||
|
plaintext := p[c.leaveSize+int32(nonceSize) : len(p)-c.aead.Overhead()]
|
||||||
|
_ = c.aead.Seal(plaintext[:0], nonce, plaintext, nil)
|
||||||
|
|
||||||
|
return c.conn.WriteTo(p, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aes128gcmConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aes128gcmConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aes128gcmConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aes128gcmConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *aes128gcmConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package original
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Config) UDP() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnServer(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.10
|
||||||
|
// protoc v6.33.1
|
||||||
|
// source: transport/internet/finalmask/mkcp/original/config.proto
|
||||||
|
|
||||||
|
package original
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_transport_internet_finalmask_mkcp_original_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_finalmask_mkcp_original_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_mkcp_original_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_finalmask_mkcp_original_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_transport_internet_finalmask_mkcp_original_config_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
"7transport/internet/finalmask/mkcp/original/config.proto\x12/xray.transport.internet.finalmask.mkcp.original\"\b\n" +
|
||||||
|
"\x06ConfigB\xaf\x01\n" +
|
||||||
|
"3com.xray.transport.internet.finalmask.mkcp.originalP\x01ZDgithub.com/xtls/xray-core/transport/internet/finalmask/mkcp/original\xaa\x02/Xray.Transport.Internet.Finalmask.Mkcp.Originalb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_finalmask_mkcp_original_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_finalmask_mkcp_original_config_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_finalmask_mkcp_original_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_finalmask_mkcp_original_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_finalmask_mkcp_original_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_mkcp_original_config_proto_rawDesc), len(file_transport_internet_finalmask_mkcp_original_config_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_transport_internet_finalmask_mkcp_original_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_finalmask_mkcp_original_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_transport_internet_finalmask_mkcp_original_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.transport.internet.finalmask.mkcp.original.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_finalmask_mkcp_original_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_finalmask_mkcp_original_config_proto_init() }
|
||||||
|
func file_transport_internet_finalmask_mkcp_original_config_proto_init() {
|
||||||
|
if File_transport_internet_finalmask_mkcp_original_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_mkcp_original_config_proto_rawDesc), len(file_transport_internet_finalmask_mkcp_original_config_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_finalmask_mkcp_original_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_finalmask_mkcp_original_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_finalmask_mkcp_original_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_finalmask_mkcp_original_config_proto = out.File
|
||||||
|
file_transport_internet_finalmask_mkcp_original_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_finalmask_mkcp_original_config_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.transport.internet.finalmask.mkcp.original;
|
||||||
|
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Mkcp.Original";
|
||||||
|
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original";
|
||||||
|
option java_package = "com.xray.transport.internet.finalmask.mkcp.original";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {}
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
package original
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
"encoding/binary"
|
||||||
|
"hash/fnv"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
sync "sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type simple struct{}
|
||||||
|
|
||||||
|
func NewSimple() *simple {
|
||||||
|
return &simple{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*simple) NonceSize() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*simple) Overhead() int {
|
||||||
|
return 6
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *simple) Seal(dst, nonce, plain, extra []byte) []byte {
|
||||||
|
dst = append(dst, 0, 0, 0, 0, 0, 0)
|
||||||
|
binary.BigEndian.PutUint16(dst[4:], uint16(len(plain)))
|
||||||
|
dst = append(dst, plain...)
|
||||||
|
|
||||||
|
fnvHash := fnv.New32a()
|
||||||
|
common.Must2(fnvHash.Write(dst[4:]))
|
||||||
|
fnvHash.Sum(dst[:0])
|
||||||
|
|
||||||
|
dstLen := len(dst)
|
||||||
|
xtra := 4 - dstLen%4
|
||||||
|
if xtra != 4 {
|
||||||
|
dst = append(dst, make([]byte, xtra)...)
|
||||||
|
}
|
||||||
|
xorfwd(dst)
|
||||||
|
if xtra != 4 {
|
||||||
|
dst = dst[:dstLen]
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *simple) Open(dst, nonce, cipherText, extra []byte) ([]byte, error) {
|
||||||
|
dst = append(dst, cipherText...)
|
||||||
|
dstLen := len(dst)
|
||||||
|
xtra := 4 - dstLen%4
|
||||||
|
if xtra != 4 {
|
||||||
|
dst = append(dst, make([]byte, xtra)...)
|
||||||
|
}
|
||||||
|
xorbkd(dst)
|
||||||
|
if xtra != 4 {
|
||||||
|
dst = dst[:dstLen]
|
||||||
|
}
|
||||||
|
|
||||||
|
fnvHash := fnv.New32a()
|
||||||
|
common.Must2(fnvHash.Write(dst[4:]))
|
||||||
|
if binary.BigEndian.Uint32(dst[:4]) != fnvHash.Sum32() {
|
||||||
|
return nil, errors.New("invalid auth")
|
||||||
|
}
|
||||||
|
|
||||||
|
length := binary.BigEndian.Uint16(dst[4:6])
|
||||||
|
if len(dst)-6 != int(length) {
|
||||||
|
return nil, errors.New("invalid auth")
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst[6:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type simpleConn struct {
|
||||||
|
first bool
|
||||||
|
leaveSize int32
|
||||||
|
|
||||||
|
conn net.PacketConn
|
||||||
|
aead cipher.AEAD
|
||||||
|
|
||||||
|
readBuf []byte
|
||||||
|
readMutex sync.Mutex
|
||||||
|
writeBuf []byte
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
conn := &simpleConn{
|
||||||
|
first: first,
|
||||||
|
leaveSize: leaveSize,
|
||||||
|
|
||||||
|
conn: raw,
|
||||||
|
aead: &simple{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
conn.readBuf = make([]byte, 8192)
|
||||||
|
conn.writeBuf = make([]byte, 8192)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *simpleConn) Size() int32 {
|
||||||
|
return int32(c.aead.NonceSize()) + int32(c.aead.Overhead())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *simpleConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if c.first {
|
||||||
|
c.readMutex.Lock()
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) < n-int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext := c.readBuf[:n]
|
||||||
|
opened, err := c.aead.Open(nil, nil, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("aead open").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, opened)
|
||||||
|
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n - int(c.Size()), addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
return 0, addr, errors.New("aead").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext := p[:n]
|
||||||
|
opened, err := c.aead.Open(nil, nil, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("aead open").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(p, opened)
|
||||||
|
|
||||||
|
return n - int(c.Size()), addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *simpleConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
if c.first {
|
||||||
|
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||||
|
return 0, errors.New("too many masks")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Lock()
|
||||||
|
|
||||||
|
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||||
|
n += int(c.leaveSize) + int(c.Size())
|
||||||
|
|
||||||
|
plaintext := c.writeBuf[c.leaveSize+c.Size() : n]
|
||||||
|
sealed := c.aead.Seal(nil, nil, plaintext, nil)
|
||||||
|
copy(c.writeBuf[c.leaveSize:], sealed)
|
||||||
|
|
||||||
|
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn != n {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, errors.New("nn != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
plaintext := p[c.leaveSize+c.Size():]
|
||||||
|
sealed := c.aead.Seal(nil, nil, plaintext, nil)
|
||||||
|
copy(p[c.leaveSize:], sealed)
|
||||||
|
|
||||||
|
return c.conn.WriteTo(p, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *simpleConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *simpleConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *simpleConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *simpleConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *simpleConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package original_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOriginalBounce(t *testing.T) {
|
||||||
|
aead := original.NewSimple()
|
||||||
|
buf := make([]byte, aead.NonceSize()+aead.Overhead())
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
_, _ = rand.Read(buf)
|
||||||
|
_, err := aead.Open(buf[:0], nil, buf, nil)
|
||||||
|
assert.NotEqual(t, err, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
//go:build !amd64
|
//go:build !amd64
|
||||||
// +build !amd64
|
// +build !amd64
|
||||||
|
|
||||||
package kcp
|
package original
|
||||||
|
|
||||||
// xorfwd performs XOR forwards in words, x[i] ^= x[i-4], i from 0 to len
|
// xorfwd performs XOR forwards in words, x[i] ^= x[i-4], i from 0 to len
|
||||||
func xorfwd(x []byte) {
|
func xorfwd(x []byte) {
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package kcp
|
package original
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func xorfwd(x []byte)
|
func xorfwd(x []byte)
|
||||||
File renamed without changes.
@@ -2,41 +2,15 @@ package salamander
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/finalmask/salamander/obfs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Config) UDP() {
|
func (c *Config) UDP() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) WrapConnClient(raw net.Conn) (net.Conn, error) {
|
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
return raw, nil
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) WrapConnServer(raw net.Conn) (net.Conn, error) {
|
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
return raw, nil
|
return NewConnServer(c, raw, first, leaveSize)
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) {
|
|
||||||
ob, err := obfs.NewSalamanderObfuscator([]byte(c.Password))
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("salamander err").Base(err)
|
|
||||||
}
|
|
||||||
return obfs.WrapPacketConn(raw, ob), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) {
|
|
||||||
ob, err := obfs.NewSalamanderObfuscator([]byte(c.Password))
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("salamander err").Base(err)
|
|
||||||
}
|
|
||||||
return obfs.WrapPacketConn(raw, ob), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) Size() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) Serialize([]byte) {
|
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.36.10
|
// protoc-gen-go v1.36.10
|
||||||
// protoc v6.33.1
|
// protoc v6.33.1
|
||||||
// source: transport/internet/udpmask/salamander/config.proto
|
// source: transport/internet/finalmask/salamander/config.proto
|
||||||
|
|
||||||
package salamander
|
package salamander
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ type Config struct {
|
|||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
*x = Config{}
|
*x = Config{}
|
||||||
mi := &file_transport_internet_udpmask_salamander_config_proto_msgTypes[0]
|
mi := &file_transport_internet_finalmask_salamander_config_proto_msgTypes[0]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ func (x *Config) String() string {
|
|||||||
func (*Config) ProtoMessage() {}
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_transport_internet_udpmask_salamander_config_proto_msgTypes[0]
|
mi := &file_transport_internet_finalmask_salamander_config_proto_msgTypes[0]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -55,7 +55,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
func (*Config) Descriptor() ([]byte, []int) {
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
return file_transport_internet_udpmask_salamander_config_proto_rawDescGZIP(), []int{0}
|
return file_transport_internet_finalmask_salamander_config_proto_rawDescGZIP(), []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetPassword() string {
|
func (x *Config) GetPassword() string {
|
||||||
@@ -65,32 +65,32 @@ func (x *Config) GetPassword() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var File_transport_internet_udpmask_salamander_config_proto protoreflect.FileDescriptor
|
var File_transport_internet_finalmask_salamander_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_transport_internet_udpmask_salamander_config_proto_rawDesc = "" +
|
const file_transport_internet_finalmask_salamander_config_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"2transport/internet/udpmask/salamander/config.proto\x12*xray.transport.internet.udpmask.salamander\"$\n" +
|
"4transport/internet/finalmask/salamander/config.proto\x12,xray.transport.internet.finalmask.salamander\"$\n" +
|
||||||
"\x06Config\x12\x1a\n" +
|
"\x06Config\x12\x1a\n" +
|
||||||
"\bpassword\x18\x01 \x01(\tR\bpasswordB\xa0\x01\n" +
|
"\bpassword\x18\x01 \x01(\tR\bpasswordB\xa6\x01\n" +
|
||||||
".com.xray.transport.internet.udpmask.salamanderP\x01Z?github.com/xtls/xray-core/transport/internet/udpmask/salamander\xaa\x02*Xray.Transport.Internet.Udpmask.Salamanderb\x06proto3"
|
"0com.xray.transport.internet.finalmask.salamanderP\x01ZAgithub.com/xtls/xray-core/transport/internet/finalmask/salamander\xaa\x02,Xray.Transport.Internet.Finalmask.Salamanderb\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
file_transport_internet_udpmask_salamander_config_proto_rawDescOnce sync.Once
|
file_transport_internet_finalmask_salamander_config_proto_rawDescOnce sync.Once
|
||||||
file_transport_internet_udpmask_salamander_config_proto_rawDescData []byte
|
file_transport_internet_finalmask_salamander_config_proto_rawDescData []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
func file_transport_internet_udpmask_salamander_config_proto_rawDescGZIP() []byte {
|
func file_transport_internet_finalmask_salamander_config_proto_rawDescGZIP() []byte {
|
||||||
file_transport_internet_udpmask_salamander_config_proto_rawDescOnce.Do(func() {
|
file_transport_internet_finalmask_salamander_config_proto_rawDescOnce.Do(func() {
|
||||||
file_transport_internet_udpmask_salamander_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_udpmask_salamander_config_proto_rawDesc), len(file_transport_internet_udpmask_salamander_config_proto_rawDesc)))
|
file_transport_internet_finalmask_salamander_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_salamander_config_proto_rawDesc), len(file_transport_internet_finalmask_salamander_config_proto_rawDesc)))
|
||||||
})
|
})
|
||||||
return file_transport_internet_udpmask_salamander_config_proto_rawDescData
|
return file_transport_internet_finalmask_salamander_config_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_transport_internet_udpmask_salamander_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
var file_transport_internet_finalmask_salamander_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
var file_transport_internet_udpmask_salamander_config_proto_goTypes = []any{
|
var file_transport_internet_finalmask_salamander_config_proto_goTypes = []any{
|
||||||
(*Config)(nil), // 0: xray.transport.internet.udpmask.salamander.Config
|
(*Config)(nil), // 0: xray.transport.internet.finalmask.salamander.Config
|
||||||
}
|
}
|
||||||
var file_transport_internet_udpmask_salamander_config_proto_depIdxs = []int32{
|
var file_transport_internet_finalmask_salamander_config_proto_depIdxs = []int32{
|
||||||
0, // [0:0] is the sub-list for method output_type
|
0, // [0:0] is the sub-list for method output_type
|
||||||
0, // [0:0] is the sub-list for method input_type
|
0, // [0:0] is the sub-list for method input_type
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
@@ -98,26 +98,26 @@ var file_transport_internet_udpmask_salamander_config_proto_depIdxs = []int32{
|
|||||||
0, // [0:0] is the sub-list for field type_name
|
0, // [0:0] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_transport_internet_udpmask_salamander_config_proto_init() }
|
func init() { file_transport_internet_finalmask_salamander_config_proto_init() }
|
||||||
func file_transport_internet_udpmask_salamander_config_proto_init() {
|
func file_transport_internet_finalmask_salamander_config_proto_init() {
|
||||||
if File_transport_internet_udpmask_salamander_config_proto != nil {
|
if File_transport_internet_finalmask_salamander_config_proto != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_udpmask_salamander_config_proto_rawDesc), len(file_transport_internet_udpmask_salamander_config_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_salamander_config_proto_rawDesc), len(file_transport_internet_finalmask_salamander_config_proto_rawDesc)),
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 1,
|
NumMessages: 1,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
GoTypes: file_transport_internet_udpmask_salamander_config_proto_goTypes,
|
GoTypes: file_transport_internet_finalmask_salamander_config_proto_goTypes,
|
||||||
DependencyIndexes: file_transport_internet_udpmask_salamander_config_proto_depIdxs,
|
DependencyIndexes: file_transport_internet_finalmask_salamander_config_proto_depIdxs,
|
||||||
MessageInfos: file_transport_internet_udpmask_salamander_config_proto_msgTypes,
|
MessageInfos: file_transport_internet_finalmask_salamander_config_proto_msgTypes,
|
||||||
}.Build()
|
}.Build()
|
||||||
File_transport_internet_udpmask_salamander_config_proto = out.File
|
File_transport_internet_finalmask_salamander_config_proto = out.File
|
||||||
file_transport_internet_udpmask_salamander_config_proto_goTypes = nil
|
file_transport_internet_finalmask_salamander_config_proto_goTypes = nil
|
||||||
file_transport_internet_udpmask_salamander_config_proto_depIdxs = nil
|
file_transport_internet_finalmask_salamander_config_proto_depIdxs = nil
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
package xray.transport.internet.udpmask.salamander;
|
package xray.transport.internet.finalmask.salamander;
|
||||||
option csharp_namespace = "Xray.Transport.Internet.Udpmask.Salamander";
|
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Salamander";
|
||||||
option go_package = "github.com/xtls/xray-core/transport/internet/udpmask/salamander";
|
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/salamander";
|
||||||
option java_package = "com.xray.transport.internet.udpmask.salamander";
|
option java_package = "com.xray.transport.internet.finalmask.salamander";
|
||||||
option java_multiple_files = true;
|
option java_multiple_files = true;
|
||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package salamander
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type obfsPacketConn struct {
|
||||||
|
first bool
|
||||||
|
leaveSize int32
|
||||||
|
|
||||||
|
conn net.PacketConn
|
||||||
|
obfs *SalamanderObfuscator
|
||||||
|
|
||||||
|
readBuf []byte
|
||||||
|
readMutex sync.Mutex
|
||||||
|
writeBuf []byte
|
||||||
|
writeMutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnClient(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
ob, err := NewSalamanderObfuscator([]byte(c.Password))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("salamander err").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := &obfsPacketConn{
|
||||||
|
first: first,
|
||||||
|
leaveSize: leaveSize,
|
||||||
|
|
||||||
|
conn: raw,
|
||||||
|
obfs: ob,
|
||||||
|
}
|
||||||
|
|
||||||
|
if first {
|
||||||
|
conn.readBuf = make([]byte, 8192)
|
||||||
|
conn.writeBuf = make([]byte, 8192)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnServer(c *Config, raw net.PacketConn, first bool, leaveSize int32) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, first, leaveSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) Size() int32 {
|
||||||
|
return smSaltLen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
if c.first {
|
||||||
|
c.readMutex.Lock()
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(c.readBuf)
|
||||||
|
if err != nil {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p) < n-int(c.Size()) {
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.obfs.Deobfuscate(c.readBuf[:n], p)
|
||||||
|
|
||||||
|
c.readMutex.Unlock()
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, addr, err = c.conn.ReadFrom(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < int(c.Size()) {
|
||||||
|
return 0, addr, errors.New("salamander").Base(io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.obfs.Deobfuscate(p[:n], p)
|
||||||
|
|
||||||
|
return n - int(c.Size()), addr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
if c.first {
|
||||||
|
if c.leaveSize+c.Size()+int32(len(p)) > 8192 {
|
||||||
|
return 0, errors.New("too many masks")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Lock()
|
||||||
|
|
||||||
|
n = copy(c.writeBuf[c.leaveSize+c.Size():], p)
|
||||||
|
n += int(c.leaveSize) + int(c.Size())
|
||||||
|
|
||||||
|
c.obfs.Obfuscate(c.writeBuf[c.leaveSize+c.Size():n], c.writeBuf[c.leaveSize:n])
|
||||||
|
|
||||||
|
nn, err := c.conn.WriteTo(c.writeBuf[:n], addr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nn != n {
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return 0, errors.New("nn != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.writeMutex.Unlock()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.obfs.Obfuscate(p[c.leaveSize+c.Size():], p[c.leaveSize:])
|
||||||
|
|
||||||
|
return c.conn.WriteTo(p, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *obfsPacketConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const udpBufferSize = 2048 // QUIC packets are at most 1500 bytes long, so 2k should be more than enough
|
|
||||||
|
|
||||||
// Obfuscator is the interface that wraps the Obfuscate and Deobfuscate methods.
|
|
||||||
// Both methods return the number of bytes written to out.
|
|
||||||
// If a packet is not valid, the methods should return 0.
|
|
||||||
type Obfuscator interface {
|
|
||||||
Obfuscate(in, out []byte) int
|
|
||||||
Deobfuscate(in, out []byte) int
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ net.PacketConn = (*obfsPacketConn)(nil)
|
|
||||||
|
|
||||||
type obfsPacketConn struct {
|
|
||||||
Conn net.PacketConn
|
|
||||||
Obfs Obfuscator
|
|
||||||
|
|
||||||
readBuf []byte
|
|
||||||
readMutex sync.Mutex
|
|
||||||
writeBuf []byte
|
|
||||||
writeMutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// obfsPacketConnUDP is a special case of obfsPacketConn that uses a UDPConn
|
|
||||||
// as the underlying connection. We pass additional methods to quic-go to
|
|
||||||
// enable UDP-specific optimizations.
|
|
||||||
type obfsPacketConnUDP struct {
|
|
||||||
*obfsPacketConn
|
|
||||||
UDPConn *net.UDPConn
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapPacketConn enables obfuscation on a net.PacketConn.
|
|
||||||
// The obfuscation is transparent to the caller - the n bytes returned by
|
|
||||||
// ReadFrom and WriteTo are the number of original bytes, not after
|
|
||||||
// obfuscation/deobfuscation.
|
|
||||||
func WrapPacketConn(conn net.PacketConn, obfs Obfuscator) net.PacketConn {
|
|
||||||
opc := &obfsPacketConn{
|
|
||||||
Conn: conn,
|
|
||||||
Obfs: obfs,
|
|
||||||
readBuf: make([]byte, udpBufferSize),
|
|
||||||
writeBuf: make([]byte, udpBufferSize),
|
|
||||||
}
|
|
||||||
if udpConn, ok := conn.(*net.UDPConn); ok {
|
|
||||||
return &obfsPacketConnUDP{
|
|
||||||
obfsPacketConn: opc,
|
|
||||||
UDPConn: udpConn,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return opc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *obfsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|
||||||
for {
|
|
||||||
c.readMutex.Lock()
|
|
||||||
n, addr, err = c.Conn.ReadFrom(c.readBuf)
|
|
||||||
if n <= 0 {
|
|
||||||
c.readMutex.Unlock()
|
|
||||||
return n, addr, err
|
|
||||||
}
|
|
||||||
n = c.Obfs.Deobfuscate(c.readBuf[:n], p)
|
|
||||||
c.readMutex.Unlock()
|
|
||||||
if n > 0 || err != nil {
|
|
||||||
return n, addr, err
|
|
||||||
}
|
|
||||||
// Invalid packet, try again
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *obfsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|
||||||
c.writeMutex.Lock()
|
|
||||||
nn := c.Obfs.Obfuscate(p, c.writeBuf)
|
|
||||||
_, err = c.Conn.WriteTo(c.writeBuf[:nn], addr)
|
|
||||||
c.writeMutex.Unlock()
|
|
||||||
if err == nil {
|
|
||||||
n = len(p)
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *obfsPacketConn) Close() error {
|
|
||||||
return c.Conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *obfsPacketConn) LocalAddr() net.Addr {
|
|
||||||
return c.Conn.LocalAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *obfsPacketConn) SetDeadline(t time.Time) error {
|
|
||||||
return c.Conn.SetDeadline(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *obfsPacketConn) SetReadDeadline(t time.Time) error {
|
|
||||||
return c.Conn.SetReadDeadline(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *obfsPacketConn) SetWriteDeadline(t time.Time) error {
|
|
||||||
return c.Conn.SetWriteDeadline(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UDP-specific methods below
|
|
||||||
|
|
||||||
func (c *obfsPacketConnUDP) SetReadBuffer(bytes int) error {
|
|
||||||
return c.UDPConn.SetReadBuffer(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *obfsPacketConnUDP) SetWriteBuffer(bytes int) error {
|
|
||||||
return c.UDPConn.SetWriteBuffer(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *obfsPacketConnUDP) SyscallConn() (syscall.RawConn, error) {
|
|
||||||
return c.UDPConn.SyscallConn()
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package obfs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkSalamanderObfuscator_Obfuscate(b *testing.B) {
|
|
||||||
o, _ := NewSalamanderObfuscator([]byte("average_password"))
|
|
||||||
in := make([]byte, 1200)
|
|
||||||
_, _ = rand.Read(in)
|
|
||||||
out := make([]byte, 2048)
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
o.Obfuscate(in, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSalamanderObfuscator_Deobfuscate(b *testing.B) {
|
|
||||||
o, _ := NewSalamanderObfuscator([]byte("average_password"))
|
|
||||||
in := make([]byte, 1200)
|
|
||||||
_, _ = rand.Read(in)
|
|
||||||
out := make([]byte, 2048)
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
o.Deobfuscate(in, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSalamanderObfuscator(t *testing.T) {
|
|
||||||
o, _ := NewSalamanderObfuscator([]byte("average_password"))
|
|
||||||
in := make([]byte, 1200)
|
|
||||||
oOut := make([]byte, 2048)
|
|
||||||
dOut := make([]byte, 2048)
|
|
||||||
for i := 0; i < 1000; i++ {
|
|
||||||
_, _ = rand.Read(in)
|
|
||||||
n := o.Obfuscate(in, oOut)
|
|
||||||
assert.Equal(t, len(in)+smSaltLen, n)
|
|
||||||
n = o.Deobfuscate(oOut[:n], dOut)
|
|
||||||
assert.Equal(t, len(in), n)
|
|
||||||
assert.Equal(t, in, dOut[:n])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-3
@@ -1,4 +1,4 @@
|
|||||||
package obfs
|
package salamander
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -15,8 +15,6 @@ const (
|
|||||||
smKeyLen = blake2b.Size256
|
smKeyLen = blake2b.Size256
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Obfuscator = (*SalamanderObfuscator)(nil)
|
|
||||||
|
|
||||||
var ErrPSKTooShort = fmt.Errorf("PSK must be at least %d bytes", smPSKMinLen)
|
var ErrPSKTooShort = fmt.Errorf("PSK must be at least %d bytes", smPSKMinLen)
|
||||||
|
|
||||||
// SalamanderObfuscator is an obfuscator that obfuscates each packet with
|
// SalamanderObfuscator is an obfuscator that obfuscates each packet with
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package salamander_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
smSaltLen = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkSalamanderObfuscator_Obfuscate(b *testing.B) {
|
||||||
|
o, _ := salamander.NewSalamanderObfuscator([]byte("average_password"))
|
||||||
|
in := make([]byte, 1200)
|
||||||
|
_, _ = rand.Read(in)
|
||||||
|
out := make([]byte, 2048)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
o.Obfuscate(in, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSalamanderObfuscator_Deobfuscate(b *testing.B) {
|
||||||
|
o, _ := salamander.NewSalamanderObfuscator([]byte("average_password"))
|
||||||
|
in := make([]byte, 1200)
|
||||||
|
_, _ = rand.Read(in)
|
||||||
|
out := make([]byte, 2048)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
o.Deobfuscate(in, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSalamanderObfuscator(t *testing.T) {
|
||||||
|
o, _ := salamander.NewSalamanderObfuscator([]byte("average_password"))
|
||||||
|
in := make([]byte, 1200)
|
||||||
|
oOut := make([]byte, 2048)
|
||||||
|
dOut := make([]byte, 2048)
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
_, _ = rand.Read(in)
|
||||||
|
n := o.Obfuscate(in, oOut)
|
||||||
|
assert.Equal(t, len(in)+smSaltLen, n)
|
||||||
|
n = o.Deobfuscate(oOut[:n], dOut)
|
||||||
|
assert.Equal(t, len(in), n)
|
||||||
|
assert.Equal(t, in, dOut[:n])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSalamanderInPlace(t *testing.T) {
|
||||||
|
o, _ := salamander.NewSalamanderObfuscator([]byte("average_password"))
|
||||||
|
|
||||||
|
in := make([]byte, 1200)
|
||||||
|
out := make([]byte, 2048)
|
||||||
|
_, _ = rand.Read(in)
|
||||||
|
o.Obfuscate(in, out)
|
||||||
|
|
||||||
|
out2 := make([]byte, 2048)
|
||||||
|
copy(out2[smSaltLen:], in)
|
||||||
|
o.Obfuscate(out2[smSaltLen:], out2)
|
||||||
|
|
||||||
|
dOut := make([]byte, 2048)
|
||||||
|
o.Deobfuscate(out, dOut)
|
||||||
|
|
||||||
|
o.Deobfuscate(out2, out2)
|
||||||
|
|
||||||
|
assert.Equal(t, in, dOut[:1200])
|
||||||
|
assert.Equal(t, in, out2[:1200])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSalamanderBounce(t *testing.T) {
|
||||||
|
o, _ := salamander.NewSalamanderObfuscator([]byte("average_password"))
|
||||||
|
buf := make([]byte, 8)
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
_, _ = rand.Read(buf)
|
||||||
|
n := o.Deobfuscate(buf, buf)
|
||||||
|
assert.Equal(t, 0, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package finalmask_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/dns"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/srtp"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/utp"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/wechat"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/header/wireguard"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/aes128gcm"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/mkcp/original"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/finalmask/salamander"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mustSendRecv(
|
||||||
|
t *testing.T,
|
||||||
|
from net.PacketConn,
|
||||||
|
to net.PacketConn,
|
||||||
|
msg []byte,
|
||||||
|
) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_, err := from.WriteTo(msg, to.LocalAddr())
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, _, err := to.ReadFrom(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != len(msg) {
|
||||||
|
t.Fatalf("unexpected size: %d", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(buf[:n], msg) {
|
||||||
|
t.Fatalf("unexpected data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type layerMask struct {
|
||||||
|
name string
|
||||||
|
mask finalmask.Udpmask
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacketConnReadWrite(t *testing.T) {
|
||||||
|
cases := []layerMask{
|
||||||
|
{
|
||||||
|
name: "aes128gcm",
|
||||||
|
mask: &aes128gcm.Config{Password: "123"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "original",
|
||||||
|
mask: &original.Config{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dns",
|
||||||
|
mask: &dns.Config{Domain: "www.baidu.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "srtp",
|
||||||
|
mask: &srtp.Config{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "utp",
|
||||||
|
mask: &utp.Config{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wechat",
|
||||||
|
mask: &wechat.Config{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wireguard",
|
||||||
|
mask: &wireguard.Config{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "salamander",
|
||||||
|
mask: &salamander.Config{Password: "1234"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
mask := c.mask
|
||||||
|
|
||||||
|
maskManager := finalmask.NewUdpmaskManager([]finalmask.Udpmask{mask, mask})
|
||||||
|
|
||||||
|
client, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
client, err = maskManager.WrapPacketConnClient(client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
server, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
server, err = maskManager.WrapPacketConnServer(server)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = client.SetDeadline(time.Now().Add(time.Second))
|
||||||
|
_ = server.SetDeadline(time.Now().Add(time.Second))
|
||||||
|
|
||||||
|
mustSendRecv(t, client, server, []byte("client -> server"))
|
||||||
|
mustSendRecv(t, server, client, []byte("server -> client"))
|
||||||
|
|
||||||
|
mustSendRecv(t, client, server, []byte{})
|
||||||
|
mustSendRecv(t, server, client, []byte{})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,373 @@
|
|||||||
|
package xdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base32"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
numPadding = 3
|
||||||
|
numPaddingForPoll = 8
|
||||||
|
initPollDelay = 500 * time.Millisecond
|
||||||
|
maxPollDelay = 10 * time.Second
|
||||||
|
pollDelayMultiplier = 2.0
|
||||||
|
pollLimit = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
var base32Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
|
||||||
|
|
||||||
|
type packet struct {
|
||||||
|
p []byte
|
||||||
|
addr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
type xdnsConnClient struct {
|
||||||
|
conn net.PacketConn
|
||||||
|
|
||||||
|
clientID []byte
|
||||||
|
domain Name
|
||||||
|
|
||||||
|
pollChan chan struct{}
|
||||||
|
readQueue chan *packet
|
||||||
|
writeQueue chan *packet
|
||||||
|
|
||||||
|
closed bool
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnClient(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) {
|
||||||
|
if !end {
|
||||||
|
return nil, errors.New("xdns requires being at the outermost level")
|
||||||
|
}
|
||||||
|
|
||||||
|
domain, err := ParseName(c.Domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := &xdnsConnClient{
|
||||||
|
conn: raw,
|
||||||
|
|
||||||
|
clientID: make([]byte, 8),
|
||||||
|
domain: domain,
|
||||||
|
|
||||||
|
pollChan: make(chan struct{}, pollLimit),
|
||||||
|
readQueue: make(chan *packet, 128),
|
||||||
|
writeQueue: make(chan *packet, 128),
|
||||||
|
}
|
||||||
|
|
||||||
|
rand.Read(conn.clientID)
|
||||||
|
|
||||||
|
go conn.recvLoop()
|
||||||
|
go conn.sendLoop()
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnClient) recvLoop() {
|
||||||
|
for {
|
||||||
|
if c.closed {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf [4096]byte
|
||||||
|
|
||||||
|
n, addr, err := c.conn.ReadFrom(buf[:])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := MessageFromWireFormat(buf[:n])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := dnsResponsePayload(&resp, c.domain)
|
||||||
|
|
||||||
|
r := bytes.NewReader(payload)
|
||||||
|
anyPacket := false
|
||||||
|
for {
|
||||||
|
p, err := nextPacket(r)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
anyPacket = true
|
||||||
|
|
||||||
|
buf := make([]byte, len(p))
|
||||||
|
copy(buf, p)
|
||||||
|
select {
|
||||||
|
case c.readQueue <- &packet{
|
||||||
|
p: buf,
|
||||||
|
addr: addr,
|
||||||
|
}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if anyPacket {
|
||||||
|
select {
|
||||||
|
case c.pollChan <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(c.pollChan)
|
||||||
|
close(c.readQueue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnClient) sendLoop() {
|
||||||
|
var addr net.Addr
|
||||||
|
|
||||||
|
pollDelay := initPollDelay
|
||||||
|
pollTimer := time.NewTimer(pollDelay)
|
||||||
|
for {
|
||||||
|
var p *packet
|
||||||
|
pollTimerExpired := false
|
||||||
|
|
||||||
|
select {
|
||||||
|
case p = <-c.writeQueue:
|
||||||
|
default:
|
||||||
|
select {
|
||||||
|
case p = <-c.writeQueue:
|
||||||
|
case <-c.pollChan:
|
||||||
|
case <-pollTimer.C:
|
||||||
|
pollTimerExpired = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if p != nil {
|
||||||
|
addr = p.addr
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-c.pollChan:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
} else if addr != nil {
|
||||||
|
encoded, _ := encode(nil, c.clientID, c.domain)
|
||||||
|
p = &packet{
|
||||||
|
p: encoded,
|
||||||
|
addr: addr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pollTimerExpired {
|
||||||
|
pollDelay = time.Duration(float64(pollDelay) * pollDelayMultiplier)
|
||||||
|
if pollDelay > maxPollDelay {
|
||||||
|
pollDelay = maxPollDelay
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !pollTimer.Stop() {
|
||||||
|
<-pollTimer.C
|
||||||
|
}
|
||||||
|
pollDelay = initPollDelay
|
||||||
|
}
|
||||||
|
pollTimer.Reset(pollDelay)
|
||||||
|
|
||||||
|
if c.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if p != nil {
|
||||||
|
_, _ = c.conn.WriteTo(p.p, p.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnClient) Size() int32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnClient) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
packet, ok := <-c.readQueue
|
||||||
|
if !ok {
|
||||||
|
return 0, nil, io.EOF
|
||||||
|
}
|
||||||
|
n = copy(p, packet.p)
|
||||||
|
if n != len(packet.p) {
|
||||||
|
return 0, nil, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
return n, packet.addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
if c.closed {
|
||||||
|
return 0, errors.New("xdns closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
encoded, err := encode(p, c.clientID, c.domain)
|
||||||
|
if err != nil {
|
||||||
|
errors.LogDebug(context.Background(), "xdns encode err", err)
|
||||||
|
return 0, errors.New("xdns encode").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case c.writeQueue <- &packet{
|
||||||
|
p: encoded,
|
||||||
|
addr: addr,
|
||||||
|
}:
|
||||||
|
return len(p), nil
|
||||||
|
default:
|
||||||
|
return 0, errors.New("xdns queue full")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnClient) Close() error {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
if c.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.closed = true
|
||||||
|
close(c.writeQueue)
|
||||||
|
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnClient) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnClient) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnClient) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnClient) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(p []byte, clientID []byte, domain Name) ([]byte, error) {
|
||||||
|
var decoded []byte
|
||||||
|
{
|
||||||
|
if len(p) >= 224 {
|
||||||
|
return nil, errors.New("too long")
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.Write(clientID[:])
|
||||||
|
n := numPadding
|
||||||
|
if len(p) == 0 {
|
||||||
|
n = numPaddingForPoll
|
||||||
|
}
|
||||||
|
buf.WriteByte(byte(224 + n))
|
||||||
|
_, _ = io.CopyN(&buf, rand.Reader, int64(n))
|
||||||
|
if len(p) > 0 {
|
||||||
|
buf.WriteByte(byte(len(p)))
|
||||||
|
buf.Write(p)
|
||||||
|
}
|
||||||
|
decoded = buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
encoded := make([]byte, base32Encoding.EncodedLen(len(decoded)))
|
||||||
|
base32Encoding.Encode(encoded, decoded)
|
||||||
|
encoded = bytes.ToLower(encoded)
|
||||||
|
labels := chunks(encoded, 63)
|
||||||
|
labels = append(labels, domain...)
|
||||||
|
name, err := NewName(labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var id uint16
|
||||||
|
_ = binary.Read(rand.Reader, binary.BigEndian, &id)
|
||||||
|
query := &Message{
|
||||||
|
ID: id,
|
||||||
|
Flags: 0x0100,
|
||||||
|
Question: []Question{
|
||||||
|
{
|
||||||
|
Name: name,
|
||||||
|
Type: RRTypeTXT,
|
||||||
|
Class: ClassIN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Additional: []RR{
|
||||||
|
{
|
||||||
|
Name: Name{},
|
||||||
|
Type: RRTypeOPT,
|
||||||
|
Class: 4096,
|
||||||
|
TTL: 0,
|
||||||
|
Data: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := query.WireFormat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func chunks(p []byte, n int) [][]byte {
|
||||||
|
var result [][]byte
|
||||||
|
for len(p) > 0 {
|
||||||
|
sz := len(p)
|
||||||
|
if sz > n {
|
||||||
|
sz = n
|
||||||
|
}
|
||||||
|
result = append(result, p[:sz])
|
||||||
|
p = p[sz:]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextPacket(r *bytes.Reader) ([]byte, error) {
|
||||||
|
var n uint16
|
||||||
|
err := binary.Read(r, binary.BigEndian, &n)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := make([]byte, n)
|
||||||
|
_, err = io.ReadFull(r, p)
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func dnsResponsePayload(resp *Message, domain Name) []byte {
|
||||||
|
if resp.Flags&0x8000 != 0x8000 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if resp.Flags&0x000f != RcodeNoError {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.Answer) != 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
answer := resp.Answer[0]
|
||||||
|
|
||||||
|
_, ok := answer.Name.TrimSuffix(domain)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if answer.Type != RRTypeTXT {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
payload, err := DecodeRDataTXT(answer.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package xdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Config) UDP() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnClient(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnClient(c, raw, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) WrapPacketConnServer(raw net.PacketConn, first bool, leaveSize int32, end bool) (net.PacketConn, error) {
|
||||||
|
return NewConnServer(c, raw, end)
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.36.10
|
||||||
|
// protoc v6.33.1
|
||||||
|
// source: transport/internet/finalmask/xdns/config.proto
|
||||||
|
|
||||||
|
package xdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
unsafe "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_transport_internet_finalmask_xdns_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_transport_internet_finalmask_xdns_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_transport_internet_finalmask_xdns_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetDomain() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Domain
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_transport_internet_finalmask_xdns_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
const file_transport_internet_finalmask_xdns_config_proto_rawDesc = "" +
|
||||||
|
"\n" +
|
||||||
|
".transport/internet/finalmask/xdns/config.proto\x12&xray.transport.internet.finalmask.xdns\" \n" +
|
||||||
|
"\x06Config\x12\x16\n" +
|
||||||
|
"\x06domain\x18\x01 \x01(\tR\x06domainB\x94\x01\n" +
|
||||||
|
"*com.xray.transport.internet.finalmask.xdnsP\x01Z;github.com/xtls/xray-core/transport/internet/finalmask/xdns\xaa\x02&Xray.Transport.Internet.Finalmask.Xdnsb\x06proto3"
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_transport_internet_finalmask_xdns_config_proto_rawDescOnce sync.Once
|
||||||
|
file_transport_internet_finalmask_xdns_config_proto_rawDescData []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_transport_internet_finalmask_xdns_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_transport_internet_finalmask_xdns_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_transport_internet_finalmask_xdns_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_xdns_config_proto_rawDesc), len(file_transport_internet_finalmask_xdns_config_proto_rawDesc)))
|
||||||
|
})
|
||||||
|
return file_transport_internet_finalmask_xdns_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_transport_internet_finalmask_xdns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_transport_internet_finalmask_xdns_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.transport.internet.finalmask.xdns.Config
|
||||||
|
}
|
||||||
|
var file_transport_internet_finalmask_xdns_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_transport_internet_finalmask_xdns_config_proto_init() }
|
||||||
|
func file_transport_internet_finalmask_xdns_config_proto_init() {
|
||||||
|
if File_transport_internet_finalmask_xdns_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_finalmask_xdns_config_proto_rawDesc), len(file_transport_internet_finalmask_xdns_config_proto_rawDesc)),
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_transport_internet_finalmask_xdns_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_transport_internet_finalmask_xdns_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_transport_internet_finalmask_xdns_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_transport_internet_finalmask_xdns_config_proto = out.File
|
||||||
|
file_transport_internet_finalmask_xdns_config_proto_goTypes = nil
|
||||||
|
file_transport_internet_finalmask_xdns_config_proto_depIdxs = nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.transport.internet.finalmask.xdns;
|
||||||
|
option csharp_namespace = "Xray.Transport.Internet.Finalmask.Xdns";
|
||||||
|
option go_package = "github.com/xtls/xray-core/transport/internet/finalmask/xdns";
|
||||||
|
option java_package = "com.xray.transport.internet.finalmask.xdns";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message Config {
|
||||||
|
string domain = 1;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,575 @@
|
|||||||
|
// Package dns deals with encoding and decoding DNS wire format.
|
||||||
|
package xdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The maximum number of DNS name compression pointers we are willing to follow.
|
||||||
|
// Without something like this, infinite loops are possible.
|
||||||
|
const compressionPointerLimit = 10
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrZeroLengthLabel is the error returned for names that contain a
|
||||||
|
// zero-length label, like "example..com".
|
||||||
|
ErrZeroLengthLabel = errors.New("name contains a zero-length label")
|
||||||
|
|
||||||
|
// ErrLabelTooLong is the error returned for labels that are longer than
|
||||||
|
// 63 octets.
|
||||||
|
ErrLabelTooLong = errors.New("name contains a label longer than 63 octets")
|
||||||
|
|
||||||
|
// ErrNameTooLong is the error returned for names whose encoded
|
||||||
|
// representation is longer than 255 octets.
|
||||||
|
ErrNameTooLong = errors.New("name is longer than 255 octets")
|
||||||
|
|
||||||
|
// ErrReservedLabelType is the error returned when reading a label type
|
||||||
|
// prefix whose two most significant bits are not 00 or 11.
|
||||||
|
ErrReservedLabelType = errors.New("reserved label type")
|
||||||
|
|
||||||
|
// ErrTooManyPointers is the error returned when reading a compressed
|
||||||
|
// name that has too many compression pointers.
|
||||||
|
ErrTooManyPointers = errors.New("too many compression pointers")
|
||||||
|
|
||||||
|
// ErrTrailingBytes is the error returned when bytes remain in the parse
|
||||||
|
// buffer after parsing a message.
|
||||||
|
ErrTrailingBytes = errors.New("trailing bytes after message")
|
||||||
|
|
||||||
|
// ErrIntegerOverflow is the error returned when trying to encode an
|
||||||
|
// integer greater than 65535 into a 16-bit field.
|
||||||
|
ErrIntegerOverflow = errors.New("integer overflow")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.2.2
|
||||||
|
RRTypeTXT = 16
|
||||||
|
// https://tools.ietf.org/html/rfc6891#section-6.1.1
|
||||||
|
RRTypeOPT = 41
|
||||||
|
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.2.4
|
||||||
|
ClassIN = 1
|
||||||
|
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.1
|
||||||
|
RcodeNoError = 0 // a.k.a. NOERROR
|
||||||
|
RcodeFormatError = 1 // a.k.a. FORMERR
|
||||||
|
RcodeNameError = 3 // a.k.a. NXDOMAIN
|
||||||
|
RcodeNotImplemented = 4 // a.k.a. NOTIMPL
|
||||||
|
// https://tools.ietf.org/html/rfc6891#section-9
|
||||||
|
ExtendedRcodeBadVers = 16 // a.k.a. BADVERS
|
||||||
|
)
|
||||||
|
|
||||||
|
// Name represents a domain name, a sequence of labels each of which is 63
|
||||||
|
// octets or less in length.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.1
|
||||||
|
type Name [][]byte
|
||||||
|
|
||||||
|
// NewName returns a Name from a slice of labels, after checking the labels for
|
||||||
|
// validity. Does not include a zero-length label at the end of the slice.
|
||||||
|
func NewName(labels [][]byte) (Name, error) {
|
||||||
|
name := Name(labels)
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-2.3.4
|
||||||
|
// Various objects and parameters in the DNS have size limits.
|
||||||
|
// labels 63 octets or less
|
||||||
|
// names 255 octets or less
|
||||||
|
for _, label := range labels {
|
||||||
|
if len(label) == 0 {
|
||||||
|
return nil, ErrZeroLengthLabel
|
||||||
|
}
|
||||||
|
if len(label) > 63 {
|
||||||
|
return nil, ErrLabelTooLong
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check the total length.
|
||||||
|
builder := newMessageBuilder()
|
||||||
|
builder.WriteName(name)
|
||||||
|
if len(builder.Bytes()) > 255 {
|
||||||
|
return nil, ErrNameTooLong
|
||||||
|
}
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseName returns a new Name from a string of labels separated by dots, after
|
||||||
|
// checking the name for validity. A single dot at the end of the string is
|
||||||
|
// ignored.
|
||||||
|
func ParseName(s string) (Name, error) {
|
||||||
|
b := bytes.TrimSuffix([]byte(s), []byte("."))
|
||||||
|
if len(b) == 0 {
|
||||||
|
// bytes.Split(b, ".") would return [""] in this case
|
||||||
|
return NewName([][]byte{})
|
||||||
|
} else {
|
||||||
|
return NewName(bytes.Split(b, []byte(".")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a reversible string representation of name. Labels are
|
||||||
|
// separated by dots, and any bytes in a label that are outside the set
|
||||||
|
// [0-9A-Za-z-] are replaced with a \xXX hex escape sequence.
|
||||||
|
func (name Name) String() string {
|
||||||
|
if len(name) == 0 {
|
||||||
|
return "."
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf strings.Builder
|
||||||
|
for i, label := range name {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteByte('.')
|
||||||
|
}
|
||||||
|
for _, b := range label {
|
||||||
|
if b == '-' ||
|
||||||
|
('0' <= b && b <= '9') ||
|
||||||
|
('A' <= b && b <= 'Z') ||
|
||||||
|
('a' <= b && b <= 'z') {
|
||||||
|
buf.WriteByte(b)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(&buf, "\\x%02x", b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrimSuffix returns a Name with the given suffix removed, if it was present.
|
||||||
|
// The second return value indicates whether the suffix was present. If the
|
||||||
|
// suffix was not present, the first return value is nil.
|
||||||
|
func (name Name) TrimSuffix(suffix Name) (Name, bool) {
|
||||||
|
if len(name) < len(suffix) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
split := len(name) - len(suffix)
|
||||||
|
fore, aft := name[:split], name[split:]
|
||||||
|
for i := 0; i < len(aft); i++ {
|
||||||
|
if !bytes.Equal(bytes.ToLower(aft[i]), bytes.ToLower(suffix[i])) {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fore, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message represents a DNS message.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1
|
||||||
|
type Message struct {
|
||||||
|
ID uint16
|
||||||
|
Flags uint16
|
||||||
|
|
||||||
|
Question []Question
|
||||||
|
Answer []RR
|
||||||
|
Authority []RR
|
||||||
|
Additional []RR
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opcode extracts the OPCODE part of the Flags field.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.1
|
||||||
|
func (message *Message) Opcode() uint16 {
|
||||||
|
return (message.Flags >> 11) & 0xf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rcode extracts the RCODE part of the Flags field.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.1
|
||||||
|
func (message *Message) Rcode() uint16 {
|
||||||
|
return message.Flags & 0x000f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Question represents an entry in the question section of a message.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.2
|
||||||
|
type Question struct {
|
||||||
|
Name Name
|
||||||
|
Type uint16
|
||||||
|
Class uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// RR represents a resource record.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.3
|
||||||
|
type RR struct {
|
||||||
|
Name Name
|
||||||
|
Type uint16
|
||||||
|
Class uint16
|
||||||
|
TTL uint32
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// readName parses a DNS name from r. It leaves r positioned just after the
|
||||||
|
// parsed name.
|
||||||
|
func readName(r io.ReadSeeker) (Name, error) {
|
||||||
|
var labels [][]byte
|
||||||
|
// We limit the number of compression pointers we are willing to follow.
|
||||||
|
numPointers := 0
|
||||||
|
// If we followed any compression pointers, we must finally seek to just
|
||||||
|
// past the first pointer.
|
||||||
|
var seekTo int64
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
var labelType byte
|
||||||
|
err := binary.Read(r, binary.BigEndian, &labelType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch labelType & 0xc0 {
|
||||||
|
case 0x00:
|
||||||
|
// This is an ordinary label.
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.1
|
||||||
|
length := int(labelType & 0x3f)
|
||||||
|
if length == 0 {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
label := make([]byte, length)
|
||||||
|
_, err := io.ReadFull(r, label)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
labels = append(labels, label)
|
||||||
|
case 0xc0:
|
||||||
|
// This is a compression pointer.
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.4
|
||||||
|
upper := labelType & 0x3f
|
||||||
|
var lower byte
|
||||||
|
err := binary.Read(r, binary.BigEndian, &lower)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
offset := (uint16(upper) << 8) | uint16(lower)
|
||||||
|
|
||||||
|
if numPointers == 0 {
|
||||||
|
// The first time we encounter a pointer,
|
||||||
|
// remember our position so we can seek back to
|
||||||
|
// it when done.
|
||||||
|
seekTo, err = r.Seek(0, io.SeekCurrent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numPointers++
|
||||||
|
if numPointers > compressionPointerLimit {
|
||||||
|
return nil, ErrTooManyPointers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Follow the pointer and continue.
|
||||||
|
_, err = r.Seek(int64(offset), io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// "The 10 and 01 combinations are reserved for future
|
||||||
|
// use."
|
||||||
|
return nil, ErrReservedLabelType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we followed any pointers, then seek back to just after the first
|
||||||
|
// one.
|
||||||
|
if numPointers > 0 {
|
||||||
|
_, err := r.Seek(seekTo, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NewName(labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readQuestion parses one entry from the Question section. It leaves r
|
||||||
|
// positioned just after the parsed entry.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.2
|
||||||
|
func readQuestion(r io.ReadSeeker) (Question, error) {
|
||||||
|
var question Question
|
||||||
|
var err error
|
||||||
|
question.Name, err = readName(r)
|
||||||
|
if err != nil {
|
||||||
|
return question, err
|
||||||
|
}
|
||||||
|
for _, ptr := range []*uint16{&question.Type, &question.Class} {
|
||||||
|
err := binary.Read(r, binary.BigEndian, ptr)
|
||||||
|
if err != nil {
|
||||||
|
return question, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return question, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readRR parses one resource record. It leaves r positioned just after the
|
||||||
|
// parsed resource record.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.3
|
||||||
|
func readRR(r io.ReadSeeker) (RR, error) {
|
||||||
|
var rr RR
|
||||||
|
var err error
|
||||||
|
rr.Name, err = readName(r)
|
||||||
|
if err != nil {
|
||||||
|
return rr, err
|
||||||
|
}
|
||||||
|
for _, ptr := range []*uint16{&rr.Type, &rr.Class} {
|
||||||
|
err := binary.Read(r, binary.BigEndian, ptr)
|
||||||
|
if err != nil {
|
||||||
|
return rr, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = binary.Read(r, binary.BigEndian, &rr.TTL)
|
||||||
|
if err != nil {
|
||||||
|
return rr, err
|
||||||
|
}
|
||||||
|
var rdLength uint16
|
||||||
|
err = binary.Read(r, binary.BigEndian, &rdLength)
|
||||||
|
if err != nil {
|
||||||
|
return rr, err
|
||||||
|
}
|
||||||
|
rr.Data = make([]byte, rdLength)
|
||||||
|
_, err = io.ReadFull(r, rr.Data)
|
||||||
|
if err != nil {
|
||||||
|
return rr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readMessage parses a complete DNS message. It leaves r positioned just after
|
||||||
|
// the parsed message.
|
||||||
|
func readMessage(r io.ReadSeeker) (Message, error) {
|
||||||
|
var message Message
|
||||||
|
|
||||||
|
// Header section
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.1
|
||||||
|
var qdCount, anCount, nsCount, arCount uint16
|
||||||
|
for _, ptr := range []*uint16{
|
||||||
|
&message.ID, &message.Flags,
|
||||||
|
&qdCount, &anCount, &nsCount, &arCount,
|
||||||
|
} {
|
||||||
|
err := binary.Read(r, binary.BigEndian, ptr)
|
||||||
|
if err != nil {
|
||||||
|
return message, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Question section
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.2
|
||||||
|
for i := 0; i < int(qdCount); i++ {
|
||||||
|
question, err := readQuestion(r)
|
||||||
|
if err != nil {
|
||||||
|
return message, err
|
||||||
|
}
|
||||||
|
message.Question = append(message.Question, question)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Answer, Authority, and Additional sections
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.3
|
||||||
|
for _, rec := range []struct {
|
||||||
|
ptr *[]RR
|
||||||
|
count uint16
|
||||||
|
}{
|
||||||
|
{&message.Answer, anCount},
|
||||||
|
{&message.Authority, nsCount},
|
||||||
|
{&message.Additional, arCount},
|
||||||
|
} {
|
||||||
|
for i := 0; i < int(rec.count); i++ {
|
||||||
|
rr, err := readRR(r)
|
||||||
|
if err != nil {
|
||||||
|
return message, err
|
||||||
|
}
|
||||||
|
*rec.ptr = append(*rec.ptr, rr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return message, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageFromWireFormat parses a message from buf and returns a Message object.
|
||||||
|
// It returns ErrTrailingBytes if there are bytes remaining in buf after parsing
|
||||||
|
// is done.
|
||||||
|
func MessageFromWireFormat(buf []byte) (Message, error) {
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
message, err := readMessage(r)
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
} else if err == nil {
|
||||||
|
// Check for trailing bytes.
|
||||||
|
_, err = r.ReadByte()
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
} else if err == nil {
|
||||||
|
err = ErrTrailingBytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// messageBuilder manages the state of serializing a DNS message. Its main
|
||||||
|
// function is to keep track of names already written for the purpose of name
|
||||||
|
// compression.
|
||||||
|
type messageBuilder struct {
|
||||||
|
w bytes.Buffer
|
||||||
|
nameCache map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
// newMessageBuilder creates a new messageBuilder with an empty name cache.
|
||||||
|
func newMessageBuilder() *messageBuilder {
|
||||||
|
return &messageBuilder{
|
||||||
|
nameCache: make(map[string]int),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the serialized DNS message as a slice of bytes.
|
||||||
|
func (builder *messageBuilder) Bytes() []byte {
|
||||||
|
return builder.w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteName appends name to the in-progress messageBuilder, employing
|
||||||
|
// compression pointers to previously written names if possible.
|
||||||
|
func (builder *messageBuilder) WriteName(name Name) {
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.1
|
||||||
|
for i := range name {
|
||||||
|
// Has this suffix already been encoded in the message?
|
||||||
|
if ptr, ok := builder.nameCache[name[i:].String()]; ok && ptr&0x3fff == ptr {
|
||||||
|
// If so, we can write a compression pointer.
|
||||||
|
binary.Write(&builder.w, binary.BigEndian, uint16(0xc000|ptr))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Not cached; we must encode this label verbatim. Store a cache
|
||||||
|
// entry pointing to the beginning of it.
|
||||||
|
builder.nameCache[name[i:].String()] = builder.w.Len()
|
||||||
|
length := len(name[i])
|
||||||
|
if length == 0 || length > 63 {
|
||||||
|
panic(length)
|
||||||
|
}
|
||||||
|
builder.w.WriteByte(byte(length))
|
||||||
|
builder.w.Write(name[i])
|
||||||
|
}
|
||||||
|
builder.w.WriteByte(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteQuestion appends a Question section entry to the in-progress
|
||||||
|
// messageBuilder.
|
||||||
|
func (builder *messageBuilder) WriteQuestion(question *Question) {
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.2
|
||||||
|
builder.WriteName(question.Name)
|
||||||
|
binary.Write(&builder.w, binary.BigEndian, question.Type)
|
||||||
|
binary.Write(&builder.w, binary.BigEndian, question.Class)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteRR appends a resource record to the in-progress messageBuilder. It
|
||||||
|
// returns ErrIntegerOverflow if the length of rr.Data does not fit in 16 bits.
|
||||||
|
func (builder *messageBuilder) WriteRR(rr *RR) error {
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.3
|
||||||
|
builder.WriteName(rr.Name)
|
||||||
|
binary.Write(&builder.w, binary.BigEndian, rr.Type)
|
||||||
|
binary.Write(&builder.w, binary.BigEndian, rr.Class)
|
||||||
|
binary.Write(&builder.w, binary.BigEndian, rr.TTL)
|
||||||
|
rdLength := uint16(len(rr.Data))
|
||||||
|
if int(rdLength) != len(rr.Data) {
|
||||||
|
return ErrIntegerOverflow
|
||||||
|
}
|
||||||
|
binary.Write(&builder.w, binary.BigEndian, rdLength)
|
||||||
|
builder.w.Write(rr.Data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteMessage appends a complete DNS message to the in-progress
|
||||||
|
// messageBuilder. It returns ErrIntegerOverflow if the number of entries in any
|
||||||
|
// section, or the length of the data in any resource record, does not fit in 16
|
||||||
|
// bits.
|
||||||
|
func (builder *messageBuilder) WriteMessage(message *Message) error {
|
||||||
|
// Header section
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.1
|
||||||
|
binary.Write(&builder.w, binary.BigEndian, message.ID)
|
||||||
|
binary.Write(&builder.w, binary.BigEndian, message.Flags)
|
||||||
|
for _, count := range []int{
|
||||||
|
len(message.Question),
|
||||||
|
len(message.Answer),
|
||||||
|
len(message.Authority),
|
||||||
|
len(message.Additional),
|
||||||
|
} {
|
||||||
|
count16 := uint16(count)
|
||||||
|
if int(count16) != count {
|
||||||
|
return ErrIntegerOverflow
|
||||||
|
}
|
||||||
|
binary.Write(&builder.w, binary.BigEndian, count16)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Question section
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.2
|
||||||
|
for _, question := range message.Question {
|
||||||
|
builder.WriteQuestion(&question)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Answer, Authority, and Additional sections
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-4.1.3
|
||||||
|
for _, rrs := range [][]RR{message.Answer, message.Authority, message.Additional} {
|
||||||
|
for _, rr := range rrs {
|
||||||
|
err := builder.WriteRR(&rr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WireFormat encodes a Message as a slice of bytes in DNS wire format. It
|
||||||
|
// returns ErrIntegerOverflow if the number of entries in any section, or the
|
||||||
|
// length of the data in any resource record, does not fit in 16 bits.
|
||||||
|
func (message *Message) WireFormat() ([]byte, error) {
|
||||||
|
builder := newMessageBuilder()
|
||||||
|
err := builder.WriteMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return builder.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeRDataTXT decodes TXT-DATA (as found in the RDATA for a resource record
|
||||||
|
// with TYPE=TXT) as a raw byte slice, by concatenating all the
|
||||||
|
// <character-string>s it contains.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.3.14
|
||||||
|
func DecodeRDataTXT(p []byte) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
n := int(p[0])
|
||||||
|
p = p[1:]
|
||||||
|
if len(p) < n {
|
||||||
|
return nil, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
buf.Write(p[:n])
|
||||||
|
p = p[n:]
|
||||||
|
if len(p) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeRDataTXT encodes a slice of bytes as TXT-DATA, as appropriate for the
|
||||||
|
// RDATA of a resource record with TYPE=TXT. No length restriction is enforced
|
||||||
|
// here; that must be checked at a higher level.
|
||||||
|
//
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.3.14
|
||||||
|
func EncodeRDataTXT(p []byte) []byte {
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.3
|
||||||
|
// https://tools.ietf.org/html/rfc1035#section-3.3.14
|
||||||
|
// TXT data is a sequence of one or more <character-string>s, where
|
||||||
|
// <character-string> is a length octet followed by that number of
|
||||||
|
// octets.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for len(p) > 255 {
|
||||||
|
buf.WriteByte(255)
|
||||||
|
buf.Write(p[:255])
|
||||||
|
p = p[255:]
|
||||||
|
}
|
||||||
|
// Must write here, even if len(p) == 0, because it's "*one or more*
|
||||||
|
// <character-string>s".
|
||||||
|
buf.WriteByte(byte(len(p)))
|
||||||
|
buf.Write(p)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
@@ -0,0 +1,592 @@
|
|||||||
|
package xdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func namesEqual(a, b Name) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < len(a); i++ {
|
||||||
|
if !bytes.Equal(a[i], b[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestName(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
labels [][]byte
|
||||||
|
err error
|
||||||
|
s string
|
||||||
|
}{
|
||||||
|
{[][]byte{}, nil, "."},
|
||||||
|
{[][]byte{[]byte("test")}, nil, "test"},
|
||||||
|
{[][]byte{[]byte("a"), []byte("b"), []byte("c")}, nil, "a.b.c"},
|
||||||
|
|
||||||
|
{[][]byte{{}}, ErrZeroLengthLabel, ""},
|
||||||
|
{[][]byte{[]byte("a"), {}, []byte("c")}, ErrZeroLengthLabel, ""},
|
||||||
|
|
||||||
|
// 63 octets.
|
||||||
|
{[][]byte{[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE")}, nil,
|
||||||
|
"0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"},
|
||||||
|
// 64 octets.
|
||||||
|
{[][]byte{[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF")}, ErrLabelTooLong, ""},
|
||||||
|
|
||||||
|
// 64+64+64+62 octets.
|
||||||
|
{[][]byte{
|
||||||
|
[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"),
|
||||||
|
[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"),
|
||||||
|
[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"),
|
||||||
|
[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABC"),
|
||||||
|
}, nil,
|
||||||
|
"0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE.0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE.0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE.0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABC"},
|
||||||
|
// 64+64+64+63 octets.
|
||||||
|
{[][]byte{
|
||||||
|
[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"),
|
||||||
|
[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"),
|
||||||
|
[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE"),
|
||||||
|
[]byte("0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD"),
|
||||||
|
}, ErrNameTooLong, ""},
|
||||||
|
// 127 one-octet labels.
|
||||||
|
{[][]byte{
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'},
|
||||||
|
}, nil,
|
||||||
|
"0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E.F.0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.0.1.2.3.4.5.6.7.8.9.A.B.C.D.E"},
|
||||||
|
// 128 one-octet labels.
|
||||||
|
{[][]byte{
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'},
|
||||||
|
{'0'}, {'1'}, {'2'}, {'3'}, {'4'}, {'5'}, {'6'}, {'7'}, {'8'}, {'9'}, {'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'},
|
||||||
|
}, ErrNameTooLong, ""},
|
||||||
|
} {
|
||||||
|
// Test that NewName returns proper error codes, and otherwise
|
||||||
|
// returns an equal slice of labels.
|
||||||
|
name, err := NewName(test.labels)
|
||||||
|
if err != test.err || (err == nil && !namesEqual(name, test.labels)) {
|
||||||
|
t.Errorf("%+q returned (%+q, %v), expected (%+q, %v)",
|
||||||
|
test.labels, name, err, test.labels, test.err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if test.err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that the string version of the name comes out as
|
||||||
|
// expected.
|
||||||
|
s := name.String()
|
||||||
|
if s != test.s {
|
||||||
|
t.Errorf("%+q became string %+q, expected %+q", test.labels, s, test.s)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that parsing from a string back to a Name results in the
|
||||||
|
// original slice of labels.
|
||||||
|
name, err = ParseName(s)
|
||||||
|
if err != nil || !namesEqual(name, test.labels) {
|
||||||
|
t.Errorf("%+q parsing %+q returned (%+q, %v), expected (%+q, %v)",
|
||||||
|
test.labels, s, name, err, test.labels, nil)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// A trailing dot should be ignored.
|
||||||
|
if !strings.HasSuffix(s, ".") {
|
||||||
|
dotName, dotErr := ParseName(s + ".")
|
||||||
|
if dotErr != err || !namesEqual(dotName, name) {
|
||||||
|
t.Errorf("%+q parsing %+q returned (%+q, %v), expected (%+q, %v)",
|
||||||
|
test.labels, s+".", dotName, dotErr, name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseName(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
s string
|
||||||
|
name Name
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
// This case can't be tested by TestName above because String
|
||||||
|
// will never produce "" (it produces "." instead).
|
||||||
|
{"", [][]byte{}, nil},
|
||||||
|
} {
|
||||||
|
name, err := ParseName(test.s)
|
||||||
|
if err != test.err || (err == nil && !namesEqual(name, test.name)) {
|
||||||
|
t.Errorf("%+q returned (%+q, %v), expected (%+q, %v)",
|
||||||
|
test.s, name, err, test.name, test.err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unescapeString(s string) ([][]byte, error) {
|
||||||
|
if s == "." {
|
||||||
|
return [][]byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var result [][]byte
|
||||||
|
for _, label := range strings.Split(s, ".") {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
i := 0
|
||||||
|
for i < len(label) {
|
||||||
|
switch label[i] {
|
||||||
|
case '\\':
|
||||||
|
if i+3 >= len(label) {
|
||||||
|
return nil, fmt.Errorf("truncated escape sequence at index %v", i)
|
||||||
|
}
|
||||||
|
if label[i+1] != 'x' {
|
||||||
|
return nil, fmt.Errorf("malformed escape sequence at index %v", i)
|
||||||
|
}
|
||||||
|
b, err := strconv.ParseUint(string(label[i+2:i+4]), 16, 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("malformed hex sequence at index %v", i+2)
|
||||||
|
}
|
||||||
|
buf.WriteByte(byte(b))
|
||||||
|
i += 4
|
||||||
|
default:
|
||||||
|
buf.WriteByte(label[i])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, buf.Bytes())
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNameString(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
name Name
|
||||||
|
s string
|
||||||
|
}{
|
||||||
|
{[][]byte{}, "."},
|
||||||
|
{[][]byte{[]byte("\x00"), []byte("a.b"), []byte("c\nd\\")}, "\\x00.a\\x2eb.c\\x0ad\\x5c"},
|
||||||
|
{[][]byte{
|
||||||
|
[]byte("\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>"),
|
||||||
|
[]byte("?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}"),
|
||||||
|
[]byte("~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc"),
|
||||||
|
[]byte("\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb"),
|
||||||
|
[]byte("\xfc\xfd\xfe\xff"),
|
||||||
|
}, "\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\\x20\\x21\\x22\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2a\\x2b\\x2c-\\x2e\\x2f0123456789\\x3a\\x3b\\x3c\\x3d\\x3e.\\x3f\\x40ABCDEFGHIJKLMNOPQRSTUVWXYZ\\x5b\\x5c\\x5d\\x5e\\x5f\\x60abcdefghijklmnopqrstuvwxyz\\x7b\\x7c\\x7d.\\x7e\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc.\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb.\\xfc\\xfd\\xfe\\xff"},
|
||||||
|
} {
|
||||||
|
s := test.name.String()
|
||||||
|
if s != test.s {
|
||||||
|
t.Errorf("%+q escaped to %+q, expected %+q", test.name, s, test.s)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
unescaped, err := unescapeString(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%+q unescaping %+q resulted in error %v", test.name, s, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !namesEqual(Name(unescaped), test.name) {
|
||||||
|
t.Errorf("%+q roundtripped through %+q to %+q", test.name, s, unescaped)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNameTrimSuffix(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
name, suffix string
|
||||||
|
trimmed string
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{"", "", ".", true},
|
||||||
|
{".", ".", ".", true},
|
||||||
|
{"abc", "", "abc", true},
|
||||||
|
{"abc", ".", "abc", true},
|
||||||
|
{"", "abc", ".", false},
|
||||||
|
{".", "abc", ".", false},
|
||||||
|
{"example.com", "com", "example", true},
|
||||||
|
{"example.com", "net", ".", false},
|
||||||
|
{"example.com", "example.com", ".", true},
|
||||||
|
{"example.com", "test.com", ".", false},
|
||||||
|
{"example.com", "xample.com", ".", false},
|
||||||
|
{"example.com", "example", ".", false},
|
||||||
|
{"example.com", "COM", "example", true},
|
||||||
|
{"EXAMPLE.COM", "com", "EXAMPLE", true},
|
||||||
|
} {
|
||||||
|
tmp, ok := mustParseName(test.name).TrimSuffix(mustParseName(test.suffix))
|
||||||
|
trimmed := tmp.String()
|
||||||
|
if ok != test.ok || trimmed != test.trimmed {
|
||||||
|
t.Errorf("TrimSuffix %+q %+q returned (%+q, %v), expected (%+q, %v)",
|
||||||
|
test.name, test.suffix, trimmed, ok, test.trimmed, test.ok)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadName(t *testing.T) {
|
||||||
|
// Good tests.
|
||||||
|
for _, test := range []struct {
|
||||||
|
start int64
|
||||||
|
end int64
|
||||||
|
input string
|
||||||
|
s string
|
||||||
|
}{
|
||||||
|
// Empty name.
|
||||||
|
{0, 1, "\x00abcd", "."},
|
||||||
|
// No pointers.
|
||||||
|
{12, 25, "AAAABBBBCCCC\x07example\x03com\x00", "example.com"},
|
||||||
|
// Backward pointer.
|
||||||
|
{25, 31, "AAAABBBBCCCC\x07example\x03com\x00\x03sub\xc0\x0c", "sub.example.com"},
|
||||||
|
// Forward pointer.
|
||||||
|
{0, 4, "\x01a\xc0\x04\x03bcd\x00", "a.bcd"},
|
||||||
|
// Two backwards pointers.
|
||||||
|
{31, 38, "AAAABBBBCCCC\x07example\x03com\x00\x03sub\xc0\x0c\x04sub2\xc0\x19", "sub2.sub.example.com"},
|
||||||
|
// Forward then backward pointer.
|
||||||
|
{25, 31, "AAAABBBBCCCC\x07example\x03com\x00\x03sub\xc0\x1f\x04sub2\xc0\x0c", "sub.sub2.example.com"},
|
||||||
|
// Overlapping codons.
|
||||||
|
{0, 4, "\x01a\xc0\x03bcd\x00", "a.bcd"},
|
||||||
|
// Pointer to empty label.
|
||||||
|
{0, 10, "\x07example\xc0\x0a\x00", "example"},
|
||||||
|
{1, 11, "\x00\x07example\xc0\x00", "example"},
|
||||||
|
// Pointer to pointer to empty label.
|
||||||
|
{0, 10, "\x07example\xc0\x0a\xc0\x0c\x00", "example"},
|
||||||
|
{1, 11, "\x00\x07example\xc0\x0c\xc0\x00", "example"},
|
||||||
|
} {
|
||||||
|
r := bytes.NewReader([]byte(test.input))
|
||||||
|
_, err := r.Seek(test.start, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
name, err := readName(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%+q returned error %s", test.input, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s := name.String()
|
||||||
|
if s != test.s {
|
||||||
|
t.Errorf("%+q returned %+q, expected %+q", test.input, s, test.s)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cur, _ := r.Seek(0, io.SeekCurrent)
|
||||||
|
if cur != test.end {
|
||||||
|
t.Errorf("%+q left offset %d, expected %d", test.input, cur, test.end)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bad tests.
|
||||||
|
for _, test := range []struct {
|
||||||
|
start int64
|
||||||
|
input string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{0, "", io.ErrUnexpectedEOF},
|
||||||
|
// Reserved label type.
|
||||||
|
{0, "\x80example", ErrReservedLabelType},
|
||||||
|
// Reserved label type.
|
||||||
|
{0, "\x40example", ErrReservedLabelType},
|
||||||
|
// No Terminating empty label.
|
||||||
|
{0, "\x07example\x03com", io.ErrUnexpectedEOF},
|
||||||
|
// Pointer past end of buffer.
|
||||||
|
{0, "\x07example\xc0\xff", io.ErrUnexpectedEOF},
|
||||||
|
// Pointer to self.
|
||||||
|
{0, "\x07example\x03com\xc0\x0c", ErrTooManyPointers},
|
||||||
|
// Pointer to self with intermediate label.
|
||||||
|
{0, "\x07example\x03com\xc0\x08", ErrTooManyPointers},
|
||||||
|
// Two pointers that point to each other.
|
||||||
|
{0, "\xc0\x02\xc0\x00", ErrTooManyPointers},
|
||||||
|
// Two pointers that point to each other, with intermediate labels.
|
||||||
|
{0, "\x01a\xc0\x04\x01b\xc0\x00", ErrTooManyPointers},
|
||||||
|
// EOF while reading label.
|
||||||
|
{0, "\x0aexample", io.ErrUnexpectedEOF},
|
||||||
|
// EOF before second byte of pointer.
|
||||||
|
{0, "\xc0", io.ErrUnexpectedEOF},
|
||||||
|
{0, "\x07example\xc0", io.ErrUnexpectedEOF},
|
||||||
|
} {
|
||||||
|
r := bytes.NewReader([]byte(test.input))
|
||||||
|
_, err := r.Seek(test.start, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
name, err := readName(r)
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if err != test.err {
|
||||||
|
t.Errorf("%+q returned (%+q, %v), expected %v", test.input, name, err, test.err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustParseName(s string) Name {
|
||||||
|
name, err := ParseName(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func questionsEqual(a, b *Question) bool {
|
||||||
|
if !namesEqual(a.Name, b.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.Type != b.Type || a.Class != b.Class {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func rrsEqual(a, b *RR) bool {
|
||||||
|
if !namesEqual(a.Name, b.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.Type != b.Type || a.Class != b.Class || a.TTL != b.TTL {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !bytes.Equal(a.Data, b.Data) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func messagesEqual(a, b *Message) bool {
|
||||||
|
if a.ID != b.ID || a.Flags != b.Flags {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(a.Question) != len(b.Question) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < len(a.Question); i++ {
|
||||||
|
if !questionsEqual(&a.Question[i], &b.Question[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, rec := range []struct{ rrA, rrB []RR }{
|
||||||
|
{a.Answer, b.Answer},
|
||||||
|
{a.Authority, b.Authority},
|
||||||
|
{a.Additional, b.Additional},
|
||||||
|
} {
|
||||||
|
if len(rec.rrA) != len(rec.rrB) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < len(rec.rrA); i++ {
|
||||||
|
if !rrsEqual(&rec.rrA[i], &rec.rrB[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMessageFromWireFormat(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
buf string
|
||||||
|
expected Message
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"\x12\x34",
|
||||||
|
Message{},
|
||||||
|
io.ErrUnexpectedEOF,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01",
|
||||||
|
Message{
|
||||||
|
ID: 0x1234,
|
||||||
|
Flags: 0x0100,
|
||||||
|
Question: []Question{
|
||||||
|
{
|
||||||
|
Name: mustParseName("www.example.com"),
|
||||||
|
Type: 1,
|
||||||
|
Class: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Answer: []RR{},
|
||||||
|
Authority: []RR{},
|
||||||
|
Additional: []RR{},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01X",
|
||||||
|
Message{},
|
||||||
|
ErrTrailingBytes,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"\x12\x34\x81\x80\x00\x01\x00\x01\x00\x00\x00\x00\x03www\x07example\x03com\x00\x00\x01\x00\x01\x03www\x07example\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x80\x00\x04\xc0\x00\x02\x01",
|
||||||
|
Message{
|
||||||
|
ID: 0x1234,
|
||||||
|
Flags: 0x8180,
|
||||||
|
Question: []Question{
|
||||||
|
{
|
||||||
|
Name: mustParseName("www.example.com"),
|
||||||
|
Type: 1,
|
||||||
|
Class: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Answer: []RR{
|
||||||
|
{
|
||||||
|
Name: mustParseName("www.example.com"),
|
||||||
|
Type: 1,
|
||||||
|
Class: 1,
|
||||||
|
TTL: 128,
|
||||||
|
Data: []byte{192, 0, 2, 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Authority: []RR{},
|
||||||
|
Additional: []RR{},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
message, err := MessageFromWireFormat([]byte(test.buf))
|
||||||
|
if err != test.err || (err == nil && !messagesEqual(&message, &test.expected)) {
|
||||||
|
t.Errorf("%+q\nreturned (%+v, %v)\nexpected (%+v, %v)",
|
||||||
|
test.buf, message, err, test.expected, test.err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMessageWireFormatRoundTrip(t *testing.T) {
|
||||||
|
for _, message := range []Message{
|
||||||
|
{
|
||||||
|
ID: 0x1234,
|
||||||
|
Flags: 0x0100,
|
||||||
|
Question: []Question{
|
||||||
|
{
|
||||||
|
Name: mustParseName("www.example.com"),
|
||||||
|
Type: 1,
|
||||||
|
Class: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: mustParseName("www2.example.com"),
|
||||||
|
Type: 2,
|
||||||
|
Class: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Answer: []RR{
|
||||||
|
{
|
||||||
|
Name: mustParseName("abc"),
|
||||||
|
Type: 2,
|
||||||
|
Class: 3,
|
||||||
|
TTL: 0xffffffff,
|
||||||
|
Data: []byte{1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: mustParseName("xyz"),
|
||||||
|
Type: 2,
|
||||||
|
Class: 3,
|
||||||
|
TTL: 255,
|
||||||
|
Data: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Authority: []RR{
|
||||||
|
{
|
||||||
|
Name: mustParseName("."),
|
||||||
|
Type: 65535,
|
||||||
|
Class: 65535,
|
||||||
|
TTL: 0,
|
||||||
|
Data: []byte("XXXXXXXXXXXXXXXXXXX"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Additional: []RR{},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
buf, err := message.WireFormat()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%+v cannot make wire format: %v", message, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
message2, err := MessageFromWireFormat(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%+q cannot parse wire format: %v", buf, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !messagesEqual(&message, &message2) {
|
||||||
|
t.Errorf("messages unequal\nbefore: %+v\n after: %+v", message, message2)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeRDataTXT(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
p []byte
|
||||||
|
decoded []byte
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{[]byte{}, nil, io.ErrUnexpectedEOF},
|
||||||
|
{[]byte("\x00"), []byte{}, nil},
|
||||||
|
{[]byte("\x01"), nil, io.ErrUnexpectedEOF},
|
||||||
|
} {
|
||||||
|
decoded, err := DecodeRDataTXT(test.p)
|
||||||
|
if err != test.err || (err == nil && !bytes.Equal(decoded, test.decoded)) {
|
||||||
|
t.Errorf("%+q\nreturned (%+q, %v)\nexpected (%+q, %v)",
|
||||||
|
test.p, decoded, err, test.decoded, test.err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeRDataTXT(t *testing.T) {
|
||||||
|
// Encoding 0 bytes needs to return at least a single length octet of
|
||||||
|
// zero, not an empty slice.
|
||||||
|
p := make([]byte, 0)
|
||||||
|
encoded := EncodeRDataTXT(p)
|
||||||
|
if len(encoded) < 0 {
|
||||||
|
t.Errorf("EncodeRDataTXT(%v) returned %v", p, encoded)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 255 bytes should be able to be encoded into 256 bytes.
|
||||||
|
p = make([]byte, 255)
|
||||||
|
encoded = EncodeRDataTXT(p)
|
||||||
|
if len(encoded) > 256 {
|
||||||
|
t.Errorf("EncodeRDataTXT(%d bytes) returned %d bytes", len(p), len(encoded))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRDataTXTRoundTrip(t *testing.T) {
|
||||||
|
for _, p := range [][]byte{
|
||||||
|
{},
|
||||||
|
[]byte("\x00"),
|
||||||
|
{
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||||
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||||
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
||||||
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
||||||
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
||||||
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
||||||
|
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
||||||
|
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
||||||
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
||||||
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
|
||||||
|
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
||||||
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
|
||||||
|
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
|
||||||
|
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
|
||||||
|
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
||||||
|
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
rdata := EncodeRDataTXT(p)
|
||||||
|
decoded, err := DecodeRDataTXT(rdata)
|
||||||
|
if err != nil || !bytes.Equal(decoded, p) {
|
||||||
|
t.Errorf("%+q returned (%+q, %v)", p, decoded, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,567 @@
|
|||||||
|
package xdns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
idleTimeout = 2 * time.Minute
|
||||||
|
responseTTL = 60
|
||||||
|
maxResponseDelay = 1 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
maxUDPPayload = 1280 - 40 - 8
|
||||||
|
maxEncodedPayload = computeMaxEncodedPayload(maxUDPPayload)
|
||||||
|
)
|
||||||
|
|
||||||
|
func clientIDToAddr(clientID [8]byte) *net.UDPAddr {
|
||||||
|
ip := make(net.IP, 16)
|
||||||
|
|
||||||
|
copy(ip, []byte{0xfd, 0x00, 0, 0, 0, 0, 0, 0})
|
||||||
|
copy(ip[8:], clientID[:])
|
||||||
|
|
||||||
|
return &net.UDPAddr{
|
||||||
|
IP: ip,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type record struct {
|
||||||
|
Resp *Message
|
||||||
|
Addr net.Addr
|
||||||
|
// ClientID [8]byte
|
||||||
|
ClientAddr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
type queue struct {
|
||||||
|
lash time.Time
|
||||||
|
queue chan []byte
|
||||||
|
stash chan []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type xdnsConnServer struct {
|
||||||
|
conn net.PacketConn
|
||||||
|
|
||||||
|
domain Name
|
||||||
|
|
||||||
|
ch chan *record
|
||||||
|
readQueue chan *packet
|
||||||
|
writeQueueMap map[string]*queue
|
||||||
|
|
||||||
|
closed bool
|
||||||
|
mutex sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnServer(c *Config, raw net.PacketConn, end bool) (net.PacketConn, error) {
|
||||||
|
if !end {
|
||||||
|
return nil, errors.New("xdns requires being at the outermost level")
|
||||||
|
}
|
||||||
|
|
||||||
|
domain, err := ParseName(c.Domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := &xdnsConnServer{
|
||||||
|
conn: raw,
|
||||||
|
|
||||||
|
domain: domain,
|
||||||
|
|
||||||
|
ch: make(chan *record, 100),
|
||||||
|
readQueue: make(chan *packet, 128),
|
||||||
|
writeQueueMap: make(map[string]*queue),
|
||||||
|
}
|
||||||
|
|
||||||
|
go conn.clean()
|
||||||
|
go conn.recvLoop()
|
||||||
|
go conn.sendLoop()
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) clean() {
|
||||||
|
f := func() bool {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
if c.closed {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for key, q := range c.writeQueueMap {
|
||||||
|
if now.Sub(q.lash) >= idleTimeout {
|
||||||
|
close(q.queue)
|
||||||
|
close(q.stash)
|
||||||
|
delete(c.writeQueueMap, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
time.Sleep(idleTimeout / 2)
|
||||||
|
if f() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) ensureQueue(addr net.Addr) *queue {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
if c.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
q, ok := c.writeQueueMap[addr.String()]
|
||||||
|
if !ok {
|
||||||
|
q = &queue{
|
||||||
|
queue: make(chan []byte, 128),
|
||||||
|
stash: make(chan []byte, 1),
|
||||||
|
}
|
||||||
|
c.writeQueueMap[addr.String()] = q
|
||||||
|
}
|
||||||
|
q.lash = time.Now()
|
||||||
|
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) stash(queue *queue, p []byte) {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
if c.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case queue.stash <- p:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) recvLoop() {
|
||||||
|
for {
|
||||||
|
if c.closed {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf [4096]byte
|
||||||
|
n, addr, err := c.conn.ReadFrom(buf[:])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
query, err := MessageFromWireFormat(buf[:n])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, payload := responseFor(&query, c.domain)
|
||||||
|
|
||||||
|
var clientID [8]byte
|
||||||
|
n = copy(clientID[:], payload)
|
||||||
|
payload = payload[n:]
|
||||||
|
if n == len(clientID) {
|
||||||
|
r := bytes.NewReader(payload)
|
||||||
|
for {
|
||||||
|
p, err := nextPacketServer(r)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, len(p))
|
||||||
|
copy(buf, p)
|
||||||
|
select {
|
||||||
|
case c.readQueue <- &packet{
|
||||||
|
p: buf,
|
||||||
|
addr: clientIDToAddr(clientID),
|
||||||
|
}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if resp != nil && resp.Rcode() == RcodeNoError {
|
||||||
|
resp.Flags |= RcodeNameError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != nil {
|
||||||
|
select {
|
||||||
|
case c.ch <- &record{resp, addr, clientIDToAddr(clientID)}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(c.ch)
|
||||||
|
close(c.readQueue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) sendLoop() {
|
||||||
|
var nextRec *record
|
||||||
|
for {
|
||||||
|
rec := nextRec
|
||||||
|
nextRec = nil
|
||||||
|
|
||||||
|
if rec == nil {
|
||||||
|
var ok bool
|
||||||
|
rec, ok = <-c.ch
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rec.Resp.Rcode() == RcodeNoError && len(rec.Resp.Question) == 1 {
|
||||||
|
rec.Resp.Answer = []RR{
|
||||||
|
{
|
||||||
|
Name: rec.Resp.Question[0].Name,
|
||||||
|
Type: rec.Resp.Question[0].Type,
|
||||||
|
Class: rec.Resp.Question[0].Class,
|
||||||
|
TTL: responseTTL,
|
||||||
|
Data: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload bytes.Buffer
|
||||||
|
limit := maxEncodedPayload
|
||||||
|
timer := time.NewTimer(maxResponseDelay)
|
||||||
|
for {
|
||||||
|
queue := c.ensureQueue(rec.ClientAddr)
|
||||||
|
if queue == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var p []byte
|
||||||
|
|
||||||
|
select {
|
||||||
|
case p = <-queue.stash:
|
||||||
|
default:
|
||||||
|
select {
|
||||||
|
case p = <-queue.stash:
|
||||||
|
case p = <-queue.queue:
|
||||||
|
default:
|
||||||
|
select {
|
||||||
|
case p = <-queue.stash:
|
||||||
|
case p = <-queue.queue:
|
||||||
|
case <-timer.C:
|
||||||
|
case nextRec = <-c.ch:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.Reset(0)
|
||||||
|
|
||||||
|
if len(p) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
limit -= 2 + len(p)
|
||||||
|
if payload.Len() == 0 {
|
||||||
|
|
||||||
|
} else if limit < 0 {
|
||||||
|
c.stash(queue, p)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(uint16(len(p))) != len(p) {
|
||||||
|
panic(len(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = binary.Write(&payload, binary.BigEndian, uint16(len(p)))
|
||||||
|
payload.Write(p)
|
||||||
|
}
|
||||||
|
timer.Stop()
|
||||||
|
|
||||||
|
rec.Resp.Answer[0].Data = EncodeRDataTXT(payload.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := rec.Resp.WireFormat()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf) > maxUDPPayload {
|
||||||
|
errors.LogDebug(context.Background(), "xdns server truncate ", len(buf))
|
||||||
|
buf = buf[:maxUDPPayload]
|
||||||
|
buf[2] |= 0x02
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = c.conn.WriteTo(buf, rec.Addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) Size() int32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||||
|
packet, ok := <-c.readQueue
|
||||||
|
if !ok {
|
||||||
|
return 0, nil, io.EOF
|
||||||
|
}
|
||||||
|
n = copy(p, packet.p)
|
||||||
|
if n != len(packet.p) {
|
||||||
|
return 0, nil, io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
return n, packet.addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||||
|
q := c.ensureQueue(addr)
|
||||||
|
if q == nil {
|
||||||
|
return 0, errors.New("xdns closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
if c.closed {
|
||||||
|
return 0, errors.New("xdns closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, len(p))
|
||||||
|
copy(buf, p)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case q.queue <- buf:
|
||||||
|
return len(p), nil
|
||||||
|
default:
|
||||||
|
return 0, errors.New("xdns queue full")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) Close() error {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
|
||||||
|
if c.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.closed = true
|
||||||
|
for key, q := range c.writeQueueMap {
|
||||||
|
close(q.queue)
|
||||||
|
close(q.stash)
|
||||||
|
delete(c.writeQueueMap, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) LocalAddr() net.Addr {
|
||||||
|
return c.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) SetDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) SetReadDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *xdnsConnServer) SetWriteDeadline(t time.Time) error {
|
||||||
|
return c.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextPacketServer(r *bytes.Reader) ([]byte, error) {
|
||||||
|
eof := func(err error) error {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
prefix, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if prefix >= 224 {
|
||||||
|
paddingLen := prefix - 224
|
||||||
|
_, err := io.CopyN(io.Discard, r, int64(paddingLen))
|
||||||
|
if err != nil {
|
||||||
|
return nil, eof(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p := make([]byte, int(prefix))
|
||||||
|
_, err = io.ReadFull(r, p)
|
||||||
|
return p, eof(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseFor(query *Message, domain Name) (*Message, []byte) {
|
||||||
|
resp := &Message{
|
||||||
|
ID: query.ID,
|
||||||
|
Flags: 0x8000,
|
||||||
|
Question: query.Question,
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.Flags&0x8000 != 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadSize := 0
|
||||||
|
for _, rr := range query.Additional {
|
||||||
|
if rr.Type != RRTypeOPT {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(resp.Additional) != 0 {
|
||||||
|
resp.Flags |= RcodeFormatError
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
resp.Additional = append(resp.Additional, RR{
|
||||||
|
Name: Name{},
|
||||||
|
Type: RRTypeOPT,
|
||||||
|
Class: 4096,
|
||||||
|
TTL: 0,
|
||||||
|
Data: []byte{},
|
||||||
|
})
|
||||||
|
additional := &resp.Additional[0]
|
||||||
|
|
||||||
|
version := (rr.TTL >> 16) & 0xff
|
||||||
|
if version != 0 {
|
||||||
|
resp.Flags |= ExtendedRcodeBadVers & 0xf
|
||||||
|
additional.TTL = (ExtendedRcodeBadVers >> 4) << 24
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadSize = int(rr.Class)
|
||||||
|
}
|
||||||
|
if payloadSize < 512 {
|
||||||
|
payloadSize = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(query.Question) != 1 {
|
||||||
|
resp.Flags |= RcodeFormatError
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
question := query.Question[0]
|
||||||
|
|
||||||
|
prefix, ok := question.Name.TrimSuffix(domain)
|
||||||
|
if !ok {
|
||||||
|
resp.Flags |= RcodeNameError
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
resp.Flags |= 0x0400
|
||||||
|
|
||||||
|
if query.Opcode() != 0 {
|
||||||
|
resp.Flags |= RcodeNotImplemented
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if question.Type != RRTypeTXT {
|
||||||
|
resp.Flags |= RcodeNameError
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
encoded := bytes.ToUpper(bytes.Join(prefix, nil))
|
||||||
|
payload := make([]byte, base32Encoding.DecodedLen(len(encoded)))
|
||||||
|
n, err := base32Encoding.Decode(payload, encoded)
|
||||||
|
if err != nil {
|
||||||
|
resp.Flags |= RcodeNameError
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
payload = payload[:n]
|
||||||
|
|
||||||
|
if payloadSize < maxUDPPayload {
|
||||||
|
resp.Flags |= RcodeFormatError
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeMaxEncodedPayload(limit int) int {
|
||||||
|
maxLengthName, err := NewName([][]byte{
|
||||||
|
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
||||||
|
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
||||||
|
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
||||||
|
[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
n := 0
|
||||||
|
for _, label := range maxLengthName {
|
||||||
|
n += len(label) + 1
|
||||||
|
}
|
||||||
|
n += 1
|
||||||
|
if n != 255 {
|
||||||
|
panic("computeMaxEncodedPayload n != 255")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queryLimit := uint16(limit)
|
||||||
|
if int(queryLimit) != limit {
|
||||||
|
queryLimit = 0xffff
|
||||||
|
}
|
||||||
|
query := &Message{
|
||||||
|
Question: []Question{
|
||||||
|
{
|
||||||
|
Name: maxLengthName,
|
||||||
|
Type: RRTypeTXT,
|
||||||
|
Class: RRTypeTXT,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Additional: []RR{
|
||||||
|
{
|
||||||
|
Name: Name{},
|
||||||
|
Type: RRTypeOPT,
|
||||||
|
Class: queryLimit,
|
||||||
|
TTL: 0,
|
||||||
|
Data: []byte{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp, _ := responseFor(query, [][]byte{})
|
||||||
|
|
||||||
|
resp.Answer = []RR{
|
||||||
|
{
|
||||||
|
Name: query.Question[0].Name,
|
||||||
|
Type: query.Question[0].Type,
|
||||||
|
Class: query.Question[0].Class,
|
||||||
|
TTL: responseTTL,
|
||||||
|
Data: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
low := 0
|
||||||
|
high := 32768
|
||||||
|
for low+1 < high {
|
||||||
|
mid := (low + high) / 2
|
||||||
|
resp.Answer[0].Data = EncodeRDataTXT(make([]byte, mid))
|
||||||
|
buf, err := resp.WireFormat()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if len(buf) <= limit {
|
||||||
|
low = mid
|
||||||
|
} else {
|
||||||
|
high = mid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return low
|
||||||
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package internet_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
. "github.com/xtls/xray-core/transport/internet"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/noop"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/srtp"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/utp"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/wechat"
|
|
||||||
"github.com/xtls/xray-core/transport/internet/headers/wireguard"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAllHeadersLoadable(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
Input interface{}
|
|
||||||
Size int32
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Input: new(noop.Config),
|
|
||||||
Size: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Input: new(srtp.Config),
|
|
||||||
Size: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Input: new(utp.Config),
|
|
||||||
Size: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Input: new(wechat.VideoConfig),
|
|
||||||
Size: 13,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Input: new(wireguard.WireguardConfig),
|
|
||||||
Size: 4,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
|
||||||
header, err := CreatePacketHeader(testCase.Input)
|
|
||||||
common.Must(err)
|
|
||||||
if header.Size() != testCase.Size {
|
|
||||||
t.Error("expected size ", testCase.Size, " but got ", header.Size())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.35.1
|
|
||||||
// protoc v5.28.2
|
|
||||||
// source: transport/internet/headers/dns/config.proto
|
|
||||||
|
|
||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
|
||||||
*x = Config{}
|
|
||||||
mi := &file_transport_internet_headers_dns_config_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Config) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_transport_internet_headers_dns_config_proto_msgTypes[0]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Config) Descriptor() ([]byte, []int) {
|
|
||||||
return file_transport_internet_headers_dns_config_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetDomain() string {
|
|
||||||
if x != nil {
|
|
||||||
return x.Domain
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_transport_internet_headers_dns_config_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_transport_internet_headers_dns_config_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x2b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x64, 0x6e, 0x73,
|
|
||||||
0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x78,
|
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
|
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x64,
|
|
||||||
0x6e, 0x73, 0x22, 0x20, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06,
|
|
||||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f,
|
|
||||||
0x6d, 0x61, 0x69, 0x6e, 0x42, 0x8b, 0x01, 0x0a, 0x27, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
|
|
||||||
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x64, 0x6e, 0x73,
|
|
||||||
0x50, 0x01, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78,
|
|
||||||
0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72,
|
|
||||||
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
|
||||||
0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x23, 0x58,
|
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
|
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x44,
|
|
||||||
0x4e, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_transport_internet_headers_dns_config_proto_rawDescOnce sync.Once
|
|
||||||
file_transport_internet_headers_dns_config_proto_rawDescData = file_transport_internet_headers_dns_config_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_transport_internet_headers_dns_config_proto_rawDescGZIP() []byte {
|
|
||||||
file_transport_internet_headers_dns_config_proto_rawDescOnce.Do(func() {
|
|
||||||
file_transport_internet_headers_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_dns_config_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_transport_internet_headers_dns_config_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_transport_internet_headers_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
|
||||||
var file_transport_internet_headers_dns_config_proto_goTypes = []any{
|
|
||||||
(*Config)(nil), // 0: xray.transport.internet.headers.dns.Config
|
|
||||||
}
|
|
||||||
var file_transport_internet_headers_dns_config_proto_depIdxs = []int32{
|
|
||||||
0, // [0:0] is the sub-list for method output_type
|
|
||||||
0, // [0:0] is the sub-list for method input_type
|
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
|
||||||
0, // [0:0] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_transport_internet_headers_dns_config_proto_init() }
|
|
||||||
func file_transport_internet_headers_dns_config_proto_init() {
|
|
||||||
if File_transport_internet_headers_dns_config_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_transport_internet_headers_dns_config_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 1,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 0,
|
|
||||||
},
|
|
||||||
GoTypes: file_transport_internet_headers_dns_config_proto_goTypes,
|
|
||||||
DependencyIndexes: file_transport_internet_headers_dns_config_proto_depIdxs,
|
|
||||||
MessageInfos: file_transport_internet_headers_dns_config_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_transport_internet_headers_dns_config_proto = out.File
|
|
||||||
file_transport_internet_headers_dns_config_proto_rawDesc = nil
|
|
||||||
file_transport_internet_headers_dns_config_proto_goTypes = nil
|
|
||||||
file_transport_internet_headers_dns_config_proto_depIdxs = nil
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package xray.transport.internet.headers.dns;
|
|
||||||
option csharp_namespace = "Xray.Transport.Internet.Headers.DNS";
|
|
||||||
option go_package = "github.com/xtls/xray-core/transport/internet/headers/dns";
|
|
||||||
option java_package = "com.xray.transport.internet.headers.dns";
|
|
||||||
option java_multiple_files = true;
|
|
||||||
|
|
||||||
message Config {
|
|
||||||
string domain = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/dice"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DNS struct {
|
|
||||||
header []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d DNS) Size() int32 {
|
|
||||||
return int32(len(d.header))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize implements PacketHeader.
|
|
||||||
func (d DNS) Serialize(b []byte) {
|
|
||||||
copy(b, d.header)
|
|
||||||
binary.BigEndian.PutUint16(b[0:], dice.RollUint16()) // random transaction ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDNS returns a new DNS instance based on given config.
|
|
||||||
func NewDNS(ctx context.Context, config interface{}) (interface{}, error) {
|
|
||||||
var header []byte
|
|
||||||
|
|
||||||
header = binary.BigEndian.AppendUint16(header, 0x0000) // Transaction ID
|
|
||||||
header = binary.BigEndian.AppendUint16(header, 0x0100) // Flags: Standard query
|
|
||||||
header = binary.BigEndian.AppendUint16(header, 0x0001) // Questions
|
|
||||||
header = binary.BigEndian.AppendUint16(header, 0x0000) // Answer RRs
|
|
||||||
header = binary.BigEndian.AppendUint16(header, 0x0000) // Authority RRs
|
|
||||||
header = binary.BigEndian.AppendUint16(header, 0x0000) // Additional RRs
|
|
||||||
|
|
||||||
buf := make([]byte, 0x100)
|
|
||||||
|
|
||||||
off1, err := packDomainName(config.(*Config).Domain+".", buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
header = append(header, buf[:off1]...)
|
|
||||||
|
|
||||||
header = binary.BigEndian.AppendUint16(header, 0x0001) // Type: A
|
|
||||||
header = binary.BigEndian.AppendUint16(header, 0x0001) // Class: IN
|
|
||||||
|
|
||||||
return DNS{
|
|
||||||
header: header,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// copied from github.com/miekg/dns
|
|
||||||
func packDomainName(s string, msg []byte) (off1 int, err error) {
|
|
||||||
off := 0
|
|
||||||
ls := len(s)
|
|
||||||
// Each dot ends a segment of the name.
|
|
||||||
// We trade each dot byte for a length byte.
|
|
||||||
// Except for escaped dots (\.), which are normal dots.
|
|
||||||
// There is also a trailing zero.
|
|
||||||
|
|
||||||
// Emit sequence of counted strings, chopping at dots.
|
|
||||||
var (
|
|
||||||
begin int
|
|
||||||
bs []byte
|
|
||||||
)
|
|
||||||
for i := 0; i < ls; i++ {
|
|
||||||
var c byte
|
|
||||||
if bs == nil {
|
|
||||||
c = s[i]
|
|
||||||
} else {
|
|
||||||
c = bs[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
switch c {
|
|
||||||
case '\\':
|
|
||||||
if off+1 > len(msg) {
|
|
||||||
return len(msg), errors.New("buffer size too small")
|
|
||||||
}
|
|
||||||
|
|
||||||
if bs == nil {
|
|
||||||
bs = []byte(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(bs[i:ls-1], bs[i+1:])
|
|
||||||
ls--
|
|
||||||
case '.':
|
|
||||||
labelLen := i - begin
|
|
||||||
if labelLen >= 1<<6 { // top two bits of length must be clear
|
|
||||||
return len(msg), errors.New("bad rdata")
|
|
||||||
}
|
|
||||||
|
|
||||||
// off can already (we're in a loop) be bigger than len(msg)
|
|
||||||
// this happens when a name isn't fully qualified
|
|
||||||
if off+1+labelLen > len(msg) {
|
|
||||||
return len(msg), errors.New("buffer size too small")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following is covered by the length check above.
|
|
||||||
msg[off] = byte(labelLen)
|
|
||||||
|
|
||||||
if bs == nil {
|
|
||||||
copy(msg[off+1:], s[begin:i])
|
|
||||||
} else {
|
|
||||||
copy(msg[off+1:], bs[begin:i])
|
|
||||||
}
|
|
||||||
off += 1 + labelLen
|
|
||||||
begin = i + 1
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if off < len(msg) {
|
|
||||||
msg[off] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return off + 1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
common.Must(common.RegisterConfig((*Config)(nil), NewDNS))
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.35.1
|
|
||||||
// protoc v5.28.2
|
|
||||||
// source: transport/internet/headers/srtp/config.proto
|
|
||||||
|
|
||||||
package srtp
|
|
||||||
|
|
||||||
import (
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
|
|
||||||
Padding bool `protobuf:"varint,2,opt,name=padding,proto3" json:"padding,omitempty"`
|
|
||||||
Extension bool `protobuf:"varint,3,opt,name=extension,proto3" json:"extension,omitempty"`
|
|
||||||
CsrcCount uint32 `protobuf:"varint,4,opt,name=csrc_count,json=csrcCount,proto3" json:"csrc_count,omitempty"`
|
|
||||||
Marker bool `protobuf:"varint,5,opt,name=marker,proto3" json:"marker,omitempty"`
|
|
||||||
PayloadType uint32 `protobuf:"varint,6,opt,name=payload_type,json=payloadType,proto3" json:"payload_type,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
|
||||||
*x = Config{}
|
|
||||||
mi := &file_transport_internet_headers_srtp_config_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Config) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_transport_internet_headers_srtp_config_proto_msgTypes[0]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Config) Descriptor() ([]byte, []int) {
|
|
||||||
return file_transport_internet_headers_srtp_config_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetVersion() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Version
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetPadding() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.Padding
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetExtension() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.Extension
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetCsrcCount() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.CsrcCount
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetMarker() bool {
|
|
||||||
if x != nil {
|
|
||||||
return x.Marker
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetPayloadType() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.PayloadType
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_transport_internet_headers_srtp_config_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_transport_internet_headers_srtp_config_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x2c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x73, 0x72, 0x74,
|
|
||||||
0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x24,
|
|
||||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
|
|
||||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e,
|
|
||||||
0x73, 0x72, 0x74, 0x70, 0x22, 0xb4, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
|
|
||||||
0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
|
|
||||||
0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64,
|
|
||||||
0x64, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64,
|
|
||||||
0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
|
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
|
|
||||||
0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x73, 0x72, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
|
||||||
0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x73, 0x72, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74,
|
|
||||||
0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
|
|
||||||
0x52, 0x06, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6c,
|
|
||||||
0x6f, 0x61, 0x64, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b,
|
|
||||||
0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x42, 0x8e, 0x01, 0x0a, 0x28,
|
|
||||||
0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
|
|
||||||
0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64,
|
|
||||||
0x65, 0x72, 0x73, 0x2e, 0x73, 0x72, 0x74, 0x70, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68,
|
|
||||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
|
||||||
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
|
|
||||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
|
|
||||||
0x2f, 0x73, 0x72, 0x74, 0x70, 0xaa, 0x02, 0x24, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61,
|
|
||||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
|
||||||
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x53, 0x72, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72,
|
|
||||||
0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_transport_internet_headers_srtp_config_proto_rawDescOnce sync.Once
|
|
||||||
file_transport_internet_headers_srtp_config_proto_rawDescData = file_transport_internet_headers_srtp_config_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_transport_internet_headers_srtp_config_proto_rawDescGZIP() []byte {
|
|
||||||
file_transport_internet_headers_srtp_config_proto_rawDescOnce.Do(func() {
|
|
||||||
file_transport_internet_headers_srtp_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_srtp_config_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_transport_internet_headers_srtp_config_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_transport_internet_headers_srtp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
|
||||||
var file_transport_internet_headers_srtp_config_proto_goTypes = []any{
|
|
||||||
(*Config)(nil), // 0: xray.transport.internet.headers.srtp.Config
|
|
||||||
}
|
|
||||||
var file_transport_internet_headers_srtp_config_proto_depIdxs = []int32{
|
|
||||||
0, // [0:0] is the sub-list for method output_type
|
|
||||||
0, // [0:0] is the sub-list for method input_type
|
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
|
||||||
0, // [0:0] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_transport_internet_headers_srtp_config_proto_init() }
|
|
||||||
func file_transport_internet_headers_srtp_config_proto_init() {
|
|
||||||
if File_transport_internet_headers_srtp_config_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_transport_internet_headers_srtp_config_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 1,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 0,
|
|
||||||
},
|
|
||||||
GoTypes: file_transport_internet_headers_srtp_config_proto_goTypes,
|
|
||||||
DependencyIndexes: file_transport_internet_headers_srtp_config_proto_depIdxs,
|
|
||||||
MessageInfos: file_transport_internet_headers_srtp_config_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_transport_internet_headers_srtp_config_proto = out.File
|
|
||||||
file_transport_internet_headers_srtp_config_proto_rawDesc = nil
|
|
||||||
file_transport_internet_headers_srtp_config_proto_goTypes = nil
|
|
||||||
file_transport_internet_headers_srtp_config_proto_depIdxs = nil
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package xray.transport.internet.headers.srtp;
|
|
||||||
option csharp_namespace = "Xray.Transport.Internet.Headers.Srtp";
|
|
||||||
option go_package = "github.com/xtls/xray-core/transport/internet/headers/srtp";
|
|
||||||
option java_package = "com.xray.transport.internet.headers.srtp";
|
|
||||||
option java_multiple_files = true;
|
|
||||||
|
|
||||||
message Config {
|
|
||||||
uint32 version = 1;
|
|
||||||
bool padding = 2;
|
|
||||||
bool extension = 3;
|
|
||||||
uint32 csrc_count = 4;
|
|
||||||
bool marker = 5;
|
|
||||||
uint32 payload_type = 6;
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package srtp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/binary"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/dice"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SRTP struct {
|
|
||||||
header uint16
|
|
||||||
number uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*SRTP) Size() int32 {
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize implements PacketHeader.
|
|
||||||
func (s *SRTP) Serialize(b []byte) {
|
|
||||||
s.number++
|
|
||||||
binary.BigEndian.PutUint16(b, s.header)
|
|
||||||
binary.BigEndian.PutUint16(b[2:], s.number)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a new SRTP instance based on the given config.
|
|
||||||
func New(ctx context.Context, config interface{}) (interface{}, error) {
|
|
||||||
return &SRTP{
|
|
||||||
header: 0xB5E8,
|
|
||||||
number: dice.RollUint16(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
common.Must(common.RegisterConfig((*Config)(nil), New))
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package srtp_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
|
||||||
. "github.com/xtls/xray-core/transport/internet/headers/srtp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSRTPWrite(t *testing.T) {
|
|
||||||
content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
|
|
||||||
srtpRaw, err := New(context.Background(), &Config{})
|
|
||||||
common.Must(err)
|
|
||||||
|
|
||||||
srtp := srtpRaw.(*SRTP)
|
|
||||||
|
|
||||||
payload := buf.New()
|
|
||||||
srtp.Serialize(payload.Extend(srtp.Size()))
|
|
||||||
payload.Write(content)
|
|
||||||
|
|
||||||
expectedLen := int32(len(content)) + srtp.Size()
|
|
||||||
if payload.Len() != expectedLen {
|
|
||||||
t.Error("expected ", expectedLen, " of bytes, but got ", payload.Len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.35.1
|
|
||||||
// protoc v5.28.2
|
|
||||||
// source: transport/internet/headers/tls/config.proto
|
|
||||||
|
|
||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
type PacketConfig struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PacketConfig) Reset() {
|
|
||||||
*x = PacketConfig{}
|
|
||||||
mi := &file_transport_internet_headers_tls_config_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *PacketConfig) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*PacketConfig) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *PacketConfig) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_transport_internet_headers_tls_config_proto_msgTypes[0]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use PacketConfig.ProtoReflect.Descriptor instead.
|
|
||||||
func (*PacketConfig) Descriptor() ([]byte, []int) {
|
|
||||||
return file_transport_internet_headers_tls_config_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_transport_internet_headers_tls_config_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_transport_internet_headers_tls_config_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x2b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x74, 0x6c, 0x73,
|
|
||||||
0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x78,
|
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
|
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x74,
|
|
||||||
0x6c, 0x73, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
|
||||||
0x69, 0x67, 0x42, 0x8b, 0x01, 0x0a, 0x27, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
|
||||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
|
||||||
0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01,
|
|
||||||
0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
|
|
||||||
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e,
|
|
||||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68,
|
|
||||||
0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x74, 0x6c, 0x73, 0xaa, 0x02, 0x23, 0x58, 0x72, 0x61,
|
|
||||||
0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65,
|
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x54, 0x6c, 0x73,
|
|
||||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_transport_internet_headers_tls_config_proto_rawDescOnce sync.Once
|
|
||||||
file_transport_internet_headers_tls_config_proto_rawDescData = file_transport_internet_headers_tls_config_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_transport_internet_headers_tls_config_proto_rawDescGZIP() []byte {
|
|
||||||
file_transport_internet_headers_tls_config_proto_rawDescOnce.Do(func() {
|
|
||||||
file_transport_internet_headers_tls_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_tls_config_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_transport_internet_headers_tls_config_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_transport_internet_headers_tls_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
|
||||||
var file_transport_internet_headers_tls_config_proto_goTypes = []any{
|
|
||||||
(*PacketConfig)(nil), // 0: xray.transport.internet.headers.tls.PacketConfig
|
|
||||||
}
|
|
||||||
var file_transport_internet_headers_tls_config_proto_depIdxs = []int32{
|
|
||||||
0, // [0:0] is the sub-list for method output_type
|
|
||||||
0, // [0:0] is the sub-list for method input_type
|
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
|
||||||
0, // [0:0] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_transport_internet_headers_tls_config_proto_init() }
|
|
||||||
func file_transport_internet_headers_tls_config_proto_init() {
|
|
||||||
if File_transport_internet_headers_tls_config_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_transport_internet_headers_tls_config_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 1,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 0,
|
|
||||||
},
|
|
||||||
GoTypes: file_transport_internet_headers_tls_config_proto_goTypes,
|
|
||||||
DependencyIndexes: file_transport_internet_headers_tls_config_proto_depIdxs,
|
|
||||||
MessageInfos: file_transport_internet_headers_tls_config_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_transport_internet_headers_tls_config_proto = out.File
|
|
||||||
file_transport_internet_headers_tls_config_proto_rawDesc = nil
|
|
||||||
file_transport_internet_headers_tls_config_proto_goTypes = nil
|
|
||||||
file_transport_internet_headers_tls_config_proto_depIdxs = nil
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package xray.transport.internet.headers.tls;
|
|
||||||
option csharp_namespace = "Xray.Transport.Internet.Headers.Tls";
|
|
||||||
option go_package = "github.com/xtls/xray-core/transport/internet/headers/tls";
|
|
||||||
option java_package = "com.xray.transport.internet.headers.tls";
|
|
||||||
option java_multiple_files = true;
|
|
||||||
|
|
||||||
message PacketConfig {}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package tls
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/dice"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DTLS writes header as DTLS. See https://tools.ietf.org/html/rfc6347
|
|
||||||
type DTLS struct {
|
|
||||||
epoch uint16
|
|
||||||
length uint16
|
|
||||||
sequence uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size implements PacketHeader.
|
|
||||||
func (*DTLS) Size() int32 {
|
|
||||||
return 1 + 2 + 2 + 6 + 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize implements PacketHeader.
|
|
||||||
func (d *DTLS) Serialize(b []byte) {
|
|
||||||
b[0] = 23 // application data
|
|
||||||
b[1] = 254
|
|
||||||
b[2] = 253
|
|
||||||
b[3] = byte(d.epoch >> 8)
|
|
||||||
b[4] = byte(d.epoch)
|
|
||||||
b[5] = 0
|
|
||||||
b[6] = 0
|
|
||||||
b[7] = byte(d.sequence >> 24)
|
|
||||||
b[8] = byte(d.sequence >> 16)
|
|
||||||
b[9] = byte(d.sequence >> 8)
|
|
||||||
b[10] = byte(d.sequence)
|
|
||||||
d.sequence++
|
|
||||||
b[11] = byte(d.length >> 8)
|
|
||||||
b[12] = byte(d.length)
|
|
||||||
d.length += 17
|
|
||||||
if d.length > 100 {
|
|
||||||
d.length -= 50
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new UTP header for the given config.
|
|
||||||
func New(ctx context.Context, config interface{}) (interface{}, error) {
|
|
||||||
return &DTLS{
|
|
||||||
epoch: dice.RollUint16(),
|
|
||||||
sequence: 0,
|
|
||||||
length: 17,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
common.Must(common.RegisterConfig((*PacketConfig)(nil), New))
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package tls_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
|
||||||
. "github.com/xtls/xray-core/transport/internet/headers/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDTLSWrite(t *testing.T) {
|
|
||||||
content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
|
|
||||||
dtlsRaw, err := New(context.Background(), &PacketConfig{})
|
|
||||||
common.Must(err)
|
|
||||||
|
|
||||||
dtls := dtlsRaw.(*DTLS)
|
|
||||||
|
|
||||||
payload := buf.New()
|
|
||||||
dtls.Serialize(payload.Extend(dtls.Size()))
|
|
||||||
payload.Write(content)
|
|
||||||
|
|
||||||
if payload.Len() != int32(len(content))+dtls.Size() {
|
|
||||||
t.Error("payload len: ", payload.Len(), " want ", int32(len(content))+dtls.Size())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.35.1
|
|
||||||
// protoc v5.28.2
|
|
||||||
// source: transport/internet/headers/utp/config.proto
|
|
||||||
|
|
||||||
package utp
|
|
||||||
|
|
||||||
import (
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
|
||||||
*x = Config{}
|
|
||||||
mi := &file_transport_internet_headers_utp_config_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Config) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_transport_internet_headers_utp_config_proto_msgTypes[0]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Config) Descriptor() ([]byte, []int) {
|
|
||||||
return file_transport_internet_headers_utp_config_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetVersion() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Version
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_transport_internet_headers_utp_config_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_transport_internet_headers_utp_config_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x2b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x75, 0x74, 0x70,
|
|
||||||
0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x78,
|
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
|
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x75,
|
|
||||||
0x74, 0x70, 0x22, 0x22, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07,
|
|
||||||
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76,
|
|
||||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x8b, 0x01, 0x0a, 0x27, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
|
|
||||||
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
|
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x75,
|
|
||||||
0x74, 0x70, 0x50, 0x01, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
|
||||||
0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f,
|
|
||||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
|
||||||
0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x75, 0x74, 0x70, 0xaa, 0x02,
|
|
||||||
0x23, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
|
|
||||||
0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
|
|
||||||
0x2e, 0x55, 0x74, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_transport_internet_headers_utp_config_proto_rawDescOnce sync.Once
|
|
||||||
file_transport_internet_headers_utp_config_proto_rawDescData = file_transport_internet_headers_utp_config_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_transport_internet_headers_utp_config_proto_rawDescGZIP() []byte {
|
|
||||||
file_transport_internet_headers_utp_config_proto_rawDescOnce.Do(func() {
|
|
||||||
file_transport_internet_headers_utp_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_utp_config_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_transport_internet_headers_utp_config_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_transport_internet_headers_utp_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
|
||||||
var file_transport_internet_headers_utp_config_proto_goTypes = []any{
|
|
||||||
(*Config)(nil), // 0: xray.transport.internet.headers.utp.Config
|
|
||||||
}
|
|
||||||
var file_transport_internet_headers_utp_config_proto_depIdxs = []int32{
|
|
||||||
0, // [0:0] is the sub-list for method output_type
|
|
||||||
0, // [0:0] is the sub-list for method input_type
|
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
|
||||||
0, // [0:0] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_transport_internet_headers_utp_config_proto_init() }
|
|
||||||
func file_transport_internet_headers_utp_config_proto_init() {
|
|
||||||
if File_transport_internet_headers_utp_config_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_transport_internet_headers_utp_config_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 1,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 0,
|
|
||||||
},
|
|
||||||
GoTypes: file_transport_internet_headers_utp_config_proto_goTypes,
|
|
||||||
DependencyIndexes: file_transport_internet_headers_utp_config_proto_depIdxs,
|
|
||||||
MessageInfos: file_transport_internet_headers_utp_config_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_transport_internet_headers_utp_config_proto = out.File
|
|
||||||
file_transport_internet_headers_utp_config_proto_rawDesc = nil
|
|
||||||
file_transport_internet_headers_utp_config_proto_goTypes = nil
|
|
||||||
file_transport_internet_headers_utp_config_proto_depIdxs = nil
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package xray.transport.internet.headers.utp;
|
|
||||||
option csharp_namespace = "Xray.Transport.Internet.Headers.Utp";
|
|
||||||
option go_package = "github.com/xtls/xray-core/transport/internet/headers/utp";
|
|
||||||
option java_package = "com.xray.transport.internet.headers.utp";
|
|
||||||
option java_multiple_files = true;
|
|
||||||
|
|
||||||
message Config {
|
|
||||||
uint32 version = 1;
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package utp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/binary"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/dice"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UTP struct {
|
|
||||||
header byte
|
|
||||||
extension byte
|
|
||||||
connectionID uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*UTP) Size() int32 {
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize implements PacketHeader.
|
|
||||||
func (u *UTP) Serialize(b []byte) {
|
|
||||||
binary.BigEndian.PutUint16(b, u.connectionID)
|
|
||||||
b[2] = u.header
|
|
||||||
b[3] = u.extension
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new UTP header for the given config.
|
|
||||||
func New(ctx context.Context, config interface{}) (interface{}, error) {
|
|
||||||
return &UTP{
|
|
||||||
header: 1,
|
|
||||||
extension: 0,
|
|
||||||
connectionID: dice.RollUint16(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
common.Must(common.RegisterConfig((*Config)(nil), New))
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package utp_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
|
||||||
. "github.com/xtls/xray-core/transport/internet/headers/utp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUTPWrite(t *testing.T) {
|
|
||||||
content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
|
|
||||||
utpRaw, err := New(context.Background(), &Config{})
|
|
||||||
common.Must(err)
|
|
||||||
|
|
||||||
utp := utpRaw.(*UTP)
|
|
||||||
|
|
||||||
payload := buf.New()
|
|
||||||
utp.Serialize(payload.Extend(utp.Size()))
|
|
||||||
payload.Write(content)
|
|
||||||
|
|
||||||
if payload.Len() != int32(len(content))+utp.Size() {
|
|
||||||
t.Error("unexpected payload length: ", payload.Len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.35.1
|
|
||||||
// protoc v5.28.2
|
|
||||||
// source: transport/internet/headers/wechat/config.proto
|
|
||||||
|
|
||||||
package wechat
|
|
||||||
|
|
||||||
import (
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
type VideoConfig struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *VideoConfig) Reset() {
|
|
||||||
*x = VideoConfig{}
|
|
||||||
mi := &file_transport_internet_headers_wechat_config_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *VideoConfig) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*VideoConfig) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *VideoConfig) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_transport_internet_headers_wechat_config_proto_msgTypes[0]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use VideoConfig.ProtoReflect.Descriptor instead.
|
|
||||||
func (*VideoConfig) Descriptor() ([]byte, []int) {
|
|
||||||
return file_transport_internet_headers_wechat_config_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_transport_internet_headers_wechat_config_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_transport_internet_headers_wechat_config_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x77, 0x65, 0x63,
|
|
||||||
0x68, 0x61, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x12, 0x26, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
|
||||||
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
|
|
||||||
0x73, 0x2e, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x22, 0x0d, 0x0a, 0x0b, 0x56, 0x69, 0x64, 0x65,
|
|
||||||
0x6f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x94, 0x01, 0x0a, 0x2a, 0x63, 0x6f, 0x6d, 0x2e,
|
|
||||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
|
|
||||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e,
|
|
||||||
0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x01, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
|
||||||
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
|
|
||||||
0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
|
|
||||||
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x77,
|
|
||||||
0x65, 0x63, 0x68, 0x61, 0x74, 0xaa, 0x02, 0x26, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61,
|
|
||||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
|
||||||
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x57, 0x65, 0x63, 0x68, 0x61, 0x74, 0x62, 0x06,
|
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_transport_internet_headers_wechat_config_proto_rawDescOnce sync.Once
|
|
||||||
file_transport_internet_headers_wechat_config_proto_rawDescData = file_transport_internet_headers_wechat_config_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_transport_internet_headers_wechat_config_proto_rawDescGZIP() []byte {
|
|
||||||
file_transport_internet_headers_wechat_config_proto_rawDescOnce.Do(func() {
|
|
||||||
file_transport_internet_headers_wechat_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_wechat_config_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_transport_internet_headers_wechat_config_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_transport_internet_headers_wechat_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
|
||||||
var file_transport_internet_headers_wechat_config_proto_goTypes = []any{
|
|
||||||
(*VideoConfig)(nil), // 0: xray.transport.internet.headers.wechat.VideoConfig
|
|
||||||
}
|
|
||||||
var file_transport_internet_headers_wechat_config_proto_depIdxs = []int32{
|
|
||||||
0, // [0:0] is the sub-list for method output_type
|
|
||||||
0, // [0:0] is the sub-list for method input_type
|
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
|
||||||
0, // [0:0] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_transport_internet_headers_wechat_config_proto_init() }
|
|
||||||
func file_transport_internet_headers_wechat_config_proto_init() {
|
|
||||||
if File_transport_internet_headers_wechat_config_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_transport_internet_headers_wechat_config_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 1,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 0,
|
|
||||||
},
|
|
||||||
GoTypes: file_transport_internet_headers_wechat_config_proto_goTypes,
|
|
||||||
DependencyIndexes: file_transport_internet_headers_wechat_config_proto_depIdxs,
|
|
||||||
MessageInfos: file_transport_internet_headers_wechat_config_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_transport_internet_headers_wechat_config_proto = out.File
|
|
||||||
file_transport_internet_headers_wechat_config_proto_rawDesc = nil
|
|
||||||
file_transport_internet_headers_wechat_config_proto_goTypes = nil
|
|
||||||
file_transport_internet_headers_wechat_config_proto_depIdxs = nil
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package xray.transport.internet.headers.wechat;
|
|
||||||
option csharp_namespace = "Xray.Transport.Internet.Headers.Wechat";
|
|
||||||
option go_package = "github.com/xtls/xray-core/transport/internet/headers/wechat";
|
|
||||||
option java_package = "com.xray.transport.internet.headers.wechat";
|
|
||||||
option java_multiple_files = true;
|
|
||||||
|
|
||||||
message VideoConfig {}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package wechat
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/binary"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/dice"
|
|
||||||
)
|
|
||||||
|
|
||||||
type VideoChat struct {
|
|
||||||
sn uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vc *VideoChat) Size() int32 {
|
|
||||||
return 13
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize implements PacketHeader.
|
|
||||||
func (vc *VideoChat) Serialize(b []byte) {
|
|
||||||
vc.sn++
|
|
||||||
b[0] = 0xa1
|
|
||||||
b[1] = 0x08
|
|
||||||
binary.BigEndian.PutUint32(b[2:], vc.sn) // b[2:6]
|
|
||||||
b[6] = 0x00
|
|
||||||
b[7] = 0x10
|
|
||||||
b[8] = 0x11
|
|
||||||
b[9] = 0x18
|
|
||||||
b[10] = 0x30
|
|
||||||
b[11] = 0x22
|
|
||||||
b[12] = 0x30
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewVideoChat returns a new VideoChat instance based on given config.
|
|
||||||
func NewVideoChat(ctx context.Context, config interface{}) (interface{}, error) {
|
|
||||||
return &VideoChat{
|
|
||||||
sn: uint32(dice.RollUint16()),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
common.Must(common.RegisterConfig((*VideoConfig)(nil), NewVideoChat))
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package wechat_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
|
||||||
. "github.com/xtls/xray-core/transport/internet/headers/wechat"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUTPWrite(t *testing.T) {
|
|
||||||
videoRaw, err := NewVideoChat(context.Background(), &VideoConfig{})
|
|
||||||
common.Must(err)
|
|
||||||
|
|
||||||
video := videoRaw.(*VideoChat)
|
|
||||||
|
|
||||||
payload := buf.New()
|
|
||||||
video.Serialize(payload.Extend(video.Size()))
|
|
||||||
|
|
||||||
if payload.Len() != video.Size() {
|
|
||||||
t.Error("expected payload size ", video.Size(), " but got ", payload.Len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.35.1
|
|
||||||
// protoc v5.28.2
|
|
||||||
// source: transport/internet/headers/wireguard/config.proto
|
|
||||||
|
|
||||||
package wireguard
|
|
||||||
|
|
||||||
import (
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
type WireguardConfig struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *WireguardConfig) Reset() {
|
|
||||||
*x = WireguardConfig{}
|
|
||||||
mi := &file_transport_internet_headers_wireguard_config_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *WireguardConfig) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*WireguardConfig) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *WireguardConfig) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_transport_internet_headers_wireguard_config_proto_msgTypes[0]
|
|
||||||
if x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use WireguardConfig.ProtoReflect.Descriptor instead.
|
|
||||||
func (*WireguardConfig) Descriptor() ([]byte, []int) {
|
|
||||||
return file_transport_internet_headers_wireguard_config_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_transport_internet_headers_wireguard_config_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_transport_internet_headers_wireguard_config_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x31, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
|
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x77, 0x69, 0x72,
|
|
||||||
0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72,
|
|
||||||
0x6f, 0x74, 0x6f, 0x12, 0x29, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
|
|
||||||
0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61,
|
|
||||||
0x64, 0x65, 0x72, 0x73, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x22, 0x11,
|
|
||||||
0x0a, 0x0f, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
|
||||||
0x67, 0x42, 0x9d, 0x01, 0x0a, 0x2d, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
|
|
||||||
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
|
|
||||||
0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75,
|
|
||||||
0x61, 0x72, 0x64, 0x50, 0x01, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
|
||||||
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
|
|
||||||
0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
|
||||||
0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x77, 0x69, 0x72, 0x65,
|
|
||||||
0x67, 0x75, 0x61, 0x72, 0x64, 0xaa, 0x02, 0x29, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61,
|
|
||||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
|
||||||
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72,
|
|
||||||
0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_transport_internet_headers_wireguard_config_proto_rawDescOnce sync.Once
|
|
||||||
file_transport_internet_headers_wireguard_config_proto_rawDescData = file_transport_internet_headers_wireguard_config_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_transport_internet_headers_wireguard_config_proto_rawDescGZIP() []byte {
|
|
||||||
file_transport_internet_headers_wireguard_config_proto_rawDescOnce.Do(func() {
|
|
||||||
file_transport_internet_headers_wireguard_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_wireguard_config_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_transport_internet_headers_wireguard_config_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_transport_internet_headers_wireguard_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
|
||||||
var file_transport_internet_headers_wireguard_config_proto_goTypes = []any{
|
|
||||||
(*WireguardConfig)(nil), // 0: xray.transport.internet.headers.wireguard.WireguardConfig
|
|
||||||
}
|
|
||||||
var file_transport_internet_headers_wireguard_config_proto_depIdxs = []int32{
|
|
||||||
0, // [0:0] is the sub-list for method output_type
|
|
||||||
0, // [0:0] is the sub-list for method input_type
|
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
|
||||||
0, // [0:0] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_transport_internet_headers_wireguard_config_proto_init() }
|
|
||||||
func file_transport_internet_headers_wireguard_config_proto_init() {
|
|
||||||
if File_transport_internet_headers_wireguard_config_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_transport_internet_headers_wireguard_config_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 1,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 0,
|
|
||||||
},
|
|
||||||
GoTypes: file_transport_internet_headers_wireguard_config_proto_goTypes,
|
|
||||||
DependencyIndexes: file_transport_internet_headers_wireguard_config_proto_depIdxs,
|
|
||||||
MessageInfos: file_transport_internet_headers_wireguard_config_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_transport_internet_headers_wireguard_config_proto = out.File
|
|
||||||
file_transport_internet_headers_wireguard_config_proto_rawDesc = nil
|
|
||||||
file_transport_internet_headers_wireguard_config_proto_goTypes = nil
|
|
||||||
file_transport_internet_headers_wireguard_config_proto_depIdxs = nil
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package xray.transport.internet.headers.wireguard;
|
|
||||||
option csharp_namespace = "Xray.Transport.Internet.Headers.Wireguard";
|
|
||||||
option go_package = "github.com/xtls/xray-core/transport/internet/headers/wireguard";
|
|
||||||
option java_package = "com.xray.transport.internet.headers.wireguard";
|
|
||||||
option java_multiple_files = true;
|
|
||||||
|
|
||||||
message WireguardConfig {}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package wireguard
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Wireguard struct{}
|
|
||||||
|
|
||||||
func (Wireguard) Size() int32 {
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize implements PacketHeader.
|
|
||||||
func (Wireguard) Serialize(b []byte) {
|
|
||||||
b[0] = 0x04
|
|
||||||
b[1] = 0x00
|
|
||||||
b[2] = 0x00
|
|
||||||
b[3] = 0x00
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWireguard returns a new VideoChat instance based on given config.
|
|
||||||
func NewWireguard(ctx context.Context, config interface{}) (interface{}, error) {
|
|
||||||
return Wireguard{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
common.Must(common.RegisterConfig((*WireguardConfig)(nil), NewWireguard))
|
|
||||||
}
|
|
||||||
@@ -176,6 +176,7 @@ func (c *client) dial() error {
|
|||||||
}
|
}
|
||||||
pktConn, err = udphop.NewUDPHopPacketConn(addr, c.config.IntervalMin, c.config.IntervalMax, c.udphopDialer, pktConn, index)
|
pktConn, err = udphop.NewUDPHopPacketConn(addr, c.config.IntervalMin, c.config.IntervalMax, c.udphopDialer, pktConn, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
raw.Close()
|
||||||
return errors.New("udphop err").Base(err)
|
return errors.New("udphop err").Base(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,6 +184,7 @@ func (c *client) dial() error {
|
|||||||
if c.udpmaskManager != nil {
|
if c.udpmaskManager != nil {
|
||||||
pktConn, err = c.udpmaskManager.WrapPacketConnClient(pktConn)
|
pktConn, err = c.udpmaskManager.WrapPacketConnClient(pktConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
raw.Close()
|
||||||
return errors.New("mask err").Base(err)
|
return errors.New("mask err").Base(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package kcp
|
package kcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/cipher"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
@@ -48,32 +46,12 @@ func (c *Config) GetWriteBufferSize() uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetReadBufferSize returns the size of ReadBuffer in bytes.
|
// GetReadBufferSize returns the size of ReadBuffer in bytes.
|
||||||
func (c *Config) GetReadBufferSize() uint32 {
|
// func (c *Config) GetReadBufferSize() uint32 {
|
||||||
if c == nil || c.ReadBuffer == nil {
|
// if c == nil || c.ReadBuffer == nil {
|
||||||
return 2 * 1024 * 1024
|
// return 2 * 1024 * 1024
|
||||||
}
|
// }
|
||||||
return c.ReadBuffer.Size
|
// return c.ReadBuffer.Size
|
||||||
}
|
// }
|
||||||
|
|
||||||
// GetSecurity returns the security settings.
|
|
||||||
func (c *Config) GetSecurity() (cipher.AEAD, error) {
|
|
||||||
if c.Seed != nil {
|
|
||||||
return NewAEADAESGCMBasedOnSeed(c.Seed.Seed), nil
|
|
||||||
}
|
|
||||||
return NewSimpleAuthenticator(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) GetPackerHeader() (internet.PacketHeader, error) {
|
|
||||||
if c.HeaderConfig != nil {
|
|
||||||
rawConfig, err := c.HeaderConfig.GetInstance()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return internet.CreatePacketHeader(rawConfig)
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Config) GetSendingInFlightSize() uint32 {
|
func (c *Config) GetSendingInFlightSize() uint32 {
|
||||||
size := c.GetUplinkCapacityValue() * 1024 * 1024 / c.GetMTUValue() / (1000 / c.GetTTIValue())
|
size := c.GetUplinkCapacityValue() * 1024 * 1024 / c.GetMTUValue() / (1000 / c.GetTTIValue())
|
||||||
@@ -95,9 +73,9 @@ func (c *Config) GetReceivingInFlightSize() uint32 {
|
|||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetReceivingBufferSize() uint32 {
|
// func (c *Config) GetReceivingBufferSize() uint32 {
|
||||||
return c.GetReadBufferSize() / c.GetMTUValue()
|
// return c.GetReadBufferSize() / c.GetMTUValue()
|
||||||
}
|
// }
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
|
common.Must(internet.RegisterProtocolConfigCreator(protocolName, func() interface{} {
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ type Connection struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewConnection create a new KCP connection between local and remote.
|
// NewConnection create a new KCP connection between local and remote.
|
||||||
func NewConnection(meta ConnMetadata, writer PacketWriter, closer io.Closer, config *Config) *Connection {
|
func NewConnection(meta ConnMetadata, writer io.Writer, closer io.Closer, config *Config) *Connection {
|
||||||
errors.LogInfo(context.Background(), "#", meta.Conversation, " creating connection to ", meta.RemoteAddr)
|
errors.LogInfo(context.Background(), "#", meta.Conversation, " creating connection to ", meta.RemoteAddr)
|
||||||
|
|
||||||
conn := &Connection{
|
conn := &Connection{
|
||||||
@@ -215,7 +215,7 @@ func NewConnection(meta ConnMetadata, writer PacketWriter, closer io.Closer, con
|
|||||||
dataOutput: signal.NewNotifier(),
|
dataOutput: signal.NewNotifier(),
|
||||||
Config: config,
|
Config: config,
|
||||||
output: NewRetryableWriter(NewSegmentWriter(writer)),
|
output: NewRetryableWriter(NewSegmentWriter(writer)),
|
||||||
mss: config.GetMTUValue() - uint32(writer.Overhead()) - DataSegmentOverhead,
|
mss: config.GetMTUValue() - DataSegmentOverhead,
|
||||||
roundTrip: &RoundTripInfo{
|
roundTrip: &RoundTripInfo{
|
||||||
rto: 100,
|
rto: 100,
|
||||||
minRtt: config.GetTTIValue(),
|
minRtt: config.GetTTIValue(),
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ func (NoOpCloser) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestConnectionReadTimeout(t *testing.T) {
|
func TestConnectionReadTimeout(t *testing.T) {
|
||||||
conn := NewConnection(ConnMetadata{Conversation: 1}, &KCPPacketWriter{
|
conn := NewConnection(ConnMetadata{Conversation: 1}, buf.DiscardBytes, NoOpCloser(0), &Config{})
|
||||||
Writer: buf.DiscardBytes,
|
|
||||||
}, NoOpCloser(0), &Config{})
|
|
||||||
conn.SetReadDeadline(time.Now().Add(time.Second))
|
conn.SetReadDeadline(time.Now().Add(time.Second))
|
||||||
|
|
||||||
b := make([]byte, 1024)
|
b := make([]byte, 1024)
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
package kcp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/cipher"
|
|
||||||
"encoding/binary"
|
|
||||||
"hash/fnv"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SimpleAuthenticator is a legacy AEAD used for KCP encryption.
|
|
||||||
type SimpleAuthenticator struct{}
|
|
||||||
|
|
||||||
// NewSimpleAuthenticator creates a new SimpleAuthenticator
|
|
||||||
func NewSimpleAuthenticator() cipher.AEAD {
|
|
||||||
return &SimpleAuthenticator{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NonceSize implements cipher.AEAD.NonceSize().
|
|
||||||
func (*SimpleAuthenticator) NonceSize() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overhead implements cipher.AEAD.NonceSize().
|
|
||||||
func (*SimpleAuthenticator) Overhead() int {
|
|
||||||
return 6
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seal implements cipher.AEAD.Seal().
|
|
||||||
func (a *SimpleAuthenticator) Seal(dst, nonce, plain, extra []byte) []byte {
|
|
||||||
dst = append(dst, 0, 0, 0, 0, 0, 0) // 4 bytes for hash, and then 2 bytes for length
|
|
||||||
binary.BigEndian.PutUint16(dst[4:], uint16(len(plain)))
|
|
||||||
dst = append(dst, plain...)
|
|
||||||
|
|
||||||
fnvHash := fnv.New32a()
|
|
||||||
common.Must2(fnvHash.Write(dst[4:]))
|
|
||||||
fnvHash.Sum(dst[:0])
|
|
||||||
|
|
||||||
dstLen := len(dst)
|
|
||||||
xtra := 4 - dstLen%4
|
|
||||||
if xtra != 4 {
|
|
||||||
dst = append(dst, make([]byte, xtra)...)
|
|
||||||
}
|
|
||||||
xorfwd(dst)
|
|
||||||
if xtra != 4 {
|
|
||||||
dst = dst[:dstLen]
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open implements cipher.AEAD.Open().
|
|
||||||
func (a *SimpleAuthenticator) Open(dst, nonce, cipherText, extra []byte) ([]byte, error) {
|
|
||||||
dst = append(dst, cipherText...)
|
|
||||||
dstLen := len(dst)
|
|
||||||
xtra := 4 - dstLen%4
|
|
||||||
if xtra != 4 {
|
|
||||||
dst = append(dst, make([]byte, xtra)...)
|
|
||||||
}
|
|
||||||
xorbkd(dst)
|
|
||||||
if xtra != 4 {
|
|
||||||
dst = dst[:dstLen]
|
|
||||||
}
|
|
||||||
|
|
||||||
fnvHash := fnv.New32a()
|
|
||||||
common.Must2(fnvHash.Write(dst[4:]))
|
|
||||||
if binary.BigEndian.Uint32(dst[:4]) != fnvHash.Sum32() {
|
|
||||||
return nil, errors.New("invalid auth")
|
|
||||||
}
|
|
||||||
|
|
||||||
length := binary.BigEndian.Uint16(dst[4:6])
|
|
||||||
if len(dst)-6 != int(length) {
|
|
||||||
return nil, errors.New("invalid auth")
|
|
||||||
}
|
|
||||||
|
|
||||||
return dst[6:], nil
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package kcp_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
. "github.com/xtls/xray-core/transport/internet/kcp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSimpleAuthenticator(t *testing.T) {
|
|
||||||
cache := make([]byte, 512)
|
|
||||||
|
|
||||||
payload := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
|
|
||||||
|
|
||||||
auth := NewSimpleAuthenticator()
|
|
||||||
b := auth.Seal(cache[:0], nil, payload, nil)
|
|
||||||
c, err := auth.Open(cache[:0], nil, b, nil)
|
|
||||||
common.Must(err)
|
|
||||||
if r := cmp.Diff(c, payload); r != "" {
|
|
||||||
t.Error(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSimpleAuthenticator2(t *testing.T) {
|
|
||||||
cache := make([]byte, 512)
|
|
||||||
|
|
||||||
payload := []byte{'a', 'b'}
|
|
||||||
|
|
||||||
auth := NewSimpleAuthenticator()
|
|
||||||
b := auth.Seal(cache[:0], nil, payload, nil)
|
|
||||||
c, err := auth.Open(cache[:0], nil, b, nil)
|
|
||||||
common.Must(err)
|
|
||||||
if r := cmp.Diff(c, payload); r != "" {
|
|
||||||
t.Error(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package kcp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/sha256"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewAEADAESGCMBasedOnSeed(seed string) cipher.AEAD {
|
|
||||||
hashedSeed := sha256.Sum256([]byte(seed))
|
|
||||||
return crypto.NewAesGcm(hashedSeed[:16])
|
|
||||||
}
|
|
||||||
@@ -54,32 +54,32 @@ func DialKCP(ctx context.Context, dest net.Destination, streamSettings *internet
|
|||||||
return nil, errors.New("failed to dial to dest: ", err).AtWarning().Base(err)
|
return nil, errors.New("failed to dial to dest: ", err).AtWarning().Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wrapper, ok := rawConn.(*internet.PacketConnWrapper)
|
||||||
|
if !ok {
|
||||||
|
rawConn.Close()
|
||||||
|
return nil, errors.New("raw is not PacketConnWrapper")
|
||||||
|
}
|
||||||
|
|
||||||
|
raw := wrapper.Conn
|
||||||
|
|
||||||
|
if streamSettings.UdpmaskManager != nil {
|
||||||
|
wrapper.Conn, err = streamSettings.UdpmaskManager.WrapPacketConnClient(raw)
|
||||||
|
if err != nil {
|
||||||
|
raw.Close()
|
||||||
|
return nil, errors.New("mask err").Base(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
kcpSettings := streamSettings.ProtocolSettings.(*Config)
|
kcpSettings := streamSettings.ProtocolSettings.(*Config)
|
||||||
|
|
||||||
header, err := kcpSettings.GetPackerHeader()
|
reader := &KCPPacketReader{}
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("failed to create packet header").Base(err)
|
|
||||||
}
|
|
||||||
security, err := kcpSettings.GetSecurity()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("failed to create security").Base(err)
|
|
||||||
}
|
|
||||||
reader := &KCPPacketReader{
|
|
||||||
Header: header,
|
|
||||||
Security: security,
|
|
||||||
}
|
|
||||||
writer := &KCPPacketWriter{
|
|
||||||
Header: header,
|
|
||||||
Security: security,
|
|
||||||
Writer: rawConn,
|
|
||||||
}
|
|
||||||
|
|
||||||
conv := uint16(atomic.AddUint32(&globalConv, 1))
|
conv := uint16(atomic.AddUint32(&globalConv, 1))
|
||||||
session := NewConnection(ConnMetadata{
|
session := NewConnection(ConnMetadata{
|
||||||
LocalAddr: rawConn.LocalAddr(),
|
LocalAddr: rawConn.LocalAddr(),
|
||||||
RemoteAddr: rawConn.RemoteAddr(),
|
RemoteAddr: rawConn.RemoteAddr(),
|
||||||
Conversation: conv,
|
Conversation: conv,
|
||||||
}, writer, rawConn, kcpSettings)
|
}, rawConn, rawConn, kcpSettings)
|
||||||
|
|
||||||
go fetchInput(ctx, rawConn, reader, session)
|
go fetchInput(ctx, rawConn, reader, session)
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +1,12 @@
|
|||||||
package kcp
|
package kcp
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rand"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PacketReader interface {
|
type PacketReader interface {
|
||||||
Read([]byte) []Segment
|
Read([]byte) []Segment
|
||||||
}
|
}
|
||||||
|
|
||||||
type PacketWriter interface {
|
type KCPPacketReader struct{}
|
||||||
Overhead() int
|
|
||||||
io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
type KCPPacketReader struct {
|
|
||||||
Security cipher.AEAD
|
|
||||||
Header internet.PacketHeader
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *KCPPacketReader) Read(b []byte) []Segment {
|
func (r *KCPPacketReader) Read(b []byte) []Segment {
|
||||||
if r.Header != nil {
|
|
||||||
if int32(len(b)) <= r.Header.Size() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
b = b[r.Header.Size():]
|
|
||||||
}
|
|
||||||
if r.Security != nil {
|
|
||||||
nonceSize := r.Security.NonceSize()
|
|
||||||
overhead := r.Security.Overhead()
|
|
||||||
if len(b) <= nonceSize+overhead {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out, err := r.Security.Open(b[nonceSize:nonceSize], b[:nonceSize], b[nonceSize:], nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
b = out
|
|
||||||
}
|
|
||||||
var result []Segment
|
var result []Segment
|
||||||
for len(b) > 0 {
|
for len(b) > 0 {
|
||||||
seg, x := ReadSegment(b)
|
seg, x := ReadSegment(b)
|
||||||
@@ -54,42 +18,3 @@ func (r *KCPPacketReader) Read(b []byte) []Segment {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
type KCPPacketWriter struct {
|
|
||||||
Header internet.PacketHeader
|
|
||||||
Security cipher.AEAD
|
|
||||||
Writer io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *KCPPacketWriter) Overhead() int {
|
|
||||||
overhead := 0
|
|
||||||
if w.Header != nil {
|
|
||||||
overhead += int(w.Header.Size())
|
|
||||||
}
|
|
||||||
if w.Security != nil {
|
|
||||||
overhead += w.Security.Overhead()
|
|
||||||
}
|
|
||||||
return overhead
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *KCPPacketWriter) Write(b []byte) (int, error) {
|
|
||||||
bb := buf.StackNew()
|
|
||||||
defer bb.Release()
|
|
||||||
|
|
||||||
if w.Header != nil {
|
|
||||||
w.Header.Serialize(bb.Extend(w.Header.Size()))
|
|
||||||
}
|
|
||||||
if w.Security != nil {
|
|
||||||
nonceSize := w.Security.NonceSize()
|
|
||||||
common.Must2(bb.ReadFullFrom(rand.Reader, int32(nonceSize)))
|
|
||||||
nonce := bb.BytesFrom(int32(-nonceSize))
|
|
||||||
|
|
||||||
encrypted := bb.Extend(int32(w.Security.Overhead() + len(b)))
|
|
||||||
w.Security.Seal(encrypted[:0], nonce, b, nil)
|
|
||||||
} else {
|
|
||||||
bb.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := w.Writer.Write(bb.Bytes())
|
|
||||||
return len(b), err
|
|
||||||
}
|
|
||||||
@@ -7,9 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestKCPPacketReader(t *testing.T) {
|
func TestKCPPacketReader(t *testing.T) {
|
||||||
reader := KCPPacketReader{
|
reader := KCPPacketReader{}
|
||||||
Security: &SimpleAuthenticator{},
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
Input []byte
|
Input []byte
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package kcp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/cipher"
|
|
||||||
gotls "crypto/tls"
|
gotls "crypto/tls"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -30,28 +29,14 @@ type Listener struct {
|
|||||||
tlsConfig *gotls.Config
|
tlsConfig *gotls.Config
|
||||||
config *Config
|
config *Config
|
||||||
reader PacketReader
|
reader PacketReader
|
||||||
header internet.PacketHeader
|
|
||||||
security cipher.AEAD
|
|
||||||
addConn internet.ConnHandler
|
addConn internet.ConnHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewListener(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (*Listener, error) {
|
func NewListener(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (*Listener, error) {
|
||||||
kcpSettings := streamSettings.ProtocolSettings.(*Config)
|
kcpSettings := streamSettings.ProtocolSettings.(*Config)
|
||||||
header, err := kcpSettings.GetPackerHeader()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("failed to create packet header").Base(err).AtError()
|
|
||||||
}
|
|
||||||
security, err := kcpSettings.GetSecurity()
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("failed to create security").Base(err).AtError()
|
|
||||||
}
|
|
||||||
l := &Listener{
|
l := &Listener{
|
||||||
header: header,
|
reader: &KCPPacketReader{},
|
||||||
security: security,
|
|
||||||
reader: &KCPPacketReader{
|
|
||||||
Header: header,
|
|
||||||
Security: security,
|
|
||||||
},
|
|
||||||
sessions: make(map[ConnectionID]*Connection),
|
sessions: make(map[ConnectionID]*Connection),
|
||||||
config: kcpSettings,
|
config: kcpSettings,
|
||||||
addConn: addConn,
|
addConn: addConn,
|
||||||
@@ -124,11 +109,7 @@ func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination) {
|
|||||||
LocalAddr: localAddr,
|
LocalAddr: localAddr,
|
||||||
RemoteAddr: remoteAddr,
|
RemoteAddr: remoteAddr,
|
||||||
Conversation: conv,
|
Conversation: conv,
|
||||||
}, &KCPPacketWriter{
|
}, writer, writer, l.config)
|
||||||
Header: l.header,
|
|
||||||
Security: l.security,
|
|
||||||
Writer: writer,
|
|
||||||
}, writer, l.config)
|
|
||||||
var netConn stat.Connection = conn
|
var netConn stat.Connection = conn
|
||||||
if l.tlsConfig != nil {
|
if l.tlsConfig != nil {
|
||||||
netConn = tls.Server(conn, l.tlsConfig)
|
netConn = tls.Server(conn, l.tlsConfig)
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ func HubReceiveOriginalDestination(r bool) HubOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Hub struct {
|
type Hub struct {
|
||||||
conn *net.UDPConn
|
conn net.PacketConn
|
||||||
|
udpConn *net.UDPConn
|
||||||
cache chan *udp.Packet
|
cache chan *udp.Packet
|
||||||
capacity int
|
capacity int
|
||||||
recvOrigDest bool
|
recvOrigDest bool
|
||||||
@@ -56,15 +57,27 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe
|
|||||||
hub.recvOrigDest = true
|
hub.recvOrigDest = true
|
||||||
}
|
}
|
||||||
|
|
||||||
udpConn, err := internet.ListenSystemPacket(ctx, &net.UDPAddr{
|
var err error
|
||||||
|
hub.conn, err = internet.ListenSystemPacket(ctx, &net.UDPAddr{
|
||||||
IP: address.IP(),
|
IP: address.IP(),
|
||||||
Port: int(port),
|
Port: int(port),
|
||||||
}, sockopt)
|
}, sockopt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
raw := hub.conn
|
||||||
|
|
||||||
|
if streamSettings.UdpmaskManager != nil {
|
||||||
|
hub.conn, err = streamSettings.UdpmaskManager.WrapPacketConnServer(raw)
|
||||||
|
if err != nil {
|
||||||
|
raw.Close()
|
||||||
|
return nil, errors.New("mask err").Base(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
errors.LogInfo(ctx, "listening UDP on ", address, ":", port)
|
errors.LogInfo(ctx, "listening UDP on ", address, ":", port)
|
||||||
hub.conn = udpConn.(*net.UDPConn)
|
hub.udpConn, _ = hub.conn.(*net.UDPConn)
|
||||||
hub.cache = make(chan *udp.Packet, hub.capacity)
|
hub.cache = make(chan *udp.Packet, hub.capacity)
|
||||||
|
|
||||||
go hub.start()
|
go hub.start()
|
||||||
@@ -78,7 +91,7 @@ func (h *Hub) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hub) WriteTo(payload []byte, dest net.Destination) (int, error) {
|
func (h *Hub) WriteTo(payload []byte, dest net.Destination) (int, error) {
|
||||||
return h.conn.WriteToUDP(payload, &net.UDPAddr{
|
return h.conn.WriteTo(payload, &net.UDPAddr{
|
||||||
IP: dest.Address.IP(),
|
IP: dest.Address.IP(),
|
||||||
Port: int(dest.Port),
|
Port: int(dest.Port),
|
||||||
})
|
})
|
||||||
@@ -93,10 +106,21 @@ func (h *Hub) start() {
|
|||||||
for {
|
for {
|
||||||
buffer := buf.New()
|
buffer := buf.New()
|
||||||
var noob int
|
var noob int
|
||||||
var addr *net.UDPAddr
|
var udpAddr *net.UDPAddr
|
||||||
rawBytes := buffer.Extend(buf.Size)
|
rawBytes := buffer.Extend(buf.Size)
|
||||||
|
|
||||||
n, noob, _, addr, err := ReadUDPMsg(h.conn, rawBytes, oobBytes)
|
var n int
|
||||||
|
var err error
|
||||||
|
if h.udpConn != nil {
|
||||||
|
n, noob, _, udpAddr, err = ReadUDPMsg(h.udpConn, rawBytes, oobBytes)
|
||||||
|
} else {
|
||||||
|
var addr net.Addr
|
||||||
|
n, addr, err = h.conn.ReadFrom(rawBytes)
|
||||||
|
if err == nil {
|
||||||
|
udpAddr = addr.(*net.UDPAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogInfoInner(context.Background(), err, "failed to read UDP msg")
|
errors.LogInfoInner(context.Background(), err, "failed to read UDP msg")
|
||||||
buffer.Release()
|
buffer.Release()
|
||||||
@@ -111,7 +135,7 @@ func (h *Hub) start() {
|
|||||||
|
|
||||||
payload := &udp.Packet{
|
payload := &udp.Packet{
|
||||||
Payload: buffer,
|
Payload: buffer,
|
||||||
Source: net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)),
|
Source: net.UDPDestination(net.IPAddress(udpAddr.IP), net.Port(udpAddr.Port)),
|
||||||
}
|
}
|
||||||
if h.recvOrigDest && noob > 0 {
|
if h.recvOrigDest && noob > 0 {
|
||||||
payload.Target = RetrieveOriginalDest(oobBytes[:noob])
|
payload.Target = RetrieveOriginalDest(oobBytes[:noob])
|
||||||
|
|||||||
Reference in new issue
Block a user