| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- package freedom
- //go:generate go run github.com/xtls/xray-core/common/errors/errorgen
- import (
- "context"
- "crypto/rand"
- "io"
- "math/big"
- "time"
- "github.com/xtls/xray-core/common"
- "github.com/xtls/xray-core/common/buf"
- "github.com/xtls/xray-core/common/dice"
- "github.com/xtls/xray-core/common/net"
- "github.com/xtls/xray-core/common/platform"
- "github.com/xtls/xray-core/common/retry"
- "github.com/xtls/xray-core/common/session"
- "github.com/xtls/xray-core/common/signal"
- "github.com/xtls/xray-core/common/task"
- "github.com/xtls/xray-core/core"
- "github.com/xtls/xray-core/features/dns"
- "github.com/xtls/xray-core/features/policy"
- "github.com/xtls/xray-core/features/stats"
- "github.com/xtls/xray-core/proxy"
- "github.com/xtls/xray-core/transport"
- "github.com/xtls/xray-core/transport/internet"
- "github.com/xtls/xray-core/transport/internet/stat"
- )
- var useSplice bool
- 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
- }))
- const defaultFlagValue = "NOT_DEFINED_AT_ALL"
- value := platform.NewEnvFlag(platform.UseFreedomSplice).GetValue(func() string { return defaultFlagValue })
- switch value {
- case "auto", "enable":
- useSplice = true
- }
- }
- // 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.")
- }
- outbound.Name = "freedom"
- inbound := session.InboundFromContext(ctx)
- if inbound != nil {
- inbound.SetCanSpliceCopy(1)
- }
- destination := outbound.Target
- UDPOverride := net.UDPDestination(nil, 0)
- if h.config.DestinationOverride != nil {
- server := h.config.DestinationOverride.Server
- if isValidAddress(server.Address) {
- destination.Address = server.Address.AsAddress()
- UDPOverride.Address = destination.Address
- }
- if server.Port != 0 {
- destination.Port = net.Port(server.Port)
- UDPOverride.Port = destination.Port
- }
- }
- input := link.Reader
- output := link.Writer
- var conn stat.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()
- newError("connection opened to ", destination, ", local endpoint ", conn.LocalAddr(), ", remote endpoint ", conn.RemoteAddr()).WriteToLog(session.ExportIDToError(ctx))
- var newCtx context.Context
- var newCancel context.CancelFunc
- if session.TimeoutOnlyFromContext(ctx) {
- newCtx, newCancel = context.WithCancel(context.Background())
- }
- plcy := h.policy()
- ctx, cancel := context.WithCancel(ctx)
- timer := signal.CancelAfterInactivity(ctx, func() {
- cancel()
- if newCancel != nil {
- newCancel()
- }
- }, plcy.Timeouts.ConnectionIdle)
- requestDone := func() error {
- defer timer.SetTimeout(plcy.Timeouts.DownlinkOnly)
- var writer buf.Writer
- if destination.Network == net.Network_TCP {
- if h.config.Fragment != nil {
- newError("FRAGMENT", h.config.Fragment.PacketsFrom, h.config.Fragment.PacketsTo, h.config.Fragment.LengthMin, h.config.Fragment.LengthMax,
- h.config.Fragment.IntervalMin, h.config.Fragment.IntervalMax).AtDebug().WriteToLog(session.ExportIDToError(ctx))
- writer = buf.NewWriter(&FragmentWriter{
- fragment: h.config.Fragment,
- writer: conn,
- })
- } else {
- writer = buf.NewWriter(conn)
- }
- } else {
- writer = NewPacketWriter(conn, h, ctx, UDPOverride)
- }
- 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)
- if destination.Network == net.Network_TCP {
- var writeConn net.Conn
- if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil && useSplice {
- writeConn = inbound.Conn
- }
- return proxy.CopyRawConnIfExist(ctx, conn, writeConn, link.Writer, timer)
- }
- reader := NewPacketReader(conn, UDPOverride)
- if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
- return newError("failed to process response").Base(err)
- }
- return nil
- }
- if newCtx != nil {
- ctx = newCtx
- }
- if err := task.Run(ctx, requestDone, task.OnSuccess(responseDone, task.Close(output))); err != nil {
- return newError("connection ends").Base(err)
- }
- return nil
- }
- func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
- iConn := conn
- statConn, ok := iConn.(*stat.CounterConnection)
- if ok {
- iConn = statConn.Connection
- }
- var counter stats.Counter
- if statConn != nil {
- counter = statConn.ReadCounter
- }
- if c, ok := iConn.(*internet.PacketConnWrapper); ok && UDPOverride.Address == nil && UDPOverride.Port == 0 {
- return &PacketReader{
- PacketConnWrapper: c,
- Counter: counter,
- }
- }
- return &buf.PacketReader{Reader: conn}
- }
- type PacketReader struct {
- *internet.PacketConnWrapper
- stats.Counter
- }
- func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
- b := buf.New()
- b.Resize(0, buf.Size)
- n, d, err := r.PacketConnWrapper.ReadFrom(b.Bytes())
- if err != nil {
- b.Release()
- return nil, err
- }
- b.Resize(0, int32(n))
- b.UDP = &net.Destination{
- Address: net.IPAddress(d.(*net.UDPAddr).IP),
- Port: net.Port(d.(*net.UDPAddr).Port),
- Network: net.Network_UDP,
- }
- if r.Counter != nil {
- r.Counter.Add(int64(n))
- }
- return buf.MultiBuffer{b}, nil
- }
- func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride net.Destination) buf.Writer {
- iConn := conn
- statConn, ok := iConn.(*stat.CounterConnection)
- if ok {
- iConn = statConn.Connection
- }
- var counter stats.Counter
- if statConn != nil {
- counter = statConn.WriteCounter
- }
- if c, ok := iConn.(*internet.PacketConnWrapper); ok {
- return &PacketWriter{
- PacketConnWrapper: c,
- Counter: counter,
- Handler: h,
- Context: ctx,
- UDPOverride: UDPOverride,
- }
- }
- return &buf.SequentialWriter{Writer: conn}
- }
- type PacketWriter struct {
- *internet.PacketConnWrapper
- stats.Counter
- *Handler
- context.Context
- UDPOverride net.Destination
- }
- func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
- for {
- mb2, b := buf.SplitFirst(mb)
- mb = mb2
- if b == nil {
- break
- }
- var n int
- var err error
- if b.UDP != nil {
- if w.UDPOverride.Address != nil {
- b.UDP.Address = w.UDPOverride.Address
- }
- if w.UDPOverride.Port != 0 {
- b.UDP.Port = w.UDPOverride.Port
- }
- if w.Handler.config.useIP() && b.UDP.Address.Family().IsDomain() {
- ip := w.Handler.resolveIP(w.Context, b.UDP.Address.Domain(), nil)
- if ip != nil {
- b.UDP.Address = ip
- }
- }
- destAddr, _ := net.ResolveUDPAddr("udp", b.UDP.NetAddr())
- if destAddr == nil {
- b.Release()
- continue
- }
- n, err = w.PacketConnWrapper.WriteTo(b.Bytes(), destAddr)
- } else {
- n, err = w.PacketConnWrapper.Write(b.Bytes())
- }
- b.Release()
- if err != nil {
- buf.ReleaseMulti(mb)
- return err
- }
- if w.Counter != nil {
- w.Counter.Add(int64(n))
- }
- }
- return nil
- }
- type FragmentWriter struct {
- fragment *Fragment
- writer io.Writer
- count uint64
- }
- func (f *FragmentWriter) Write(b []byte) (int, error) {
- f.count++
- if f.fragment.PacketsFrom == 0 && f.fragment.PacketsTo == 1 {
- if f.count != 1 || len(b) <= 5 || b[0] != 22 {
- return f.writer.Write(b)
- }
- recordLen := 5 + ((int(b[3]) << 8) | int(b[4]))
- data := b[5:recordLen]
- buf := make([]byte, 1024)
- for from := 0; ; {
- to := from + int(randBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
- if to > len(data) {
- to = len(data)
- }
- copy(buf[:3], b)
- copy(buf[5:], data[from:to])
- l := to - from
- from = to
- buf[3] = byte(l >> 8)
- buf[4] = byte(l)
- _, err := f.writer.Write(buf[:5+l])
- time.Sleep(time.Duration(randBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
- if err != nil {
- return 0, err
- }
- if from == len(data) {
- if len(b) > recordLen {
- n, err := f.writer.Write(b[recordLen:])
- if err != nil {
- return recordLen + n, err
- }
- }
- return len(b), nil
- }
- }
- }
- if f.fragment.PacketsFrom != 0 && (f.count < f.fragment.PacketsFrom || f.count > f.fragment.PacketsTo) {
- return f.writer.Write(b)
- }
- for from := 0; ; {
- to := from + int(randBetween(int64(f.fragment.LengthMin), int64(f.fragment.LengthMax)))
- if to > len(b) {
- to = len(b)
- }
- n, err := f.writer.Write(b[from:to])
- from += n
- time.Sleep(time.Duration(randBetween(int64(f.fragment.IntervalMin), int64(f.fragment.IntervalMax))) * time.Millisecond)
- if err != nil {
- return from, err
- }
- if from >= len(b) {
- return from, nil
- }
- }
- }
- // stolen from github.com/xtls/xray-core/transport/internet/reality
- func randBetween(left int64, right int64) int64 {
- if left == right {
- return left
- }
- bigInt, _ := rand.Int(rand.Reader, big.NewInt(right-left))
- return left + bigInt.Int64()
- }
|