Skip to content

Commit d93197f

Browse files
committed
Add Datagram transport
1 parent 898e6f3 commit d93197f

File tree

7 files changed

+468
-2
lines changed

7 files changed

+468
-2
lines changed

infra/conf/transport_internet.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/xtls/xray-core/transport/internet"
1919
"github.com/xtls/xray-core/transport/internet/httpupgrade"
2020
"github.com/xtls/xray-core/transport/internet/kcp"
21+
"github.com/xtls/xray-core/transport/internet/quic"
2122
"github.com/xtls/xray-core/transport/internet/reality"
2223
"github.com/xtls/xray-core/transport/internet/splithttp"
2324
"github.com/xtls/xray-core/transport/internet/tcp"
@@ -326,6 +327,22 @@ func (c *SplitHTTPConfig) Build() (proto.Message, error) {
326327
return config, nil
327328
}
328329

330+
type QUICConfig struct {
331+
// Header json.RawMessage `json:"header"`
332+
// Security string `json:"security"`
333+
// Key string `json:"key"`
334+
335+
Fec bool `json:"fec"`
336+
}
337+
338+
// Build implements Buildable.
339+
func (c *QUICConfig) Build() (proto.Message, error) {
340+
config := &quic.Config{
341+
Fec: c.Fec,
342+
}
343+
return config, nil
344+
}
345+
329346
func readFileOrString(f string, s []string) ([]byte, error) {
330347
if len(f) > 0 {
331348
return filesystem.ReadFile(f)
@@ -659,8 +676,8 @@ func (p TransportProtocol) Build() (string, error) {
659676
return "httpupgrade", nil
660677
case "h2", "h3", "http":
661678
return "", errors.PrintRemovedFeatureError("HTTP transport (without header padding, etc.)", "XHTTP stream-one H2 & H3")
662-
case "quic":
663-
return "", errors.PrintRemovedFeatureError("QUIC transport (without web service, etc.)", "XHTTP stream-one H3")
679+
case "quic", "datagram":
680+
return "quic", nil
664681
default:
665682
return "", errors.New("Config: unknown transport protocol: ", p)
666683
}
@@ -793,6 +810,7 @@ type StreamConfig struct {
793810
XHTTPSettings *SplitHTTPConfig `json:"xhttpSettings"`
794811
SplitHTTPSettings *SplitHTTPConfig `json:"splithttpSettings"`
795812
KCPSettings *KCPConfig `json:"kcpSettings"`
813+
QUICSettings *QUICConfig `json:"quicSettings"`
796814
GRPCSettings *GRPCConfig `json:"grpcSettings"`
797815
WSSettings *WebSocketConfig `json:"wsSettings"`
798816
HTTPUPGRADESettings *HttpUpgradeConfig `json:"httpupgradeSettings"`
@@ -884,6 +902,16 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
884902
Settings: serial.ToTypedMessage(ts),
885903
})
886904
}
905+
if c.QUICSettings != nil {
906+
qs, err := c.QUICSettings.Build()
907+
if err != nil {
908+
return nil, errors.New("Failed to build QUIC config").Base(err)
909+
}
910+
config.TransportSettings = append(config.TransportSettings, &internet.TransportConfig{
911+
ProtocolName: "quic",
912+
Settings: serial.ToTypedMessage(qs),
913+
})
914+
}
887915
if c.GRPCSettings != nil {
888916
gs, err := c.GRPCSettings.Build()
889917
if err != nil {

main/distro/all/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import (
5353
_ "github.com/xtls/xray-core/transport/internet/grpc"
5454
_ "github.com/xtls/xray-core/transport/internet/httpupgrade"
5555
_ "github.com/xtls/xray-core/transport/internet/kcp"
56+
_ "github.com/xtls/xray-core/transport/internet/quic"
5657
_ "github.com/xtls/xray-core/transport/internet/reality"
5758
_ "github.com/xtls/xray-core/transport/internet/splithttp"
5859
_ "github.com/xtls/xray-core/transport/internet/tcp"

transport/internet/quic/conn.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package quic
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/xtls/quic-go"
8+
"github.com/xtls/xray-core/common/buf"
9+
"github.com/xtls/xray-core/common/net"
10+
)
11+
12+
type interConn struct {
13+
ctx context.Context
14+
quicConn quic.Connection
15+
local net.Addr
16+
remote net.Addr
17+
}
18+
19+
func (c *interConn) Read(b []byte) (int, error) {
20+
received, e := c.quicConn.ReceiveDatagram(c.ctx)
21+
if e != nil {
22+
return 0, e
23+
}
24+
nBytes := copy(b, received[:])
25+
return nBytes, nil
26+
}
27+
28+
func (c *interConn) WriteMultiBuffer(mb buf.MultiBuffer) error {
29+
mb = buf.Compact(mb)
30+
mb, err := buf.WriteMultiBuffer(c, mb)
31+
buf.ReleaseMulti(mb)
32+
return err
33+
}
34+
35+
func (c *interConn) Write(b []byte) (int, error) {
36+
return len(b), c.quicConn.SendDatagram(b)
37+
}
38+
39+
func (c *interConn) Close() error {
40+
return nil
41+
}
42+
43+
func (c *interConn) LocalAddr() net.Addr {
44+
return c.local
45+
}
46+
47+
func (c *interConn) RemoteAddr() net.Addr {
48+
return c.remote
49+
}
50+
51+
func (c *interConn) SetDeadline(t time.Time) error {
52+
return nil
53+
}
54+
55+
func (c *interConn) SetReadDeadline(t time.Time) error {
56+
return nil
57+
}
58+
59+
func (c *interConn) SetWriteDeadline(t time.Time) error {
60+
return nil
61+
}

transport/internet/quic/dialer.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package quic
2+
3+
import (
4+
"context"
5+
"sync"
6+
"time"
7+
8+
"github.com/xtls/quic-go"
9+
"github.com/xtls/xray-core/common"
10+
"github.com/xtls/xray-core/common/errors"
11+
"github.com/xtls/xray-core/common/net"
12+
"github.com/xtls/xray-core/transport/internet"
13+
"github.com/xtls/xray-core/transport/internet/stat"
14+
"github.com/xtls/xray-core/transport/internet/tls"
15+
)
16+
17+
type connectionContext struct {
18+
rawConn *net.UDPConn
19+
conn quic.Connection
20+
}
21+
22+
type clientConnections struct {
23+
access sync.Mutex
24+
conns map[net.Destination][]*connectionContext
25+
// cleanup *task.Periodic
26+
}
27+
28+
func isActive(s quic.Connection) bool {
29+
select {
30+
case <-s.Context().Done():
31+
return false
32+
default:
33+
return true
34+
}
35+
}
36+
37+
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
38+
tlsConfig := tls.ConfigFromStreamSettings(streamSettings)
39+
if tlsConfig == nil {
40+
tlsConfig = &tls.Config{
41+
ServerName: internalDomain,
42+
AllowInsecure: true,
43+
}
44+
}
45+
46+
var destAddr *net.UDPAddr
47+
if dest.Address.Family().IsIP() {
48+
destAddr = &net.UDPAddr{
49+
IP: dest.Address.IP(),
50+
Port: int(dest.Port),
51+
}
52+
} else {
53+
dialerIp := internet.DestIpAddress()
54+
if dialerIp != nil {
55+
destAddr = &net.UDPAddr{
56+
IP: dialerIp,
57+
Port: int(dest.Port),
58+
}
59+
errors.LogInfo(ctx, "quic Dial use dialer dest addr: ", destAddr)
60+
} else {
61+
addr, err := net.ResolveUDPAddr("udp", dest.NetAddr())
62+
if err != nil {
63+
return nil, err
64+
}
65+
destAddr = addr
66+
}
67+
}
68+
69+
config := streamSettings.ProtocolSettings.(*Config)
70+
71+
return client.openConnection(ctx, destAddr, config, tlsConfig, streamSettings.SocketSettings)
72+
}
73+
74+
func (s *clientConnections) openConnection(ctx context.Context, destAddr net.Addr, config *Config, tlsConfig *tls.Config, sockopt *internet.SocketConfig) (stat.Connection, error) {
75+
s.access.Lock()
76+
defer s.access.Unlock()
77+
78+
if s.conns == nil {
79+
s.conns = make(map[net.Destination][]*connectionContext)
80+
}
81+
82+
dest := net.DestinationFromAddr(destAddr)
83+
84+
var conns []*connectionContext
85+
if s, found := s.conns[dest]; found {
86+
conns = s
87+
}
88+
89+
if len(conns) > 0 {
90+
s := conns[len(conns)-1]
91+
if isActive(s.conn) {
92+
return &interConn{
93+
ctx: ctx,
94+
quicConn: s.conn,
95+
local: s.conn.LocalAddr(),
96+
remote: destAddr,
97+
}, nil
98+
} else {
99+
errors.LogInfo(ctx, "current quic connection is not active!")
100+
}
101+
}
102+
103+
errors.LogInfo(ctx, "dialing quic to ", dest)
104+
rawConn, err := internet.DialSystem(ctx, dest, sockopt)
105+
if err != nil {
106+
return nil, errors.New("failed to dial to dest: ", err).AtWarning().Base(err)
107+
}
108+
109+
quicConfig := &quic.Config{
110+
KeepAlivePeriod: 0,
111+
HandshakeIdleTimeout: time.Second * 8,
112+
MaxIdleTimeout: time.Second * 300,
113+
EnableDatagrams: true,
114+
}
115+
116+
var udpConn *net.UDPConn
117+
switch conn := rawConn.(type) {
118+
case *net.UDPConn:
119+
udpConn = conn
120+
case *internet.PacketConnWrapper:
121+
udpConn = conn.Conn.(*net.UDPConn)
122+
default:
123+
rawConn.Close()
124+
return nil, errors.New("QUIC with sockopt is unsupported").AtWarning()
125+
}
126+
127+
tr := quic.Transport{
128+
ConnectionIDLength: 12,
129+
Conn: udpConn,
130+
}
131+
conn, err := tr.Dial(context.Background(), destAddr, tlsConfig.GetTLSConfig(tls.WithDestination(dest)), quicConfig)
132+
if err != nil {
133+
udpConn.Close()
134+
return nil, err
135+
}
136+
137+
context := &connectionContext{
138+
conn: conn,
139+
rawConn: udpConn,
140+
}
141+
s.conns[dest] = append(conns, context)
142+
return &interConn{
143+
ctx: ctx,
144+
quicConn: context.conn,
145+
local: context.conn.LocalAddr(),
146+
remote: destAddr,
147+
}, nil
148+
}
149+
150+
var client clientConnections
151+
152+
func init() {
153+
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
154+
}

0 commit comments

Comments
 (0)