Add redir tproxy and dns inbound

This commit is contained in:
世界 2022-07-15 08:42:02 +08:00
parent e13b72afca
commit 2c2eb31e18
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
19 changed files with 723 additions and 20 deletions

View file

@ -17,6 +17,10 @@ type PacketHandler interface {
NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata InboundContext) error
}
type OOBPacketHandler interface {
NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, oob []byte, metadata InboundContext) error
}
type PacketConnectionHandler interface {
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
}

View file

@ -0,0 +1,37 @@
package redir
import (
"net"
"net/netip"
"syscall"
M "github.com/sagernet/sing/common/metadata"
)
func GetOriginalDestination(conn net.Conn) (destination netip.AddrPort, err error) {
rawConn, err := conn.(syscall.Conn).SyscallConn()
if err != nil {
return
}
var rawFd uintptr
err = rawConn.Control(func(fd uintptr) {
rawFd = fd
})
if err != nil {
return
}
const SO_ORIGINAL_DST = 80
if conn.RemoteAddr().(*net.TCPAddr).IP.To4() != nil {
raw, err := syscall.GetsockoptIPv6Mreq(int(rawFd), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
if err != nil {
return netip.AddrPort{}, err
}
return netip.AddrPortFrom(M.AddrFromIP(raw.Multiaddr[4:8]), uint16(raw.Multiaddr[2])<<8+uint16(raw.Multiaddr[3])), nil
} else {
raw, err := syscall.GetsockoptIPv6MTUInfo(int(rawFd), syscall.IPPROTO_IPV6, SO_ORIGINAL_DST)
if err != nil {
return netip.AddrPort{}, err
}
return netip.AddrPortFrom(M.AddrFromIP(raw.Addr.Addr[:]), raw.Addr.Port), nil
}
}

View file

@ -0,0 +1,13 @@
//go:build !linux
package redir
import (
"net"
"net/netip"
"os"
)
func GetOriginalDestination(conn net.Conn) (destination netip.AddrPort, err error) {
return netip.AddrPort{}, os.ErrInvalid
}

View file

@ -0,0 +1,132 @@
package redir
import (
"encoding/binary"
"net"
"net/netip"
"os"
"strconv"
"syscall"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata"
"golang.org/x/sys/unix"
)
func TProxy(fd uintptr, isIPv6 bool) error {
err := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)
if err != nil {
return err
}
if isIPv6 {
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1)
}
return err
}
func TProxyUDP(fd uintptr, isIPv6 bool) error {
err := syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1)
if err != nil {
return err
}
if isIPv6 {
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1)
if err != nil {
return err
}
}
return nil
}
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
controlMessages, err := unix.ParseSocketControlMessage(oob)
if err != nil {
return netip.AddrPort{}, err
}
for _, message := range controlMessages {
if message.Header.Level == unix.SOL_IP && message.Header.Type == unix.IP_RECVORIGDSTADDR {
return netip.AddrPortFrom(M.AddrFromIP(message.Data[4:8]), binary.BigEndian.Uint16(message.Data[2:4])), nil
} else if message.Header.Level == unix.SOL_IPV6 && message.Header.Type == unix.IPV6_RECVORIGDSTADDR {
return netip.AddrPortFrom(M.AddrFromIP(message.Data[8:24]), binary.BigEndian.Uint16(message.Data[2:4])), nil
}
}
return netip.AddrPort{}, E.New("not found")
}
func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
rSockAddr, err := udpAddrToSockAddr(rAddr)
if err != nil {
return nil, err
}
lSockAddr, err := udpAddrToSockAddr(lAddr)
if err != nil {
return nil, err
}
fd, err := syscall.Socket(udpAddrFamily(lAddr, rAddr), syscall.SOCK_DGRAM, 0)
if err != nil {
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.Bind(fd, lSockAddr); err != nil {
syscall.Close(fd)
return nil, err
}
if err = syscall.Connect(fd, rSockAddr); err != nil {
syscall.Close(fd)
return nil, err
}
fdFile := os.NewFile(uintptr(fd), F.ToString("net-udp-dial-", rAddr))
defer fdFile.Close()
c, err := net.FileConn(fdFile)
if err != nil {
syscall.Close(fd)
return nil, err
}
return c.(*net.UDPConn), nil
}
func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
switch {
case addr.IP.To4() != nil:
ip := [4]byte{}
copy(ip[:], addr.IP.To4())
return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil
default:
ip := [16]byte{}
copy(ip[:], addr.IP.To16())
zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
if err != nil {
zoneID = 0
}
return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
}
}
func udpAddrFamily(lAddr, rAddr *net.UDPAddr) int {
if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
return syscall.AF_INET
}
return syscall.AF_INET6
}

View file

@ -0,0 +1,25 @@
//go:build !linux
package redir
import (
"net"
"net/netip"
"os"
)
func TProxy(fd uintptr, isIPv6 bool) error {
return os.ErrInvalid
}
func TProxyUDP(fd uintptr, isIPv6 bool) error {
return os.ErrInvalid
}
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
return netip.AddrPort{}, os.ErrInvalid
}
func DialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
return nil, os.ErrInvalid
}

View file

@ -8,4 +8,7 @@ const (
TypeMixed = "mixed"
TypeShadowsocks = "shadowsocks"
TypeTun = "tun"
TypeRedirect = "redirect"
TypeTProxy = "tproxy"
TypeDNS = "dns"
)

View file

@ -0,0 +1,44 @@
`dns` inbound is a DNS server.
### Structure
```json
{
"inbounds": [
{
"type": "dns",
"tag": "dns-in",
"listen": "::",
"listen_port": 5353,
"network": "udp"
}
]
}
```
!!! note ""
There are no outbound connections by the DNS inbound, all requests are handled internally.
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
### DNS Fields
#### network
Listen network, one of `tcp` `udp`.
Both if empty.

View file

@ -15,12 +15,15 @@
| Type | Format |
|---------------|------------------------------|
| `tun` | [Tun](./tun) |
| `direct` | [Direct](./direct) |
| `mixed` | [Mixed](./mixed) |
| `socks` | [Socks](./socks) |
| `http` | [HTTP](./http) |
| `shadowsocks` | [Shadowsocks](./shadowsocks) |
| `tun` | [Tun](./tun) |
| `redirect` | [Redirect](./redirect) |
| `tproxy` | [TProxy](./tproxy) |
| `dns` | [DNS](./dns) |
#### tag

View file

@ -0,0 +1,61 @@
`redirect` inbound is a linux Redirect server.
### Structure
```json
{
"inbounds": [
{
"type": "redirect",
"tag": "redirect-in",
"listen": "::",
"listen_port": 5353,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300
}
]
}
```
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### sniff
Enable sniffing.
Reads domain names for routing, supports HTTP TLS for TCP, QUIC for UDP.
This does not break zero copy, like splice.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### udp_timeout
UDP NAT expiration time in seconds, default is 300 (5 minutes).

View file

@ -0,0 +1,71 @@
`tproxy` inbound is a linux TProxy server.
### Structure
```json
{
"inbounds": [
{
"type": "tproxy",
"tag": "tproxy-in",
"listen": "::",
"listen_port": 5353,
"sniff": false,
"sniff_override_destination": false,
"domain_strategy": "prefer_ipv6",
"udp_timeout": 300,
"network": "udp"
}
]
}
```
### Listen Fields
#### listen
==Required==
Listen address.
#### listen_port
==Required==
Listen port.
#### sniff
Enable sniffing.
Reads domain names for routing, supports HTTP TLS for TCP, QUIC for UDP.
This does not break zero copy, like splice.
#### sniff_override_destination
Override the connection destination address with the sniffed domain.
If the domain name is invalid (like tor), this will not work.
#### domain_strategy
One of `prefer_ipv4` `prefer_ipv6` `ipv4_only` `ipv6_only`.
If set, the requested domain name will be resolved to IP before routing.
If `sniff_override_destination` is in effect, its value will be taken as a fallback.
#### udp_timeout
UDP NAT expiration time in seconds, default is 300 (5 minutes).
### TProxy Fields
#### network
Listen network, one of `tcp` `udp`.
Both if empty.

View file

@ -28,6 +28,12 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
return NewShadowsocks(ctx, router, logger, options.Tag, options.ShadowsocksOptions)
case C.TypeTun:
return NewTun(ctx, router, logger, options.Tag, options.TunOptions)
case C.TypeRedirect:
return NewRedirect(ctx, router, logger, options.Tag, options.RedirectOptions), nil
case C.TypeTProxy:
return NewTProxy(ctx, router, logger, options.Tag, options.TProxyOptions), nil
case C.TypeDNS:
return NewDNS(ctx, router, logger, options.Tag, options.DNSOptions), nil
default:
return nil, E.New("unknown inbound type: ", options.Type)
}

View file

@ -26,16 +26,17 @@ import (
var _ adapter.Inbound = (*myInboundAdapter)(nil)
type myInboundAdapter struct {
protocol string
network []string
ctx context.Context
router adapter.Router
logger log.ContextLogger
tag string
listenOptions option.ListenOptions
connHandler adapter.ConnectionHandler
packetHandler adapter.PacketHandler
packetUpstream any
protocol string
network []string
ctx context.Context
router adapter.Router
logger log.ContextLogger
tag string
listenOptions option.ListenOptions
connHandler adapter.ConnectionHandler
packetHandler adapter.PacketHandler
oobPacketHandler adapter.OOBPacketHandler
packetUpstream any
// http mixed
@ -85,12 +86,20 @@ func (a *myInboundAdapter) Start() error {
a.packetForce6 = M.SocksaddrFromNet(udpConn.LocalAddr()).Addr.Is6()
a.packetOutboundClosed = make(chan struct{})
a.packetOutbound = make(chan *myInboundPacket)
if _, threadUnsafeHandler := common.Cast[N.ThreadUnsafeWriter](a.packetUpstream); !threadUnsafeHandler {
go a.loopUDPIn()
if a.oobPacketHandler != nil {
if _, threadUnsafeHandler := common.Cast[N.ThreadUnsafeWriter](a.packetUpstream); !threadUnsafeHandler {
go a.loopUDPOOBIn()
} else {
go a.loopUDPOOBInThreadSafe()
}
} else {
go a.loopUDPInThreadSafe()
if _, threadUnsafeHandler := common.Cast[N.ThreadUnsafeWriter](a.packetUpstream); !threadUnsafeHandler {
go a.loopUDPIn()
} else {
go a.loopUDPInThreadSafe()
}
go a.loopUDPOut()
}
go a.loopUDPOut()
a.logger.Info("udp server started at ", udpConn.LocalAddr())
}
if a.setSystemProxy {
@ -194,6 +203,37 @@ func (a *myInboundAdapter) loopUDPIn() {
}
}
func (a *myInboundAdapter) loopUDPOOBIn() {
defer close(a.packetOutboundClosed)
_buffer := buf.StackNewPacket()
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
defer buffer.Release()
buffer.IncRef()
defer buffer.DecRef()
packetService := (*myInboundPacketAdapter)(a)
oob := make([]byte, 1024)
for {
buffer.Reset()
n, oobN, _, addr, err := a.udpConn.ReadMsgUDPAddrPort(buffer.FreeBytes(), oob)
if err != nil {
return
}
buffer.Truncate(n)
var metadata adapter.InboundContext
metadata.Inbound = a.tag
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = C.NetworkUDP
metadata.Source = M.SocksaddrFromNetIP(addr)
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
if err != nil {
a.newError(E.Cause(err, "process packet from ", metadata.Source))
}
}
}
func (a *myInboundAdapter) loopUDPInThreadSafe() {
defer close(a.packetOutboundClosed)
packetService := (*myInboundPacketAdapter)(a)
@ -220,6 +260,33 @@ func (a *myInboundAdapter) loopUDPInThreadSafe() {
}
}
func (a *myInboundAdapter) loopUDPOOBInThreadSafe() {
defer close(a.packetOutboundClosed)
packetService := (*myInboundPacketAdapter)(a)
oob := make([]byte, 1024)
for {
buffer := buf.NewPacket()
n, oobN, _, addr, err := a.udpConn.ReadMsgUDPAddrPort(buffer.FreeBytes(), oob)
if err != nil {
buffer.Release()
return
}
buffer.Truncate(n)
var metadata adapter.InboundContext
metadata.Inbound = a.tag
metadata.SniffEnabled = a.listenOptions.SniffEnabled
metadata.SniffOverrideDestination = a.listenOptions.SniffOverrideDestination
metadata.DomainStrategy = dns.DomainStrategy(a.listenOptions.DomainStrategy)
metadata.Network = C.NetworkUDP
metadata.Source = M.SocksaddrFromNetIP(addr)
err = a.oobPacketHandler.NewPacket(a.ctx, packetService, buffer, oob[:oobN], metadata)
if err != nil {
buffer.Release()
a.newError(E.Cause(err, "process packet from ", metadata.Source))
}
}
}
func (a *myInboundAdapter) loopUDPOut() {
for {
select {

View file

@ -46,7 +46,13 @@ func NewDirect(ctx context.Context, router adapter.Router, logger log.ContextLog
inbound.overrideOption = 3
inbound.overrideDestination = M.Socksaddr{Port: options.OverridePort}
}
inbound.udpNat = udpnat.New[netip.AddrPort](options.UDPTimeout, inbound.upstreamContextHandler())
var udpTimeout int64
if options.UDPTimeout != 0 {
udpTimeout = options.UDPTimeout
} else {
udpTimeout = 300
}
inbound.udpNat = udpnat.New[netip.AddrPort](udpTimeout, inbound.upstreamContextHandler())
inbound.connHandler = inbound
inbound.packetHandler = inbound
inbound.packetUpstream = inbound.udpNat
@ -79,6 +85,8 @@ func (d *Direct) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.B
case 3:
metadata.Destination.Port = d.overrideDestination.Port
}
d.udpNat.NewPacketDirect(adapter.WithContext(log.ContextWithNewID(ctx), &metadata), metadata.Source.AddrPort(), conn, buffer, adapter.UpstreamMetadata(metadata))
d.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), natConn
})
return nil
}

View file

@ -5,16 +5,59 @@ import (
"encoding/binary"
"io"
"net"
"net/netip"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/udpnat"
"golang.org/x/net/dns/dnsmessage"
)
type DNS struct {
myInboundAdapter
udpNat *udpnat.Service[netip.AddrPort]
}
func NewDNS(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DNSInboundOptions) *DNS {
dns := &DNS{
myInboundAdapter: myInboundAdapter{
protocol: C.TypeTProxy,
network: options.Network.Build(),
ctx: ctx,
router: router,
logger: logger,
tag: tag,
listenOptions: options.ListenOptions,
},
}
dns.connHandler = dns
dns.packetHandler = dns
dns.udpNat = udpnat.New[netip.AddrPort](10, adapter.NewUpstreamContextHandler(nil, dns.newPacketConnection, dns))
dns.packetUpstream = dns.udpNat
return dns
}
func (d *DNS) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
return NewDNSConnection(ctx, d.router, d.logger, conn, metadata)
}
func (d *DNS) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, metadata adapter.InboundContext) error {
d.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), natConn
})
return nil
}
func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
return NewDNSPacketConnection(ctx, d.router, d.logger, conn, metadata)
}
func NewDNSConnection(ctx context.Context, router adapter.Router, logger log.ContextLogger, conn net.Conn, metadata adapter.InboundContext) error {
ctx = adapter.WithContext(ctx, &metadata)
_buffer := buf.StackNewSize(1024)

43
inbound/redirect.go Normal file
View file

@ -0,0 +1,43 @@
package inbound
import (
"context"
"net"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/redir"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
)
type Redirect struct {
myInboundAdapter
}
func NewRedirect(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.RedirectInboundOptions) *Redirect {
redirect := &Redirect{
myInboundAdapter{
protocol: C.TypeRedirect,
network: []string{C.NetworkTCP},
ctx: ctx,
router: router,
logger: logger,
tag: tag,
listenOptions: options.ListenOptions,
},
}
redirect.connHandler = redirect
return redirect
}
func (r *Redirect) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
destination, err := redir.GetOriginalDestination(conn)
if err != nil {
return E.Cause(err, "get redirect destination")
}
metadata.Destination = M.SocksaddrFromNetIP(destination)
return r.newConnection(ctx, conn, metadata)
}

108
inbound/tproxy.go Normal file
View file

@ -0,0 +1,108 @@
package inbound
import (
"context"
"net"
"net/netip"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/redir"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/udpnat"
)
type TProxy struct {
myInboundAdapter
udpNat *udpnat.Service[netip.AddrPort]
}
func NewTProxy(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TProxyInboundOptions) *TProxy {
tproxy := &TProxy{
myInboundAdapter: myInboundAdapter{
protocol: C.TypeTProxy,
network: options.Network.Build(),
ctx: ctx,
router: router,
logger: logger,
tag: tag,
listenOptions: options.ListenOptions,
},
}
var udpTimeout int64
if options.UDPTimeout != 0 {
udpTimeout = options.UDPTimeout
} else {
udpTimeout = 300
}
tproxy.connHandler = tproxy
tproxy.oobPacketHandler = tproxy
tproxy.udpNat = udpnat.New[netip.AddrPort](udpTimeout, tproxy.upstreamContextHandler())
tproxy.packetUpstream = tproxy.udpNat
return tproxy
}
func (t *TProxy) Start() error {
err := t.myInboundAdapter.Start()
if err != nil {
return err
}
if t.tcpListener != nil {
tcpFd, err := common.GetFileDescriptor(t.tcpListener)
if err != nil {
return err
}
err = redir.TProxy(tcpFd, M.SocksaddrFromNet(t.tcpListener.Addr()).Addr.Is6())
if err != nil {
return E.Cause(err, "configure tproxy TCP listener")
}
}
if t.udpConn != nil {
udpFd, err := common.GetFileDescriptor(t.udpConn)
if err != nil {
return err
}
err = redir.TProxyUDP(udpFd, M.SocksaddrFromNet(t.udpConn.LocalAddr()).Addr.Is6())
if err != nil {
return E.Cause(err, "configure tproxy UDP listener")
}
}
return nil
}
func (t *TProxy) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
metadata.Destination = M.SocksaddrFromNet(conn.LocalAddr())
return t.newConnection(ctx, conn, metadata)
}
func (t *TProxy) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.Buffer, oob []byte, metadata adapter.InboundContext) error {
destination, err := redir.GetOriginalDestinationFromOOB(oob)
if err != nil {
return E.Cause(err, "get tproxy destination")
}
metadata.Destination = M.SocksaddrFromNetIP(destination)
t.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) {
return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{natConn}
})
return nil
}
type tproxyPacketWriter struct {
source N.PacketConn
}
func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
udpConn, err := redir.DialUDP(destination.UDPAddr(), M.SocksaddrFromNet(w.source.LocalAddr()).UDPAddr())
if err != nil {
return E.Cause(err, "tproxy udp write back")
}
defer udpConn.Close()
return common.Error(udpConn.Write(buffer.Bytes()))
}

View file

@ -40,12 +40,15 @@ nav:
- DNS Rule: configuration/dns/rule.md
- Inbound:
- configuration/inbound/index.md
- Tun: configuration/inbound/tun.md
- Direct: configuration/inbound/direct.md
- Mixed: configuration/inbound/mixed.md
- Socks: configuration/inbound/socks.md
- HTTP: configuration/inbound/http.md
- Shadowsocks: configuration/inbound/shadowsocks.md
- Tun: configuration/inbound/tun.md
- Redirect: configuration/inbound/redirect.md
- TProxy: configuration/inbound/tproxy.md
- DNS: configuration/inbound/dns.md
- Outbound:
- configuration/outbound/index.md
- Direct: configuration/outbound/direct.md

View file

@ -18,6 +18,9 @@ type _Inbound struct {
MixedOptions HTTPMixedInboundOptions `json:"-"`
ShadowsocksOptions ShadowsocksInboundOptions `json:"-"`
TunOptions TunInboundOptions `json:"-"`
RedirectOptions RedirectInboundOptions `json:"-"`
TProxyOptions TProxyInboundOptions `json:"-"`
DNSOptions DNSInboundOptions `json:"-"`
}
type Inbound _Inbound
@ -30,7 +33,10 @@ func (h Inbound) Equals(other Inbound) bool {
h.HTTPOptions.Equals(other.HTTPOptions) &&
h.MixedOptions.Equals(other.MixedOptions) &&
h.ShadowsocksOptions.Equals(other.ShadowsocksOptions) &&
h.TunOptions == other.TunOptions
h.TunOptions == other.TunOptions &&
h.RedirectOptions == other.RedirectOptions &&
h.TProxyOptions == other.TProxyOptions &&
h.DNSOptions == other.DNSOptions
}
func (h Inbound) MarshalJSON() ([]byte, error) {
@ -48,6 +54,12 @@ func (h Inbound) MarshalJSON() ([]byte, error) {
v = h.ShadowsocksOptions
case C.TypeTun:
v = h.TunOptions
case C.TypeRedirect:
v = h.RedirectOptions
case C.TypeTProxy:
v = h.TProxyOptions
case C.TypeDNS:
v = h.DNSOptions
default:
return nil, E.New("unknown inbound type: ", h.Type)
}
@ -73,6 +85,12 @@ func (h *Inbound) UnmarshalJSON(bytes []byte) error {
v = &h.ShadowsocksOptions
case C.TypeTun:
v = &h.TunOptions
case C.TypeRedirect:
v = &h.RedirectOptions
case C.TypeTProxy:
v = &h.TProxyOptions
case C.TypeDNS:
v = &h.DNSOptions
default:
return nil
}
@ -164,3 +182,17 @@ type TunInboundOptions struct {
HijackDNS bool `json:"hijack_dns,omitempty"`
InboundOptions
}
type RedirectInboundOptions struct {
ListenOptions
}
type TProxyInboundOptions struct {
ListenOptions
Network NetworkList `json:"network,omitempty"`
}
type DNSInboundOptions struct {
ListenOptions
Network NetworkList `json:"network,omitempty"`
}

View file

@ -13,13 +13,13 @@ import (
"testing"
"time"
"github.com/sagernet/sing-box/log"
F "github.com/sagernet/sing/common/format"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/sagernet/sing-box/log"
)
// kanged from clash