mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-21 09:36:34 -05:00
afb8385a7e
Turn off fake DNS for request sent from Routing and Freedom outbound. Fake DNS now only apply to DNS outbound. This is important for Android, where VPN service take over all system DNS traffic and pass it to core. "UseIp" option can be used in Freedom outbound to avoid getting fake IP and fail connection. Co-authored-by: loyalsoldier <10487845+Loyalsoldier@users.noreply.github.com>
192 lines
5.1 KiB
Go
192 lines
5.1 KiB
Go
// +build !confonly
|
|
|
|
package freedom
|
|
|
|
//go:generate go run github.com/v2fly/v2ray-core/v4/common/errors/errorgen
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
core "github.com/v2fly/v2ray-core/v4"
|
|
"github.com/v2fly/v2ray-core/v4/common"
|
|
"github.com/v2fly/v2ray-core/v4/common/buf"
|
|
"github.com/v2fly/v2ray-core/v4/common/dice"
|
|
"github.com/v2fly/v2ray-core/v4/common/net"
|
|
"github.com/v2fly/v2ray-core/v4/common/retry"
|
|
"github.com/v2fly/v2ray-core/v4/common/session"
|
|
"github.com/v2fly/v2ray-core/v4/common/signal"
|
|
"github.com/v2fly/v2ray-core/v4/common/task"
|
|
"github.com/v2fly/v2ray-core/v4/features/dns"
|
|
"github.com/v2fly/v2ray-core/v4/features/policy"
|
|
"github.com/v2fly/v2ray-core/v4/transport"
|
|
"github.com/v2fly/v2ray-core/v4/transport/internet"
|
|
)
|
|
|
|
func init() {
|
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
|
h := new(Handler)
|
|
if err := core.RequireFeatures(ctx, func(pm policy.Manager, d dns.Client) error {
|
|
return h.Init(config.(*Config), pm, d)
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
return h, nil
|
|
}))
|
|
}
|
|
|
|
// Handler handles Freedom connections.
|
|
type Handler struct {
|
|
policyManager policy.Manager
|
|
dns dns.Client
|
|
config *Config
|
|
}
|
|
|
|
// Init initializes the Handler with necessary parameters.
|
|
func (h *Handler) Init(config *Config, pm policy.Manager, d dns.Client) error {
|
|
h.config = config
|
|
h.policyManager = pm
|
|
h.dns = d
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *Handler) policy() policy.Session {
|
|
p := h.policyManager.ForLevel(h.config.UserLevel)
|
|
if h.config.Timeout > 0 && h.config.UserLevel == 0 {
|
|
p.Timeouts.ConnectionIdle = time.Duration(h.config.Timeout) * time.Second
|
|
}
|
|
return p
|
|
}
|
|
|
|
func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address {
|
|
var option dns.IPOption = dns.IPOption{
|
|
IPv4Enable: true,
|
|
IPv6Enable: true,
|
|
FakeEnable: false,
|
|
}
|
|
if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) {
|
|
option = dns.IPOption{
|
|
IPv4Enable: true,
|
|
IPv6Enable: false,
|
|
FakeEnable: false,
|
|
}
|
|
} else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) {
|
|
option = dns.IPOption{
|
|
IPv4Enable: false,
|
|
IPv6Enable: true,
|
|
FakeEnable: false,
|
|
}
|
|
}
|
|
|
|
ips, err := h.dns.LookupIP(domain, option)
|
|
if err != nil {
|
|
newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx))
|
|
}
|
|
if len(ips) == 0 {
|
|
return nil
|
|
}
|
|
return net.IPAddress(ips[dice.Roll(len(ips))])
|
|
}
|
|
|
|
func isValidAddress(addr *net.IPOrDomain) bool {
|
|
if addr == nil {
|
|
return false
|
|
}
|
|
|
|
a := addr.AsAddress()
|
|
return a != net.AnyIP
|
|
}
|
|
|
|
// Process implements proxy.Outbound.
|
|
func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error {
|
|
outbound := session.OutboundFromContext(ctx)
|
|
if outbound == nil || !outbound.Target.IsValid() {
|
|
return newError("target not specified.")
|
|
}
|
|
destination := outbound.Target
|
|
if h.config.DestinationOverride != nil {
|
|
server := h.config.DestinationOverride.Server
|
|
if isValidAddress(server.Address) {
|
|
destination.Address = server.Address.AsAddress()
|
|
}
|
|
if server.Port != 0 {
|
|
destination.Port = net.Port(server.Port)
|
|
}
|
|
}
|
|
newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx))
|
|
|
|
input := link.Reader
|
|
output := link.Writer
|
|
|
|
var conn internet.Connection
|
|
err := retry.ExponentialBackoff(5, 100).On(func() error {
|
|
dialDest := destination
|
|
if h.config.useIP() && dialDest.Address.Family().IsDomain() {
|
|
ip := h.resolveIP(ctx, dialDest.Address.Domain(), dialer.Address())
|
|
if ip != nil {
|
|
dialDest = net.Destination{
|
|
Network: dialDest.Network,
|
|
Address: ip,
|
|
Port: dialDest.Port,
|
|
}
|
|
newError("dialing to ", dialDest).WriteToLog(session.ExportIDToError(ctx))
|
|
}
|
|
}
|
|
|
|
rawConn, err := dialer.Dial(ctx, dialDest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
conn = rawConn
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return newError("failed to open connection to ", destination).Base(err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
plcy := h.policy()
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle)
|
|
|
|
requestDone := func() error {
|
|
defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
|
|
|
|
var writer buf.Writer
|
|
if destination.Network == net.Network_TCP {
|
|
writer = buf.NewWriter(conn)
|
|
} else {
|
|
writer = &buf.SequentialWriter{Writer: conn}
|
|
}
|
|
|
|
if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil {
|
|
return newError("failed to process request").Base(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
responseDone := func() error {
|
|
defer timer.SetTimeout(plcy.Timeouts.UplinkOnly)
|
|
|
|
var reader buf.Reader
|
|
if destination.Network == net.Network_TCP {
|
|
reader = buf.NewReader(conn)
|
|
} else {
|
|
reader = buf.NewPacketReader(conn)
|
|
}
|
|
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
|
|
return newError("failed to process response").Base(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil {
|
|
return newError("connection ends").Base(err)
|
|
}
|
|
|
|
return nil
|
|
}
|