You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

103 lines
2.5 KiB

package tcputil
import (
"net"
"time"
"github.com/xtaci/kcp-go/v5"
"github.com/xtaci/smux"
)
// DtlsPacketConn wraps a net.Conn (DTLS) as a net.PacketConn for KCP.
// Each DTLS Read/Write preserves message boundaries (datagram semantics).
type DtlsPacketConn struct {
conn net.Conn
}
func NewDtlsPacketConn(conn net.Conn) *DtlsPacketConn {
return &DtlsPacketConn{conn: conn}
}
func (d *DtlsPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
n, err := d.conn.Read(b)
return n, d.conn.RemoteAddr(), err
}
func (d *DtlsPacketConn) WriteTo(b []byte, _ net.Addr) (int, error) {
return d.conn.Write(b)
}
func (d *DtlsPacketConn) Close() error {
return d.conn.Close()
}
func (d *DtlsPacketConn) LocalAddr() net.Addr {
return d.conn.LocalAddr()
}
func (d *DtlsPacketConn) SetDeadline(t time.Time) error {
return d.conn.SetDeadline(t)
}
func (d *DtlsPacketConn) SetReadDeadline(t time.Time) error {
return d.conn.SetReadDeadline(t)
}
func (d *DtlsPacketConn) SetWriteDeadline(t time.Time) error {
return d.conn.SetWriteDeadline(t)
}
// NewKCPOverDTLS creates a KCP session over a DTLS connection.
// isServer: true for server-side (listener), false for client-side (dialer).
func NewKCPOverDTLS(dtlsConn net.Conn, isServer bool) (*kcp.UDPSession, error) {
pc := NewDtlsPacketConn(dtlsConn)
block, err := kcp.NewNoneBlockCrypt(nil) // DTLS already encrypts
if err != nil {
return nil, err
}
var sess *kcp.UDPSession
if isServer {
// Server: listen on the PacketConn and accept one session
var listener *kcp.Listener
listener, err = kcp.ServeConn(block, 0, 0, pc)
if err != nil {
return nil, err
}
if err = listener.SetDeadline(time.Now().Add(30 * time.Second)); err != nil {
return nil, err
}
sess, err = listener.AcceptKCP()
if err != nil {
return nil, err
}
} else {
// Client: dial through the PacketConn
sess, err = kcp.NewConn2(dtlsConn.RemoteAddr(), block, 0, 0, pc)
if err != nil {
return nil, err
}
}
// Tune KCP for TURN tunnel:
// - NoDelay mode for lower latency
// - Window sizes suitable for ~5Mbit/s
sess.SetNoDelay(1, 10, 2, 1) // nodelay, interval(ms), resend, nc
sess.SetWindowSize(4096, 4096)
sess.SetMtu(1280) // conservative MTU to fit inside DTLS+TURN
sess.SetACKNoDelay(true)
return sess, nil
}
// DefaultSmuxConfig returns smux config tuned for TURN tunnel.
func DefaultSmuxConfig() *smux.Config {
cfg := smux.DefaultConfig()
cfg.MaxReceiveBuffer = 4 * 1024 * 1024
cfg.MaxStreamBuffer = 1 * 1024 * 1024
cfg.KeepAliveInterval = 10 * time.Second
cfg.KeepAliveTimeout = 30 * time.Second
return cfg
}