| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 | package uniximport (	"context"	"os"	"sync"	"golang.org/x/sys/unix"	"v2ray.com/core/app/vpndialer"	"v2ray.com/core/common"	"v2ray.com/core/common/net"	"v2ray.com/core/common/serial"	"v2ray.com/core/transport/internet")//go:generate go run $GOPATH/src/v2ray.com/core/tools/generrorgen/main.go -pkg unix -path App,VPNDialer,Unixtype status intconst (	statusNew status = iota	statusOK	statusFail)type fdStatus struct {	status   status	fd       int	callback chan<- error}type protector struct {	sync.Mutex	address string	conn    *net.UnixConn	status  chan fdStatus}func readFrom(conn *net.UnixConn, schan chan<- fdStatus) {	var payload [6]byte	for {		_, err := conn.Read(payload[:])		if err != nil {			break		}		fd := serial.BytesToInt(payload[1:5])		s := status(payload[5])		schan <- fdStatus{			fd:     fd,			status: s,		}	}}func (m *protector) dial() (*net.UnixConn, error) {	m.Lock()	defer m.Unlock()	if m.conn != nil {		return m.conn, nil	}	conn, err := net.DialUnix("unix", nil, &net.UnixAddr{		Name: m.address,		Net:  "unix",	})	if err != nil {		return nil, err	}	m.conn = conn	m.status = make(chan fdStatus, 32)	go readFrom(conn, m.status)	go m.monitor(conn)	return conn, nil}func (m *protector) close() {	m.Lock()	defer m.Unlock()	if m.conn == nil {		return	}	m.conn.Close()	m.conn = nil}func (m *protector) monitor(c *net.UnixConn) {	pendingFd := make(map[int]chan<- error, 32)	for s := range m.status {		switch s.status {		case statusNew:			pendingFd[s.fd] = s.callback		case statusOK:			if c, f := pendingFd[s.fd]; f {				close(c)				delete(pendingFd, s.fd)			}		case statusFail:			if c, f := pendingFd[s.fd]; f {				c <- newError("failed to protect fd")				close(c)				delete(pendingFd, s.fd)			}		}	}}func (m *protector) protect(fd int) error {	conn, err := m.dial()	if err != nil {		return err	}	var payload [6]byte	serial.IntToBytes(fd, payload[1:1])	payload[5] = byte(statusNew)	if _, err := conn.Write(payload[:]); err != nil {		return err	}	wait := make(chan error)	m.status <- fdStatus{		status:   statusNew,		fd:       fd,		callback: wait,	}	return <-wait}type App struct {	protector *protector	dialer    *Dialer}func NewApp(ctx context.Context, config *vpndialer.Config) (*App, error) {	a := &App{		dialer: &Dialer{},		protector: &protector{			address: config.Address,		},	}	a.dialer.protect = a.protector.protect	return a, nil}func (*App) Interface() interface{} {	return (*App)(nil)}func (a *App) Start() error {	internet.UseAlternativeSystemDialer(a.dialer)	return nil}func (a *App) Close() {	internet.UseAlternativeSystemDialer(nil)}type Dialer struct {	protect func(fd int) error}func socket(dest net.Destination) (int, error) {	switch dest.Network {	case net.Network_TCP:		return unix.Socket(unix.AF_INET6, unix.SOCK_STREAM, unix.IPPROTO_TCP)	case net.Network_UDP:		return unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP)	default:		return 0, newError("unknown network ", dest.Network)	}}func getIP(addr net.Address) (net.IP, error) {	if addr.Family().Either(net.AddressFamilyIPv4, net.AddressFamilyIPv6) {		return addr.IP(), nil	}	ips, err := net.LookupIP(addr.Domain())	if err != nil {		return nil, err	}	return ips[0], nil}func (d *Dialer) Dial(ctx context.Context, source net.Address, dest net.Destination) (net.Conn, error) {	fd, err := socket(dest)	if err != nil {		return nil, err	}	if err := d.protect(fd); err != nil {		return nil, err	}	ip, err := getIP(dest.Address)	if err != nil {		return nil, err	}	addr := &unix.SockaddrInet6{		Port:   int(dest.Port),		ZoneId: 0,	}	copy(addr.Addr[:], ip.To16())	if err := unix.Connect(fd, addr); err != nil {		return nil, err	}	file := os.NewFile(uintptr(fd), "Socket")	return net.FileConn(file)}func init() {	common.Must(common.RegisterConfig((*vpndialer.Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {		return NewApp(ctx, config.(*vpndialer.Config))	}))}
 |