Sfoglia il codice sorgente

prototype of vpndialer

Darien Raymond 8 anni fa
parent
commit
c5aa4acb35

+ 52 - 0
app/vpndialer/config.pb.go

@@ -0,0 +1,52 @@
+package vpndialer
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type Config struct {
+	Address string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"`
+}
+
+func (m *Config) Reset()                    { *m = Config{} }
+func (m *Config) String() string            { return proto.CompactTextString(m) }
+func (*Config) ProtoMessage()               {}
+func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func (m *Config) GetAddress() string {
+	if m != nil {
+		return m.Address
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*Config)(nil), "v2ray.core.app.vpndialer.Config")
+}
+
+func init() { proto.RegisterFile("v2ray.com/core/app/vpndialer/config.proto", fileDescriptor0) }
+
+var fileDescriptor0 = []byte{
+	// 150 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2c, 0x33, 0x2a, 0x4a,
+	0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0x2f,
+	0x2b, 0xc8, 0x4b, 0xc9, 0x4c, 0xcc, 0x49, 0x2d, 0xd2, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7,
+	0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x80, 0x29, 0x2d, 0x4a, 0xd5, 0x4b, 0x2c, 0x28, 0xd0,
+	0x83, 0x2b, 0x53, 0x52, 0xe2, 0x62, 0x73, 0x06, 0xab, 0x14, 0x92, 0xe0, 0x62, 0x4f, 0x4c, 0x49,
+	0x29, 0x4a, 0x2d, 0x2e, 0x96, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x82, 0x71, 0x9d, 0xdc, 0xb8,
+	0x64, 0x92, 0xf3, 0x73, 0xf5, 0x70, 0x99, 0x11, 0xc0, 0x18, 0xc5, 0x09, 0xe7, 0xac, 0x62, 0x92,
+	0x08, 0x33, 0x0a, 0x4a, 0xac, 0xd4, 0x73, 0x06, 0xa9, 0x73, 0x2c, 0x28, 0xd0, 0x0b, 0x2b, 0xc8,
+	0x73, 0x01, 0x4b, 0x25, 0xb1, 0x81, 0x1d, 0x63, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x97, 0xfc,
+	0x09, 0x70, 0xb9, 0x00, 0x00, 0x00,
+}

+ 11 - 0
app/vpndialer/config.proto

@@ -0,0 +1,11 @@
+syntax = "proto3";
+
+package v2ray.core.app.vpndialer;
+option csharp_namespace = "V2Ray.Core.App.VpnDialer";
+option go_package = "vpndialer";
+option java_package = "com.v2ray.core.app.vpndialer";
+option java_multiple_files = true;
+
+message Config {
+  string address = 1;
+}

+ 7 - 0
app/vpndialer/unix/errors.generated.go

@@ -0,0 +1,7 @@
+package unix
+
+import "v2ray.com/core/common/errors"
+
+func newError(values ...interface{}) *errors.Error {
+	return errors.New(values...).Path("App", "VPNDialer", "Unix")
+}

+ 218 - 0
app/vpndialer/unix/unix.go

@@ -0,0 +1,218 @@
+package unix
+
+import (
+	"context"
+	"net"
+	"os"
+	"sync"
+
+	"golang.org/x/sys/unix"
+	"v2ray.com/core/app/vpndialer"
+	"v2ray.com/core/common"
+	v2net "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,Unix
+
+type status int
+
+const (
+	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 v2net.Destination) (int, error) {
+	switch dest.Network {
+	case v2net.Network_TCP:
+		return unix.Socket(unix.AF_INET6, unix.SOCK_STREAM, unix.IPPROTO_TCP)
+	case v2net.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 v2net.Address) (net.IP, error) {
+	if addr.Family().Either(v2net.AddressFamilyIPv4, v2net.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 v2net.Address, dest v2net.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))
+	}))
+}

+ 1 - 0
app/vpndialer/vpndialer.go

@@ -0,0 +1 @@
+package vpndialer

+ 7 - 0
common/crypto/auth.go

@@ -60,6 +60,13 @@ func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) {
 	return v.AEAD.Seal(dst, iv, plainText, additionalData), nil
 }
 
+type StreamMode int
+
+const (
+	ModeStream StreamMode = iota
+	ModePacket
+)
+
 type AuthenticationReader struct {
 	auth       Authenticator
 	buffer     *buf.Buffer

+ 7 - 0
common/serial/bytes.go

@@ -20,6 +20,13 @@ func BytesToUint32(value []byte) uint32 {
 		uint32(value[3])
 }
 
+func BytesToInt(value []byte) int {
+	return int(value[0])<<24 |
+		int(value[1])<<16 |
+		int(value[2])<<8 |
+		int(value[3])
+}
+
 // BytesToInt64 deserializes a byte array to an int64 in big endian order. The byte array must have at least 8 elements.
 func BytesToInt64(value []byte) int64 {
 	return int64(value[0])<<56 |

+ 4 - 1
transport/internet/system_dialer.go

@@ -63,9 +63,12 @@ func (v *SimpleSystemDialer) Dial(ctx context.Context, src v2net.Address, dest v
 // UseAlternativeSystemDialer replaces the current system dialer with a given one.
 // Caller must ensure there is no race condition.
 func UseAlternativeSystemDialer(dialer SystemDialer) {
+	if dialer == nil {
+		effectiveSystemDialer = &DefaultSystemDialer{}
+	}
 	effectiveSystemDialer = dialer
 }
 
 func init() {
-	effectiveSystemDialer = &DefaultSystemDialer{}
+	UseAlternativeSystemDialer(nil)
 }