mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-07-28 17:51:50 +03:00
Add admin panel, manager, node_manager, bandwidth limiter, connection limiter, bonding, failover, vless encryption, mkcp transport
This commit is contained in:
231
transport/v2raykcp/dialer.go
Normal file
231
transport/v2raykcp/dialer.go
Normal file
@@ -0,0 +1,231 @@
|
||||
package v2raykcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/tls"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
var _ adapter.V2RayClientTransport = (*Client)(nil)
|
||||
|
||||
type Client struct {
|
||||
ctx context.Context
|
||||
dialer N.Dialer
|
||||
serverAddr M.Socksaddr
|
||||
config *Config
|
||||
tlsConfig tls.Config
|
||||
}
|
||||
|
||||
func NewClient(
|
||||
ctx context.Context,
|
||||
dialer N.Dialer,
|
||||
serverAddr M.Socksaddr,
|
||||
options option.V2RayKCPOptions,
|
||||
tlsConfig tls.Config,
|
||||
) (adapter.V2RayClientTransport, error) {
|
||||
return &Client{
|
||||
ctx: ctx,
|
||||
dialer: dialer,
|
||||
serverAddr: serverAddr,
|
||||
config: NewConfig(options),
|
||||
tlsConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
// Dial UDP connection
|
||||
udpConn, err := c.dialer.DialContext(ctx, N.NetworkUDP, c.serverAddr)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dial UDP")
|
||||
}
|
||||
|
||||
// Wrap as PacketConn
|
||||
packetConn := bufio.NewUnbindPacketConn(udpConn)
|
||||
|
||||
// Generate conversation ID
|
||||
var convID uint16
|
||||
binary.Read(rand.Reader, binary.BigEndian, &convID)
|
||||
|
||||
// Create KCP connection
|
||||
kcpConn, err := c.createConnection(ctx, packetConn, c.serverAddr.UDPAddr(), convID)
|
||||
if err != nil {
|
||||
udpConn.Close()
|
||||
return nil, E.Cause(err, "create KCP connection")
|
||||
}
|
||||
|
||||
// Wrap with TLS if configured
|
||||
if c.tlsConfig != nil {
|
||||
tlsConn, err := tls.ClientHandshake(ctx, kcpConn, c.tlsConfig)
|
||||
if err != nil {
|
||||
kcpConn.Close()
|
||||
return nil, E.Cause(err, "TLS handshake")
|
||||
}
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
return kcpConn, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) createConnection(ctx context.Context, conn N.PacketConn, remoteAddr *net.UDPAddr, convID uint16) (*Connection, error) {
|
||||
security, err := c.config.GetSecurity()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "get security")
|
||||
}
|
||||
|
||||
// Create packet header
|
||||
header := c.config.GetPacketHeader()
|
||||
|
||||
// Create packet writer
|
||||
writer := &kcpPacketWriter{
|
||||
conn: conn,
|
||||
remoteAddr: remoteAddr,
|
||||
header: header,
|
||||
security: security,
|
||||
}
|
||||
|
||||
// Create packet reader
|
||||
reader := &kcpPacketReader{
|
||||
security: security,
|
||||
headerSize: HeaderSize(c.config.GetHeaderType()),
|
||||
}
|
||||
|
||||
// Create connection metadata
|
||||
meta := ConnMetadata{
|
||||
LocalAddr: conn.LocalAddr(),
|
||||
RemoteAddr: remoteAddr,
|
||||
Conversation: convID,
|
||||
}
|
||||
|
||||
// Create KCP connection
|
||||
kcpConn := NewConnection(meta, writer, conn, c.config)
|
||||
|
||||
// Start reading goroutine
|
||||
go c.readLoop(ctx, conn, reader, kcpConn)
|
||||
|
||||
return kcpConn, nil
|
||||
}
|
||||
|
||||
func (c *Client) readLoop(ctx context.Context, conn N.PacketConn, reader *kcpPacketReader, kcpConn *Connection) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
buffer := buf.New()
|
||||
_, err := conn.ReadPacket(buffer)
|
||||
if err != nil {
|
||||
buffer.Release()
|
||||
return
|
||||
}
|
||||
|
||||
segments := reader.Read(buffer.Bytes())
|
||||
buffer.Release()
|
||||
|
||||
if len(segments) > 0 {
|
||||
kcpConn.Input(segments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type kcpPacketWriter struct {
|
||||
conn N.PacketConn
|
||||
remoteAddr *net.UDPAddr
|
||||
header PacketHeader
|
||||
security cipher.AEAD
|
||||
}
|
||||
|
||||
func (w *kcpPacketWriter) Overhead() int {
|
||||
overhead := 0
|
||||
if w.header != nil {
|
||||
overhead += w.header.Size()
|
||||
}
|
||||
if w.security != nil {
|
||||
overhead += w.security.Overhead()
|
||||
}
|
||||
return overhead
|
||||
}
|
||||
|
||||
func (w *kcpPacketWriter) Write(b []byte) (int, error) {
|
||||
packet := buf.New()
|
||||
defer packet.Release()
|
||||
|
||||
if w.header != nil {
|
||||
headerBytes := packet.Extend(w.header.Size())
|
||||
w.header.Serialize(headerBytes)
|
||||
}
|
||||
|
||||
if w.security != nil {
|
||||
nonceSize := w.security.NonceSize()
|
||||
nonce := packet.Extend(nonceSize)
|
||||
common.Must1(rand.Read(nonce))
|
||||
|
||||
encrypted := w.security.Seal(nil, nonce, b, nil)
|
||||
packet.Write(encrypted)
|
||||
} else {
|
||||
packet.Write(b)
|
||||
}
|
||||
|
||||
destAddr := M.SocksaddrFromNet(w.remoteAddr)
|
||||
err := w.conn.WritePacket(packet, destAddr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
type kcpPacketReader struct {
|
||||
security cipher.AEAD
|
||||
headerSize int
|
||||
}
|
||||
|
||||
func (r *kcpPacketReader) Read(b []byte) []Segment {
|
||||
if r.headerSize > 0 {
|
||||
if len(b) <= r.headerSize {
|
||||
return nil
|
||||
}
|
||||
b = b[r.headerSize:]
|
||||
}
|
||||
|
||||
if r.security != nil {
|
||||
nonceSize := r.security.NonceSize()
|
||||
overhead := r.security.Overhead()
|
||||
if len(b) <= nonceSize+overhead {
|
||||
return nil
|
||||
}
|
||||
out, err := r.security.Open(nil, b[:nonceSize], b[nonceSize:], nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
b = out
|
||||
}
|
||||
|
||||
var result []Segment
|
||||
for len(b) > 0 {
|
||||
seg, extra := ReadSegment(b)
|
||||
if seg == nil {
|
||||
break
|
||||
}
|
||||
result = append(result, seg)
|
||||
b = extra
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user