mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
vmess: worked in SecTypeNone mode
This commit is contained in:
parent
4a72e79002
commit
385f5d7790
@ -52,6 +52,7 @@ General:
|
||||
TODO:
|
||||
- [ ] Transparent UDP proxy (iptables tproxy)
|
||||
- [ ] DNS Cache
|
||||
- [ ] Performance tuning
|
||||
- [ ] TUN/TAP device support
|
||||
- [ ] IPv6 support
|
||||
- [ ] SSH tunnel support
|
||||
|
@ -22,9 +22,9 @@ const (
|
||||
|
||||
// SOCKS address types as defined in RFC 1928 section 5.
|
||||
const (
|
||||
ATypeIP4 = 1
|
||||
ATypeDomain = 3
|
||||
ATypeIP6 = 4
|
||||
ATypIP4 = 1
|
||||
ATypDomain = 3
|
||||
ATypIP6 = 4
|
||||
)
|
||||
|
||||
// MaxAddrLen is the maximum size of SOCKS address in bytes.
|
||||
@ -52,13 +52,13 @@ func (a Addr) String() string {
|
||||
var host, port string
|
||||
|
||||
switch ATYP(a[0]) { // address type
|
||||
case ATypeDomain:
|
||||
case ATypDomain:
|
||||
host = string(a[2 : 2+int(a[1])])
|
||||
port = strconv.Itoa((int(a[2+int(a[1])]) << 8) | int(a[2+int(a[1])+1]))
|
||||
case ATypeIP4:
|
||||
case ATypIP4:
|
||||
host = net.IP(a[1 : 1+net.IPv4len]).String()
|
||||
port = strconv.Itoa((int(a[1+net.IPv4len]) << 8) | int(a[1+net.IPv4len+1]))
|
||||
case ATypeIP6:
|
||||
case ATypIP6:
|
||||
host = net.IP(a[1 : 1+net.IPv6len]).String()
|
||||
port = strconv.Itoa((int(a[1+net.IPv6len]) << 8) | int(a[1+net.IPv6len+1]))
|
||||
}
|
||||
@ -66,12 +66,12 @@ func (a Addr) String() string {
|
||||
return net.JoinHostPort(host, port)
|
||||
}
|
||||
|
||||
// UoT udp over tcp
|
||||
// UoT returns whether it is udp over tcp
|
||||
func UoT(b byte) bool {
|
||||
return b&0x8 == 0x8
|
||||
}
|
||||
|
||||
// ATYP return the address type
|
||||
// ATYP returns the address type
|
||||
func ATYP(b byte) int {
|
||||
return int(b &^ 0x8)
|
||||
}
|
||||
@ -87,17 +87,17 @@ func ReadAddrBuf(r io.Reader, b []byte) (Addr, error) {
|
||||
}
|
||||
|
||||
switch ATYP(b[0]) {
|
||||
case ATypeDomain:
|
||||
case ATypDomain:
|
||||
_, err = io.ReadFull(r, b[1:2]) // read 2nd byte for domain length
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = io.ReadFull(r, b[2:2+int(b[1])+2])
|
||||
return b[:1+1+int(b[1])+2], err
|
||||
case ATypeIP4:
|
||||
case ATypIP4:
|
||||
_, err = io.ReadFull(r, b[1:1+net.IPv4len+2])
|
||||
return b[:1+net.IPv4len+2], err
|
||||
case ATypeIP6:
|
||||
case ATypIP6:
|
||||
_, err = io.ReadFull(r, b[1:1+net.IPv6len+2])
|
||||
return b[:1+net.IPv6len+2], err
|
||||
}
|
||||
@ -118,14 +118,14 @@ func SplitAddr(b []byte) Addr {
|
||||
}
|
||||
|
||||
switch ATYP(b[0]) {
|
||||
case ATypeDomain:
|
||||
case ATypDomain:
|
||||
if len(b) < 2 {
|
||||
return nil
|
||||
}
|
||||
addrLen = 1 + 1 + int(b[1]) + 2
|
||||
case ATypeIP4:
|
||||
case ATypIP4:
|
||||
addrLen = 1 + net.IPv4len + 2
|
||||
case ATypeIP6:
|
||||
case ATypIP6:
|
||||
addrLen = 1 + net.IPv6len + 2
|
||||
default:
|
||||
return nil
|
||||
@ -149,11 +149,11 @@ func ParseAddr(s string) Addr {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
addr = make([]byte, 1+net.IPv4len+2)
|
||||
addr[0] = ATypeIP4
|
||||
addr[0] = ATypIP4
|
||||
copy(addr[1:], ip4)
|
||||
} else {
|
||||
addr = make([]byte, 1+net.IPv6len+2)
|
||||
addr[0] = ATypeIP6
|
||||
addr[0] = ATypIP6
|
||||
copy(addr[1:], ip)
|
||||
}
|
||||
} else {
|
||||
@ -161,7 +161,7 @@ func ParseAddr(s string) Addr {
|
||||
return nil
|
||||
}
|
||||
addr = make([]byte, 1+1+len(host)+2)
|
||||
addr[0] = ATypeDomain
|
||||
addr[0] = ATypDomain
|
||||
addr[1] = byte(len(host))
|
||||
copy(addr[2:], host)
|
||||
}
|
||||
|
3
main.go
3
main.go
@ -21,8 +21,7 @@ import (
|
||||
_ "github.com/nadoo/glider/proxy/tls"
|
||||
_ "github.com/nadoo/glider/proxy/udptun"
|
||||
_ "github.com/nadoo/glider/proxy/uottun"
|
||||
// _ "github.com/nadoo/glider/proxy/v2ray"
|
||||
// _ "github.com/nadoo/glider/proxy/vmess"
|
||||
_ "github.com/nadoo/glider/proxy/vmess"
|
||||
)
|
||||
|
||||
// VERSION .
|
||||
|
@ -143,7 +143,7 @@ func getorigdst(fd uintptr) (socks.Addr, error) {
|
||||
}
|
||||
|
||||
addr := make([]byte, 1+net.IPv4len+2)
|
||||
addr[0] = socks.ATypeIP4
|
||||
addr[0] = socks.ATypIP4
|
||||
copy(addr[1:1+net.IPv4len], raw.Addr[:])
|
||||
port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian
|
||||
addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1]
|
||||
@ -160,7 +160,7 @@ func getorigdstIPv6(fd uintptr) (socks.Addr, error) {
|
||||
}
|
||||
|
||||
addr := make([]byte, 1+net.IPv6len+2)
|
||||
addr[0] = socks.ATypeIP6
|
||||
addr[0] = socks.ATypIP6
|
||||
copy(addr[1:1+net.IPv6len], raw.Addr[:])
|
||||
port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian
|
||||
addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1]
|
||||
|
@ -362,17 +362,17 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
buf = append(buf, socks.ATypeIP4)
|
||||
buf = append(buf, socks.ATypIP4)
|
||||
ip = ip4
|
||||
} else {
|
||||
buf = append(buf, socks.ATypeIP6)
|
||||
buf = append(buf, socks.ATypIP6)
|
||||
}
|
||||
buf = append(buf, ip...)
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return errors.New("proxy: destination hostname too long: " + host)
|
||||
}
|
||||
buf = append(buf, socks.ATypeDomain)
|
||||
buf = append(buf, socks.ATypDomain)
|
||||
buf = append(buf, byte(len(host)))
|
||||
buf = append(buf, host...)
|
||||
}
|
||||
@ -397,11 +397,11 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
||||
|
||||
bytesToDiscard := 0
|
||||
switch buf[3] {
|
||||
case socks.ATypeIP4:
|
||||
case socks.ATypIP4:
|
||||
bytesToDiscard = net.IPv4len
|
||||
case socks.ATypeIP6:
|
||||
case socks.ATypIP6:
|
||||
bytesToDiscard = net.IPv6len
|
||||
case socks.ATypeDomain:
|
||||
case socks.ATypDomain:
|
||||
_, err := io.ReadFull(conn, buf[:1])
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
|
@ -1,185 +0,0 @@
|
||||
package v2ray
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
|
||||
"v2ray.com/core"
|
||||
"v2ray.com/core/app/dispatcher"
|
||||
"v2ray.com/core/app/proxyman"
|
||||
v2net "v2ray.com/core/common/net"
|
||||
"v2ray.com/core/common/protocol"
|
||||
"v2ray.com/core/common/serial"
|
||||
"v2ray.com/core/proxy/vmess"
|
||||
"v2ray.com/core/proxy/vmess/outbound"
|
||||
"v2ray.com/core/transport/internet"
|
||||
"v2ray.com/core/transport/internet/tls"
|
||||
|
||||
// needed
|
||||
_ "v2ray.com/core/app/proxyman/outbound"
|
||||
_ "v2ray.com/core/transport/internet/tcp"
|
||||
)
|
||||
|
||||
// V2Ray .
|
||||
type V2Ray struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
uuid string
|
||||
alertID uint32
|
||||
|
||||
outboundSecurity string
|
||||
streamProtocol string
|
||||
streamSecurity string
|
||||
|
||||
config *core.Config
|
||||
instance *core.Instance
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("v2ray", NewV2RayDialer)
|
||||
}
|
||||
|
||||
// NewV2Ray returns a v2ray proxy.
|
||||
func NewV2Ray(s string, dialer proxy.Dialer) (*V2Ray, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse url err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr := u.Host
|
||||
host := u.Hostname()
|
||||
port, err := strconv.ParseUint(u.Port(), 10, 32)
|
||||
if err != nil {
|
||||
log.F("parse port err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var uuid, aid string
|
||||
if u.User != nil {
|
||||
uuid = u.User.Username()
|
||||
aid, _ = u.User.Password()
|
||||
}
|
||||
|
||||
alertID, err := strconv.ParseUint(aid, 10, 32)
|
||||
if err != nil {
|
||||
log.F("parse alertID err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&dispatcher.Config{}),
|
||||
serial.ToTypedMessage(&proxyman.OutboundConfig{}),
|
||||
},
|
||||
Outbound: []*core.OutboundHandlerConfig{{
|
||||
ProxySettings: serial.ToTypedMessage(&outbound.Config{
|
||||
Receiver: []*protocol.ServerEndpoint{
|
||||
{
|
||||
Address: v2net.NewIPOrDomain(v2net.ParseAddress(host)),
|
||||
Port: uint32(port),
|
||||
User: []*protocol.User{
|
||||
{
|
||||
Account: serial.ToTypedMessage(&vmess.Account{
|
||||
Id: uuid,
|
||||
AlterId: uint32(alertID),
|
||||
SecuritySettings: &protocol.SecurityConfig{
|
||||
Type: protocol.SecurityType_NONE,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{
|
||||
StreamSettings: &internet.StreamConfig{
|
||||
Protocol: internet.TransportProtocol_TCP,
|
||||
SecurityType: serial.GetMessageType(&tls.Config{}),
|
||||
SecuritySettings: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&tls.Config{
|
||||
AllowInsecure: true,
|
||||
}),
|
||||
},
|
||||
},
|
||||
})},
|
||||
},
|
||||
}
|
||||
|
||||
v, err := core.New(config)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to create V: ", err.Error())
|
||||
}
|
||||
|
||||
p := &V2Ray{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
|
||||
uuid: uuid,
|
||||
alertID: uint32(alertID),
|
||||
|
||||
outboundSecurity: "auto",
|
||||
streamProtocol: "tcp",
|
||||
streamSecurity: "tls",
|
||||
|
||||
config: config,
|
||||
instance: v,
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewV2RayDialer returns a v2ray proxy dialer.
|
||||
func NewV2RayDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewV2Ray(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *V2Ray) Addr() string { return s.addr }
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *V2Ray) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (s *V2Ray) Dial(network, addr string) (net.Conn, error) {
|
||||
|
||||
// c, err := s.dialer.Dial("tcp", s.addr)
|
||||
|
||||
d := strings.Split(addr, ":")
|
||||
host, portStr := d[0], d[1]
|
||||
port, err := strconv.ParseUint(portStr, 10, 32)
|
||||
if err != nil {
|
||||
log.F("parse portStr err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: does not support upstream dialer now
|
||||
c, err := core.Dial(context.Background(),
|
||||
s.instance,
|
||||
v2net.TCPDestination(v2net.ParseAddress(host), v2net.Port(port)))
|
||||
|
||||
if err != nil {
|
||||
log.F("[v2ray] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
return c, err
|
||||
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *V2Ray) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("v2ray client does not support udp now")
|
||||
}
|
61
proxy/vmess/addr.go
Normal file
61
proxy/vmess/addr.go
Normal file
@ -0,0 +1,61 @@
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// AType is vmess addr type
|
||||
type AType byte
|
||||
|
||||
// Atyp
|
||||
const (
|
||||
ATypeErr AType = 0
|
||||
ATypeIP4 AType = 1
|
||||
ATypeDomain AType = 2
|
||||
ATypeIP6 AType = 3
|
||||
)
|
||||
|
||||
// Addr is vmess addr
|
||||
type Addr []byte
|
||||
|
||||
// Port is vmess addr port
|
||||
type Port uint16
|
||||
|
||||
// ParseAddr parses the address in string s. return AType = 0 if error.
|
||||
func ParseAddr(s string) (AType, Addr, Port, error) {
|
||||
var atype AType
|
||||
var addr Addr
|
||||
|
||||
host, port, err := net.SplitHostPort(s)
|
||||
if err != nil {
|
||||
return 0, nil, 0, err
|
||||
}
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
addr = make([]byte, net.IPv4len)
|
||||
atype = ATypeIP4
|
||||
copy(addr[:], ip4)
|
||||
} else {
|
||||
addr = make([]byte, net.IPv6len)
|
||||
atype = ATypeIP6
|
||||
copy(addr[:], ip)
|
||||
}
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return 0, nil, 0, err
|
||||
}
|
||||
addr = make([]byte, 1+len(host))
|
||||
atype = ATypeDomain
|
||||
addr[0] = byte(len(host))
|
||||
copy(addr[1:], host)
|
||||
}
|
||||
|
||||
portnum, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
return 0, nil, 0, err
|
||||
}
|
||||
|
||||
return atype, addr, Port(portnum), err
|
||||
}
|
214
proxy/vmess/client.go
Normal file
214
proxy/vmess/client.go
Normal file
@ -0,0 +1,214 @@
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash/fnv"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Request Options
|
||||
const (
|
||||
OptChunkStream byte = 1
|
||||
OptReuseTCPConnection byte = 2
|
||||
OptMetadataObfuscate byte = 4
|
||||
)
|
||||
|
||||
// SEC types
|
||||
const (
|
||||
SecTypeUnknown byte = 0
|
||||
SecTypeLegacy byte = 1
|
||||
SecTypeAuto byte = 2
|
||||
SecTypeAES128GCM byte = 3
|
||||
SecTypeChacha20Poly1305 byte = 4
|
||||
SecTypeNone byte = 5
|
||||
)
|
||||
|
||||
// CMD types
|
||||
const (
|
||||
CmdTCP byte = 1
|
||||
CmdUDP byte = 2
|
||||
)
|
||||
|
||||
// Client vmess client
|
||||
type Client struct {
|
||||
user *User
|
||||
atype AType
|
||||
addr Addr
|
||||
port Port
|
||||
|
||||
reqBodyIV [16]byte
|
||||
reqBodyKey [16]byte
|
||||
reqRespV byte
|
||||
respBodyKey [16]byte
|
||||
respBodyIV [16]byte
|
||||
|
||||
net.Conn
|
||||
connected bool
|
||||
}
|
||||
|
||||
// NewClient .
|
||||
func NewClient(uuid, target string) (*Client, error) {
|
||||
user, err := NewUser(uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Client{user: user}
|
||||
|
||||
c.atype, c.addr, c.port, err = ParseAddr(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
randBytes := make([]byte, 33)
|
||||
rand.Read(randBytes)
|
||||
|
||||
copy(c.reqBodyIV[:], randBytes[:16])
|
||||
copy(c.reqBodyKey[:], randBytes[16:32])
|
||||
c.reqRespV = randBytes[32]
|
||||
|
||||
c.respBodyIV = md5.Sum(c.reqBodyIV[:])
|
||||
c.respBodyKey = md5.Sum(c.reqBodyKey[:])
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// EncodeAuthInfo returns HMAC("md5", UUID, UTC) result
|
||||
func (c *Client) EncodeAuthInfo() []byte {
|
||||
ts := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(ts, uint64(time.Now().UTC().Unix()))
|
||||
|
||||
h := hmac.New(md5.New, c.user.UUID[:])
|
||||
h.Write(ts)
|
||||
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// EncodeRequest encodes requests to network bytes
|
||||
func (c *Client) EncodeRequest() ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
// Request
|
||||
buf.WriteByte(1) // Ver
|
||||
buf.Write(c.reqBodyIV[:]) // IV
|
||||
buf.Write(c.reqBodyKey[:]) // Key
|
||||
buf.WriteByte(c.reqRespV) // V
|
||||
buf.WriteByte(0) // Opt
|
||||
|
||||
// pLen and Sec
|
||||
paddingLen := rand.Intn(16)
|
||||
pSec := byte(paddingLen<<4) | SecTypeNone // P(4bit) and Sec(4bit)
|
||||
buf.WriteByte(pSec)
|
||||
|
||||
buf.WriteByte(0) // reserved
|
||||
buf.WriteByte(CmdTCP) // cmd
|
||||
|
||||
// target
|
||||
binary.Write(buf, binary.BigEndian, uint16(c.port)) // port
|
||||
buf.WriteByte(byte(c.atype)) // atype
|
||||
buf.Write(c.addr) // addr
|
||||
|
||||
// padding
|
||||
if paddingLen > 0 {
|
||||
padding := make([]byte, paddingLen)
|
||||
rand.Read(padding)
|
||||
buf.Write(padding)
|
||||
}
|
||||
|
||||
// F
|
||||
fnv1a := fnv.New32a()
|
||||
fnv1a.Write(buf.Bytes())
|
||||
buf.Write(fnv1a.Sum(nil))
|
||||
|
||||
// AES-128-CFB crypt the request:
|
||||
// Key:MD5(UUID + []byte('c48619fe-8f02-49e0-b9e9-edf763e17e21'))
|
||||
// IV:MD5(X + X + X + X),X = []byte(timestamp.now) (8 bytes, Big Endian)
|
||||
md5hash := md5.New()
|
||||
md5hash.Write(c.user.UUID[:])
|
||||
md5hash.Write([]byte("c48619fe-8f02-49e0-b9e9-edf763e17e21"))
|
||||
key := md5hash.Sum(nil)
|
||||
|
||||
md5hash.Reset()
|
||||
ts := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(ts, uint64(time.Now().UTC().Unix()))
|
||||
md5hash.Write(ts)
|
||||
md5hash.Write(ts)
|
||||
md5hash.Write(ts)
|
||||
md5hash.Write(ts)
|
||||
iv := md5hash.Sum(nil)
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(buf.Bytes(), buf.Bytes())
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// DecodeRespHeader .
|
||||
func (c *Client) DecodeRespHeader() error {
|
||||
block, err := aes.NewCipher(c.respBodyKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBDecrypter(block, c.respBodyIV[:])
|
||||
buf := make([]byte, 4)
|
||||
io.ReadFull(c.Conn, buf)
|
||||
stream.XORKeyStream(buf, buf)
|
||||
|
||||
if buf[0] != c.reqRespV {
|
||||
return errors.New("unexpected response header")
|
||||
}
|
||||
|
||||
// TODO: Dynamic port supported
|
||||
// if buf[2] != 0 {
|
||||
// cmd := buf[2]
|
||||
// dataLen := int32(buf[3])
|
||||
// }
|
||||
|
||||
c.connected = true
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// NewConn wraps a net.Conn to VMessConn
|
||||
func (c *Client) NewConn(rc net.Conn) (net.Conn, error) {
|
||||
// AuthInfo
|
||||
rc.Write(c.EncodeAuthInfo())
|
||||
|
||||
// Request
|
||||
req, err := c.EncodeRequest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc.Write(req)
|
||||
|
||||
c.Conn = rc
|
||||
|
||||
return c, err
|
||||
}
|
||||
|
||||
func (c *Client) Read(b []byte) (n int, err error) {
|
||||
if !c.connected {
|
||||
c.DecodeRespHeader()
|
||||
}
|
||||
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *Client) Write(b []byte) (n int, err error) {
|
||||
return c.Conn.Write(b)
|
||||
}
|
37
proxy/vmess/user.go
Normal file
37
proxy/vmess/user.go
Normal file
@ -0,0 +1,37 @@
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// User of vmess client
|
||||
type User struct {
|
||||
UUID [16]byte
|
||||
}
|
||||
|
||||
// NewUser .
|
||||
func NewUser(uuidStr string) (*User, error) {
|
||||
uuid, err := StrToUUID(uuidStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u := &User{
|
||||
UUID: uuid,
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// StrToUUID converts string to uuid.
|
||||
// s fomat: "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||
func StrToUUID(s string) (uuid [16]byte, err error) {
|
||||
b := []byte(strings.Replace(s, "-", "", -1))
|
||||
if len(b) != 32 {
|
||||
return uuid, errors.New("invalid UUID: " + s)
|
||||
}
|
||||
_, err = hex.Decode(uuid[:], b)
|
||||
return
|
||||
}
|
@ -15,12 +15,9 @@ type VMess struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
uuid string
|
||||
alertID uint32
|
||||
|
||||
outboundSecurity string
|
||||
streamProtocol string
|
||||
streamSecurity string
|
||||
uuid string
|
||||
alertID uint32
|
||||
security string
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -37,10 +34,20 @@ func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) {
|
||||
|
||||
addr := u.Host
|
||||
|
||||
var uuid, aid string
|
||||
var uuid, security string
|
||||
if u.User != nil {
|
||||
uuid = u.User.Username()
|
||||
aid, _ = u.User.Password()
|
||||
security, _ = u.User.Password()
|
||||
}
|
||||
|
||||
if security == "" {
|
||||
security = "NONE"
|
||||
}
|
||||
|
||||
aid := "0"
|
||||
params, _ := url.ParseQuery(u.RawQuery)
|
||||
if v, ok := params["alertId"]; ok {
|
||||
aid = v[0]
|
||||
}
|
||||
|
||||
alertID, err := strconv.ParseUint(aid, 10, 32)
|
||||
@ -50,13 +57,11 @@ func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) {
|
||||
}
|
||||
|
||||
p := &VMess{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
uuid: uuid,
|
||||
alertID: uint32(alertID),
|
||||
outboundSecurity: "auto",
|
||||
streamProtocol: "tcp",
|
||||
streamSecurity: "tls",
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
uuid: uuid,
|
||||
alertID: uint32(alertID),
|
||||
security: security,
|
||||
}
|
||||
|
||||
return p, nil
|
||||
@ -75,7 +80,19 @@ func (s *VMess) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDi
|
||||
|
||||
// Dial connects to the address addr on the network net via the proxy.
|
||||
func (s *VMess) Dial(network, addr string) (net.Conn, error) {
|
||||
return nil, nil
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := NewClient(s.uuid, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rc, err = client.NewConn(rc)
|
||||
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
|
Loading…
Reference in New Issue
Block a user