mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-08-03 12:25:17 +03:00
Add MTProxy, MASQUE, VPN, Link parser. Update AmneziaWG. Remove Tunneling
This commit is contained in:
200
transport/masque/tunnel.go
Normal file
200
transport/masque/tunnel.go
Normal file
@@ -0,0 +1,200 @@
|
||||
package masque
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
connectip "github.com/Diniboy1123/connect-ip-go"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
||||
type TunnelDevice interface {
|
||||
ReadPacket(buf []byte) (int, error)
|
||||
WritePacket(pkt []byte) error
|
||||
}
|
||||
|
||||
type Tunnel struct {
|
||||
ctx context.Context
|
||||
logger logger.ContextLogger
|
||||
options TunnelOptions
|
||||
tunDevice Device
|
||||
tunnelDevice TunnelDevice
|
||||
}
|
||||
|
||||
func NewTunnel(ctx context.Context, logger logger.ContextLogger, options TunnelOptions) (*Tunnel, error) {
|
||||
deviceOptions := DeviceOptions{
|
||||
Context: ctx,
|
||||
Logger: logger,
|
||||
Handler: options.Handler,
|
||||
UDPTimeout: options.UDPTimeout,
|
||||
MTU: 1280,
|
||||
Address: options.Address,
|
||||
}
|
||||
tunDevice, err := NewDevice(deviceOptions)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create MASQUE device")
|
||||
}
|
||||
return &Tunnel{
|
||||
ctx: ctx,
|
||||
logger: logger,
|
||||
options: options,
|
||||
tunDevice: tunDevice,
|
||||
tunnelDevice: NewNetstackAdapter(tunDevice),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *Tunnel) Start(resolve bool) error {
|
||||
if resolve {
|
||||
err := e.tunDevice.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go e.MaintainTunnel()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Tunnel) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||
if !destination.Addr.IsValid() {
|
||||
return nil, E.Cause(os.ErrInvalid, "invalid non-IP destination")
|
||||
}
|
||||
return e.tunDevice.DialContext(ctx, network, destination)
|
||||
}
|
||||
|
||||
func (e *Tunnel) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||
if !destination.Addr.IsValid() {
|
||||
return nil, E.Cause(os.ErrInvalid, "invalid non-IP destination")
|
||||
}
|
||||
return e.tunDevice.ListenPacket(ctx, destination)
|
||||
}
|
||||
|
||||
func (e *Tunnel) Close() error {
|
||||
return e.tunDevice.Close()
|
||||
}
|
||||
|
||||
func (e *Tunnel) MaintainTunnel() {
|
||||
packetBufferPool := NewNetBuffer(1280)
|
||||
timer := time.NewTimer(0)
|
||||
defer timer.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-e.ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
e.logger.InfoContext(e.ctx, fmt.Errorf("Establishing MASQUE connection to %s", e.options.Endpoint))
|
||||
udpConn, tr, ipConn, rsp, err := ConnectTunnel(
|
||||
e.ctx,
|
||||
e.options.Dialer,
|
||||
e.options.TLSConfig,
|
||||
DefaultQuicConfig(e.options.UDPKeepalivePeriod, e.options.UDPInitialPacketSize),
|
||||
"https://cloudflareaccess.com",
|
||||
e.options.Endpoint,
|
||||
e.options.UseHTTP2,
|
||||
)
|
||||
if err != nil {
|
||||
e.logger.InfoContext(e.ctx, fmt.Errorf("Failed to connect tunnel: %v", err))
|
||||
timer.Reset(e.options.ReconnectDelay)
|
||||
select {
|
||||
case <-e.ctx.Done():
|
||||
return
|
||||
case <-timer.C:
|
||||
}
|
||||
continue
|
||||
}
|
||||
if rsp.StatusCode != 200 {
|
||||
e.logger.InfoContext(e.ctx, fmt.Errorf("Tunnel connection failed: %s", rsp.Status))
|
||||
ipConn.Close()
|
||||
if udpConn != nil {
|
||||
udpConn.Close()
|
||||
}
|
||||
if tr != nil {
|
||||
tr.Close()
|
||||
}
|
||||
timer.Reset(e.options.ReconnectDelay)
|
||||
select {
|
||||
case <-e.ctx.Done():
|
||||
return
|
||||
case <-timer.C:
|
||||
}
|
||||
continue
|
||||
}
|
||||
e.logger.InfoContext(e.ctx, "Connected to MASQUE server")
|
||||
errChan := make(chan error, 2)
|
||||
go func() {
|
||||
for {
|
||||
buf := packetBufferPool.Get()
|
||||
n, err := e.tunnelDevice.ReadPacket(buf)
|
||||
if err != nil {
|
||||
packetBufferPool.Put(buf)
|
||||
errChan <- fmt.Errorf("failed to read from TUN device: %w", err)
|
||||
return
|
||||
}
|
||||
icmp, err := ipConn.WritePacket(buf[:n])
|
||||
if err != nil {
|
||||
packetBufferPool.Put(buf)
|
||||
if errors.As(err, new(*connectip.CloseError)) {
|
||||
errChan <- fmt.Errorf("connection closed while writing to IP connection: %w", err)
|
||||
return
|
||||
}
|
||||
e.logger.InfoContext(e.ctx, fmt.Errorf("Error writing to IP connection: %v, continuing...", err))
|
||||
continue
|
||||
}
|
||||
packetBufferPool.Put(buf)
|
||||
if len(icmp) > 0 {
|
||||
if err := e.tunnelDevice.WritePacket(icmp); err != nil {
|
||||
if errors.As(err, new(*connectip.CloseError)) {
|
||||
errChan <- fmt.Errorf("connection closed while writing ICMP to TUN device: %w", err)
|
||||
return
|
||||
}
|
||||
e.logger.InfoContext(e.ctx, fmt.Errorf("Error writing ICMP to TUN device: %v, continuing...", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
buf := packetBufferPool.Get()
|
||||
defer packetBufferPool.Put(buf)
|
||||
for {
|
||||
n, err := ipConn.ReadPacket(buf, true)
|
||||
if err != nil {
|
||||
if e.options.UseHTTP2 {
|
||||
errChan <- fmt.Errorf("connection closed while reading from IP connection: %w", err)
|
||||
return
|
||||
}
|
||||
if errors.As(err, new(*connectip.CloseError)) {
|
||||
errChan <- fmt.Errorf("connection closed while reading from IP connection: %w", err)
|
||||
return
|
||||
}
|
||||
e.logger.InfoContext(e.ctx, fmt.Errorf("Error reading from IP connection: %v, continuing...", err))
|
||||
continue
|
||||
}
|
||||
if err := e.tunnelDevice.WritePacket(buf[:n]); err != nil {
|
||||
errChan <- fmt.Errorf("failed to write to TUN device: %w", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = <-errChan
|
||||
e.logger.InfoContext(e.ctx, fmt.Errorf("Tunnel connection lost: %v. Reconnecting...", err))
|
||||
ipConn.Close()
|
||||
if udpConn != nil {
|
||||
udpConn.Close()
|
||||
}
|
||||
if tr != nil {
|
||||
tr.Close()
|
||||
}
|
||||
timer.Reset(e.options.ReconnectDelay)
|
||||
select {
|
||||
case <-e.ctx.Done():
|
||||
return
|
||||
case <-timer.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user