github.com/sagernet/sing-box@v1.2.7/common/proxyproto/dialer.go (about)

     1  package proxyproto
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"net/netip"
     7  
     8  	"github.com/sagernet/sing-box/adapter"
     9  	E "github.com/sagernet/sing/common/exceptions"
    10  	M "github.com/sagernet/sing/common/metadata"
    11  	N "github.com/sagernet/sing/common/network"
    12  
    13  	"github.com/pires/go-proxyproto"
    14  )
    15  
    16  var _ N.Dialer = (*Dialer)(nil)
    17  
    18  type Dialer struct {
    19  	N.Dialer
    20  }
    21  
    22  func (d *Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
    23  	switch N.NetworkName(network) {
    24  	case N.NetworkTCP:
    25  		conn, err := d.Dialer.DialContext(ctx, network, destination)
    26  		if err != nil {
    27  			return nil, err
    28  		}
    29  		var source M.Socksaddr
    30  		metadata := adapter.ContextFrom(ctx)
    31  		if metadata != nil {
    32  			source = metadata.Source
    33  		}
    34  		if !source.IsValid() {
    35  			source = M.SocksaddrFromNet(conn.LocalAddr())
    36  		}
    37  		if destination.Addr.Is6() {
    38  			source = M.SocksaddrFrom(netip.AddrFrom16(source.Addr.As16()), source.Port)
    39  		}
    40  		h := proxyproto.HeaderProxyFromAddrs(1, source.TCPAddr(), destination.TCPAddr())
    41  		_, err = h.WriteTo(conn)
    42  		if err != nil {
    43  			conn.Close()
    44  			return nil, E.Cause(err, "write proxy protocol header")
    45  		}
    46  		return conn, nil
    47  	default:
    48  		return d.Dialer.DialContext(ctx, network, destination)
    49  	}
    50  }