mirror of
https://github.com/SagerNet/sing.git
synced 2025-03-31 10:27:39 +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
|
||||
}
|
||||
|
||||
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) {
|
||||
err := socks4.WriteRequest(conn, socks4.Request{
|
||||
Command: command,
|
||||
|
@ -121,6 +125,8 @@ func HandleConnectionEx(
|
|||
ctx context.Context, conn net.Conn, reader *std_bufio.Reader,
|
||||
authenticator *auth.Authenticator,
|
||||
handler HandlerEx,
|
||||
packetListener PacketListener,
|
||||
// resolver TorResolver,
|
||||
source M.Socksaddr,
|
||||
onClose N.CloseHandlerFunc,
|
||||
) error {
|
||||
|
@ -148,6 +154,11 @@ func HandleConnectionEx(
|
|||
}
|
||||
handler.NewConnectionEx(auth.ContextWithUser(ctx, request.Username), NewLazyConn(conn, version), source, request.Destination, onClose)
|
||||
return nil
|
||||
/*case CommandTorResolve, CommandTorResolvePTR:
|
||||
if resolver == nil {
|
||||
return E.New("socks4: torsocks: commands not implemented")
|
||||
}
|
||||
return handleTorSocks4(ctx, conn, request, resolver)*/
|
||||
default:
|
||||
err = socks4.WriteResponse(conn, socks4.Response{
|
||||
ReplyCode: socks4.ReplyCodeRejectedOrFailed,
|
||||
|
@ -214,8 +225,15 @@ func HandleConnectionEx(
|
|||
handler.NewConnectionEx(ctx, NewLazyConn(conn, version), source, request.Destination, onClose)
|
||||
return nil
|
||||
case socks5.CommandUDPAssociate:
|
||||
var udpConn *net.UDPConn
|
||||
udpConn, err = net.ListenUDP(M.NetworkFromNetAddr("udp", M.AddrFromNet(conn.LocalAddr())), net.UDPAddrFromAddrPort(netip.AddrPortFrom(M.AddrFromNet(conn.LocalAddr()), 0)))
|
||||
var (
|
||||
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 {
|
||||
return E.Cause(err, "socks5: listen udp")
|
||||
}
|
||||
|
@ -236,6 +254,11 @@ func HandleConnectionEx(
|
|||
socksPacketConn = bufio.NewCachedPacketConn(socksPacketConn, firstPacket, destination)
|
||||
handler.NewPacketConnectionEx(ctx, socksPacketConn, source, destination, onClose)
|
||||
return nil
|
||||
/*case CommandTorResolve, CommandTorResolvePTR:
|
||||
if resolver == nil {
|
||||
return E.New("socks4: torsocks: commands not implemented")
|
||||
}
|
||||
return handleTorSocks5(ctx, conn, request, resolver)*/
|
||||
default:
|
||||
err = socks5.WriteResponse(conn, socks5.Response{
|
||||
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