mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-03 03:47:38 +03:00
146 lines
4.4 KiB
Go
146 lines
4.4 KiB
Go
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
|
|
}
|
|
}
|