mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-03 20:07:38 +03:00
socks: Add custom udp listener
This commit is contained in:
parent
ea82ac275f
commit
d39c2c2fdd
2 changed files with 171 additions and 2 deletions
|
@ -25,6 +25,10 @@ type HandlerEx interface {
|
||||||
N.UDPConnectionHandlerEx
|
N.UDPConnectionHandlerEx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PacketListener interface {
|
||||||
|
ListenPacket(listenConfig net.ListenConfig, ctx context.Context, network string, address string) (net.PacketConn, error)
|
||||||
|
}
|
||||||
|
|
||||||
func ClientHandshake4(conn io.ReadWriter, command byte, destination M.Socksaddr, username string) (socks4.Response, error) {
|
func ClientHandshake4(conn io.ReadWriter, command byte, destination M.Socksaddr, username string) (socks4.Response, error) {
|
||||||
err := socks4.WriteRequest(conn, socks4.Request{
|
err := socks4.WriteRequest(conn, socks4.Request{
|
||||||
Command: command,
|
Command: command,
|
||||||
|
@ -121,6 +125,8 @@ func HandleConnectionEx(
|
||||||
ctx context.Context, conn net.Conn, reader *std_bufio.Reader,
|
ctx context.Context, conn net.Conn, reader *std_bufio.Reader,
|
||||||
authenticator *auth.Authenticator,
|
authenticator *auth.Authenticator,
|
||||||
handler HandlerEx,
|
handler HandlerEx,
|
||||||
|
packetListener PacketListener,
|
||||||
|
// resolver TorResolver,
|
||||||
source M.Socksaddr,
|
source M.Socksaddr,
|
||||||
onClose N.CloseHandlerFunc,
|
onClose N.CloseHandlerFunc,
|
||||||
) error {
|
) error {
|
||||||
|
@ -148,6 +154,11 @@ func HandleConnectionEx(
|
||||||
}
|
}
|
||||||
handler.NewConnectionEx(auth.ContextWithUser(ctx, request.Username), NewLazyConn(conn, version), source, request.Destination, onClose)
|
handler.NewConnectionEx(auth.ContextWithUser(ctx, request.Username), NewLazyConn(conn, version), source, request.Destination, onClose)
|
||||||
return nil
|
return nil
|
||||||
|
/*case CommandTorResolve, CommandTorResolvePTR:
|
||||||
|
if resolver == nil {
|
||||||
|
return E.New("socks4: torsocks: commands not implemented")
|
||||||
|
}
|
||||||
|
return handleTorSocks4(ctx, conn, request, resolver)*/
|
||||||
default:
|
default:
|
||||||
err = socks4.WriteResponse(conn, socks4.Response{
|
err = socks4.WriteResponse(conn, socks4.Response{
|
||||||
ReplyCode: socks4.ReplyCodeRejectedOrFailed,
|
ReplyCode: socks4.ReplyCodeRejectedOrFailed,
|
||||||
|
@ -214,8 +225,15 @@ func HandleConnectionEx(
|
||||||
handler.NewConnectionEx(ctx, NewLazyConn(conn, version), source, request.Destination, onClose)
|
handler.NewConnectionEx(ctx, NewLazyConn(conn, version), source, request.Destination, onClose)
|
||||||
return nil
|
return nil
|
||||||
case socks5.CommandUDPAssociate:
|
case socks5.CommandUDPAssociate:
|
||||||
var udpConn *net.UDPConn
|
var (
|
||||||
udpConn, err = net.ListenUDP(M.NetworkFromNetAddr("udp", M.AddrFromNet(conn.LocalAddr())), net.UDPAddrFromAddrPort(netip.AddrPortFrom(M.AddrFromNet(conn.LocalAddr()), 0)))
|
listenConfig net.ListenConfig
|
||||||
|
udpConn net.PacketConn
|
||||||
|
)
|
||||||
|
if packetListener != nil {
|
||||||
|
udpConn, err = packetListener.ListenPacket(listenConfig, ctx, M.NetworkFromNetAddr("udp", M.AddrFromNet(conn.LocalAddr())), M.SocksaddrFrom(M.AddrFromNet(conn.LocalAddr()), 0).String())
|
||||||
|
} else {
|
||||||
|
udpConn, err = listenConfig.ListenPacket(ctx, M.NetworkFromNetAddr("udp", M.AddrFromNet(conn.LocalAddr())), M.SocksaddrFrom(M.AddrFromNet(conn.LocalAddr()), 0).String())
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "socks5: listen udp")
|
return E.Cause(err, "socks5: listen udp")
|
||||||
}
|
}
|
||||||
|
@ -236,6 +254,11 @@ func HandleConnectionEx(
|
||||||
socksPacketConn = bufio.NewCachedPacketConn(socksPacketConn, firstPacket, destination)
|
socksPacketConn = bufio.NewCachedPacketConn(socksPacketConn, firstPacket, destination)
|
||||||
handler.NewPacketConnectionEx(ctx, socksPacketConn, source, destination, onClose)
|
handler.NewPacketConnectionEx(ctx, socksPacketConn, source, destination, onClose)
|
||||||
return nil
|
return nil
|
||||||
|
/*case CommandTorResolve, CommandTorResolvePTR:
|
||||||
|
if resolver == nil {
|
||||||
|
return E.New("socks4: torsocks: commands not implemented")
|
||||||
|
}
|
||||||
|
return handleTorSocks5(ctx, conn, request, resolver)*/
|
||||||
default:
|
default:
|
||||||
err = socks5.WriteResponse(conn, socks5.Response{
|
err = socks5.WriteResponse(conn, socks5.Response{
|
||||||
ReplyCode: socks5.ReplyCodeUnsupported,
|
ReplyCode: socks5.ReplyCodeUnsupported,
|
||||||
|
|
146
protocol/socks/handshake_tor.go
Normal file
146
protocol/socks/handshake_tor.go
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
package socks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
"github.com/sagernet/sing/protocol/socks/socks4"
|
||||||
|
"github.com/sagernet/sing/protocol/socks/socks5"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CommandTorResolve byte = 0xF0
|
||||||
|
CommandTorResolvePTR byte = 0xF1
|
||||||
|
)
|
||||||
|
|
||||||
|
type TorResolver interface {
|
||||||
|
LookupIP(ctx context.Context, host string) (netip.Addr, error)
|
||||||
|
LookupPTR(ctx context.Context, addr netip.Addr) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleTorSocks4(ctx context.Context, conn net.Conn, request socks4.Request, resolver TorResolver) error {
|
||||||
|
switch request.Command {
|
||||||
|
case CommandTorResolve:
|
||||||
|
if !request.Destination.IsFqdn() {
|
||||||
|
return E.New("socks4: torsocks: invalid destination")
|
||||||
|
}
|
||||||
|
ipAddr, err := resolver.LookupIP(ctx, request.Destination.Fqdn)
|
||||||
|
if err != nil {
|
||||||
|
err = socks4.WriteResponse(conn, socks4.Response{
|
||||||
|
ReplyCode: socks4.ReplyCodeRejectedOrFailed,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return E.Cause(err, "socks4: torsocks: lookup failed for domain: ", request.Destination.Fqdn)
|
||||||
|
}
|
||||||
|
err = socks4.WriteResponse(conn, socks4.Response{
|
||||||
|
ReplyCode: socks4.ReplyCodeGranted,
|
||||||
|
Destination: M.SocksaddrFrom(ipAddr, 0),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "socks4: torsocks: write response")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case CommandTorResolvePTR:
|
||||||
|
var ipAddr netip.Addr
|
||||||
|
if request.Destination.IsIP() {
|
||||||
|
ipAddr = request.Destination.Addr
|
||||||
|
} else if strings.HasSuffix(request.Destination.Fqdn, ".in-addr.arpa") {
|
||||||
|
ipAddr, _ = netip.ParseAddr(request.Destination.Fqdn[:len(request.Destination.Fqdn)-len(".in-addr.arpa")])
|
||||||
|
} else if strings.HasSuffix(request.Destination.Fqdn, ".ip6.arpa") {
|
||||||
|
ipAddr, _ = netip.ParseAddr(strings.ReplaceAll(request.Destination.Fqdn[:len(request.Destination.Fqdn)-len(".ip6.arpa")], ".", ":"))
|
||||||
|
}
|
||||||
|
if !ipAddr.IsValid() {
|
||||||
|
return E.New("socks4: torsocks: invalid destination")
|
||||||
|
}
|
||||||
|
host, err := resolver.LookupPTR(ctx, ipAddr)
|
||||||
|
if err != nil {
|
||||||
|
err = socks4.WriteResponse(conn, socks4.Response{
|
||||||
|
ReplyCode: socks4.ReplyCodeRejectedOrFailed,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return E.Cause(err, "socks4: torsocks: lookup PTR failed for ip: ", ipAddr)
|
||||||
|
}
|
||||||
|
err = socks4.WriteResponse(conn, socks4.Response{
|
||||||
|
ReplyCode: socks4.ReplyCodeGranted,
|
||||||
|
Destination: M.Socksaddr{
|
||||||
|
Fqdn: host,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "socks4: torsocks: write response")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleTorSocks5(ctx context.Context, conn net.Conn, request socks5.Request, resolver TorResolver) error {
|
||||||
|
switch request.Command {
|
||||||
|
case CommandTorResolve:
|
||||||
|
if !request.Destination.IsFqdn() {
|
||||||
|
return E.New("socks5: torsocks: invalid destination")
|
||||||
|
}
|
||||||
|
ipAddr, err := resolver.LookupIP(ctx, request.Destination.Fqdn)
|
||||||
|
if err != nil {
|
||||||
|
err = socks5.WriteResponse(conn, socks5.Response{
|
||||||
|
ReplyCode: socks5.ReplyCodeFailure,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return E.Cause(err, "socks5: torsocks: lookup failed for domain: ", request.Destination.Fqdn)
|
||||||
|
}
|
||||||
|
err = socks5.WriteResponse(conn, socks5.Response{
|
||||||
|
ReplyCode: socks5.ReplyCodeSuccess,
|
||||||
|
Bind: M.SocksaddrFrom(ipAddr, 0),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "socks5: torsocks: write response")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case CommandTorResolvePTR:
|
||||||
|
var ipAddr netip.Addr
|
||||||
|
if request.Destination.IsIP() {
|
||||||
|
ipAddr = request.Destination.Addr
|
||||||
|
} else if strings.HasSuffix(request.Destination.Fqdn, ".in-addr.arpa") {
|
||||||
|
ipAddr, _ = netip.ParseAddr(request.Destination.Fqdn[:len(request.Destination.Fqdn)-len(".in-addr.arpa")])
|
||||||
|
} else if strings.HasSuffix(request.Destination.Fqdn, ".ip6.arpa") {
|
||||||
|
ipAddr, _ = netip.ParseAddr(strings.ReplaceAll(request.Destination.Fqdn[:len(request.Destination.Fqdn)-len(".ip6.arpa")], ".", ":"))
|
||||||
|
}
|
||||||
|
if !ipAddr.IsValid() {
|
||||||
|
return E.New("socks5: torsocks: invalid destination")
|
||||||
|
}
|
||||||
|
host, err := resolver.LookupPTR(ctx, ipAddr)
|
||||||
|
if err != nil {
|
||||||
|
err = socks5.WriteResponse(conn, socks5.Response{
|
||||||
|
ReplyCode: socks5.ReplyCodeFailure,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return E.Cause(err, "socks5: torsocks: lookup PTR failed for ip: ", ipAddr)
|
||||||
|
}
|
||||||
|
err = socks5.WriteResponse(conn, socks5.Response{
|
||||||
|
ReplyCode: socks5.ReplyCodeSuccess,
|
||||||
|
Bind: M.Socksaddr{
|
||||||
|
Fqdn: host,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "socks5: torsocks: write response")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return os.ErrInvalid
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue