mirror of
				https://github.com/e1732a364fed/v2ray_simple.git
				synced 2025-10-31 03:56:20 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			405 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			405 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package vless
 | ||
| 
 | ||
| import (
 | ||
| 	"bytes"
 | ||
| 	"encoding/binary"
 | ||
| 	"errors"
 | ||
| 	"fmt"
 | ||
| 	"io"
 | ||
| 	"log"
 | ||
| 	"net"
 | ||
| 	"net/url"
 | ||
| 	"sync"
 | ||
| 	"time"
 | ||
| 	"unsafe"
 | ||
| 
 | ||
| 	"github.com/hahahrfool/v2ray_simple/netLayer"
 | ||
| 	"github.com/hahahrfool/v2ray_simple/proxy"
 | ||
| 	"github.com/hahahrfool/v2ray_simple/utils"
 | ||
| )
 | ||
| 
 | ||
| func init() {
 | ||
| 	proxy.RegisterServer(Name, &ServerCreator{})
 | ||
| }
 | ||
| 
 | ||
| //实现 proxy.UserServer 以及 tlsLayer.UserHaser
 | ||
| type Server struct {
 | ||
| 	proxy.ProxyCommonStruct
 | ||
| 	userHashes   map[[16]byte]*proxy.V2rayUser
 | ||
| 	userCRUMFURS map[[16]byte]*CRUMFURS
 | ||
| 	mux4Hashes   sync.RWMutex
 | ||
| }
 | ||
| 
 | ||
| type ServerCreator struct{}
 | ||
| 
 | ||
| func (_ ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) {
 | ||
| 	uuidStr := lc.Uuid
 | ||
| 	id, err := proxy.NewV2rayUser(uuidStr)
 | ||
| 	if err != nil {
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 	s := &Server{
 | ||
| 		userHashes:   make(map[[16]byte]*proxy.V2rayUser),
 | ||
| 		userCRUMFURS: make(map[[16]byte]*CRUMFURS),
 | ||
| 	}
 | ||
| 
 | ||
| 	s.addV2User(id)
 | ||
| 
 | ||
| 	return s, nil
 | ||
| }
 | ||
| 
 | ||
| func (_ ServerCreator) NewServerFromURL(u *url.URL) (proxy.Server, error) {
 | ||
| 	return NewServer(u)
 | ||
| }
 | ||
| func NewServer(url *url.URL) (proxy.Server, error) {
 | ||
| 
 | ||
| 	uuidStr := url.User.Username()
 | ||
| 	id, err := proxy.NewV2rayUser(uuidStr)
 | ||
| 	if err != nil {
 | ||
| 		return nil, err
 | ||
| 	}
 | ||
| 	s := &Server{
 | ||
| 		userHashes:   make(map[[16]byte]*proxy.V2rayUser),
 | ||
| 		userCRUMFURS: make(map[[16]byte]*CRUMFURS),
 | ||
| 	}
 | ||
| 
 | ||
| 	s.addV2User(id)
 | ||
| 
 | ||
| 	return s, nil
 | ||
| }
 | ||
| func (s *Server) CanFallback() bool {
 | ||
| 	return true
 | ||
| }
 | ||
| func (s *Server) addV2User(u *proxy.V2rayUser) {
 | ||
| 	s.userHashes[*u] = u
 | ||
| }
 | ||
| 
 | ||
| func (s *Server) AddV2User(u *proxy.V2rayUser) {
 | ||
| 
 | ||
| 	s.mux4Hashes.Lock()
 | ||
| 	s.userHashes[*u] = u
 | ||
| 	s.mux4Hashes.Unlock()
 | ||
| }
 | ||
| 
 | ||
| func (s *Server) DelV2User(u *proxy.V2rayUser) {
 | ||
| 
 | ||
| 	s.mux4Hashes.RLock()
 | ||
| 
 | ||
| 	hasu := s.userHashes[*u]
 | ||
| 	if hasu == nil {
 | ||
| 		s.mux4Hashes.RUnlock()
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	s.mux4Hashes.Lock()
 | ||
| 	delete(s.userHashes, *u)
 | ||
| 	s.mux4Hashes.Unlock()
 | ||
| 
 | ||
| }
 | ||
| 
 | ||
| func (s *Server) GetUserByBytes(bs []byte) proxy.User {
 | ||
| 	if len(bs) < 16 {
 | ||
| 		return nil
 | ||
| 	}
 | ||
| 	thisUUIDBytes := *(*[16]byte)(unsafe.Pointer(&bs[0]))
 | ||
| 	if s.userHashes[thisUUIDBytes] != nil {
 | ||
| 		return proxy.V2rayUser(thisUUIDBytes)
 | ||
| 	}
 | ||
| 	return nil
 | ||
| }
 | ||
| 
 | ||
| func (s *Server) HasUserByBytes(bs []byte) bool {
 | ||
| 	if len(bs) < 16 {
 | ||
| 		return false
 | ||
| 	}
 | ||
| 	if s.userHashes[*(*[16]byte)(unsafe.Pointer(&bs[0]))] != nil {
 | ||
| 		return true
 | ||
| 	}
 | ||
| 	return false
 | ||
| }
 | ||
| 
 | ||
| func (s *Server) UserBytesLen() int {
 | ||
| 	return 16
 | ||
| }
 | ||
| 
 | ||
| func (s *Server) GetUserByStr(str string) proxy.User {
 | ||
| 	u, e := utils.StrToUUID(str)
 | ||
| 	if e != nil {
 | ||
| 		return nil
 | ||
| 	}
 | ||
| 	return s.GetUserByBytes(u[:])
 | ||
| }
 | ||
| 
 | ||
| func (s *Server) Name() string { return Name }
 | ||
| 
 | ||
| // 返回的bytes.Buffer 是用于 回落使用的,内含了整个读取的数据;不回落时不要使用该Buffer
 | ||
| func (s *Server) Handshake(underlay net.Conn) (result io.ReadWriteCloser, targetAddr netLayer.Addr, returnErr error) {
 | ||
| 
 | ||
| 	if err := underlay.SetReadDeadline(time.Now().Add(time.Second * 4)); err != nil {
 | ||
| 		returnErr = err
 | ||
| 		return
 | ||
| 	}
 | ||
| 	defer underlay.SetReadDeadline(time.Time{})
 | ||
| 
 | ||
| 	//这里我们本 不用再创建一个buffer来缓存数据,因为tls包本身就是有缓存的,所以一点一点读就行,tcp本身系统也是有缓存的
 | ||
| 	// 因此v1.0.3以及更老版本都是直接一段一段read的。
 | ||
| 	//但是,因为需要支持fallback技术,所以还是要 进行缓存
 | ||
| 
 | ||
| 	readbs := utils.GetBytes(utils.StandardBytesLength)
 | ||
| 
 | ||
| 	//var auth [17]byte
 | ||
| 	wholeReadLen, err := underlay.Read(readbs)
 | ||
| 	if err != nil {
 | ||
| 		returnErr = utils.ErrInErr{ErrDesc: "read err", ErrDetail: err, Data: wholeReadLen}
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	if wholeReadLen < 17 {
 | ||
| 		//根据下面回答,HTTP的最小长度恰好是16字节,但是是0.9版本。1.0是18字节,1.1还要更长。总之我们可以直接不返回fallback地址
 | ||
| 		//https://stackoverflow.com/questions/25047905/http-request-minimum-size-in-bytes/25065089
 | ||
| 
 | ||
| 		returnErr = utils.ErrInErr{ErrDesc: "fallback, msg too short", Data: wholeReadLen}
 | ||
| 		return
 | ||
| 	}
 | ||
| 
 | ||
| 	readbuf := bytes.NewBuffer(readbs[:wholeReadLen])
 | ||
| 
 | ||
| 	goto realPart
 | ||
| 
 | ||
| errorPart:
 | ||
| 
 | ||
| 	//所返回的buffer必须包含所有数据,而Buffer不支持回退,所以只能重新New
 | ||
| 	returnErr = &utils.ErrFirstBuffer{
 | ||
| 		Err:   returnErr,
 | ||
| 		First: bytes.NewBuffer(readbs[:wholeReadLen]),
 | ||
| 	}
 | ||
| 	return
 | ||
| 
 | ||
| realPart:
 | ||
| 	//这部分过程可以参照 v2ray的 proxy/vless/encoding/encoding.go DecodeRequestHeader 方法
 | ||
| 	//see https://github.com/v2fly/v2ray-core/blob/master/proxy/vless/inbound/inbound.go
 | ||
| 
 | ||
| 	auth := readbuf.Next(17)
 | ||
| 
 | ||
| 	version := auth[0]
 | ||
| 	if version > 1 {
 | ||
| 
 | ||
| 		returnErr = utils.ErrInErr{ErrDesc: "Vless invalid version ", Data: version}
 | ||
| 		goto errorPart
 | ||
| 
 | ||
| 	}
 | ||
| 
 | ||
| 	idBytes := auth[1:17]
 | ||
| 
 | ||
| 	s.mux4Hashes.RLock()
 | ||
| 
 | ||
| 	thisUUIDBytes := *(*[16]byte)(unsafe.Pointer(&idBytes[0])) //下面crumfurs也有用到
 | ||
| 
 | ||
| 	if user := s.userHashes[thisUUIDBytes]; user != nil {
 | ||
| 		s.mux4Hashes.RUnlock()
 | ||
| 	} else {
 | ||
| 		s.mux4Hashes.RUnlock()
 | ||
| 		returnErr = errors.New("invalid user")
 | ||
| 		goto errorPart
 | ||
| 	}
 | ||
| 
 | ||
| 	if version == 0 {
 | ||
| 
 | ||
| 		addonLenByte, err := readbuf.ReadByte()
 | ||
| 		if err != nil {
 | ||
| 			returnErr = err //凡是和的层Read相关的错误,一律不再返回Fallback信息,因为连接已然不可用
 | ||
| 			return
 | ||
| 		}
 | ||
| 		if addonLenByte != 0 {
 | ||
| 			//v2ray的vless中没有对应的任何处理。
 | ||
| 			//v2ray 的 vless 虽然有一个没用的Flow,但是 EncodeBodyAddons里根本没向里写任何数据。所以理论上正常这部分始终应该为0
 | ||
| 			if utils.CanLogWarn() {
 | ||
| 
 | ||
| 				log.Println("potential illegal client", addonLenByte)
 | ||
| 			}
 | ||
| 
 | ||
| 			//读一下然后直接舍弃
 | ||
| 			/*
 | ||
| 				tmpBuf := utils.GetBytes(int(addonLenByte))
 | ||
| 				underlay.Read(tmpBuf)
 | ||
| 				utils.PutBytes(tmpBuf)
 | ||
| 			*/
 | ||
| 			if tmpbs := readbuf.Next(int(addonLenByte)); len(tmpbs) != int(addonLenByte) {
 | ||
| 				returnErr = errors.New("vless short read in addon")
 | ||
| 				return
 | ||
| 			}
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	commandByte, err := readbuf.ReadByte()
 | ||
| 
 | ||
| 	if err != nil {
 | ||
| 
 | ||
| 		returnErr = errors.New("fallback, reason 2")
 | ||
| 		goto errorPart
 | ||
| 	}
 | ||
| 
 | ||
| 	switch commandByte {
 | ||
| 	case CmdMux: //实际目前verysimple 暂时还未实现mux,先这么写
 | ||
| 
 | ||
| 		targetAddr.Port = 0
 | ||
| 		targetAddr.Name = "v1.mux.cool"
 | ||
| 
 | ||
| 	case Cmd_CRUMFURS:
 | ||
| 		if version != 1 {
 | ||
| 
 | ||
| 			returnErr = errors.New("在vless的vesion不为1时使用了 CRUMFURS 命令")
 | ||
| 			goto errorPart
 | ||
| 
 | ||
| 		}
 | ||
| 
 | ||
| 		_, err = underlay.Write([]byte{CRUMFURS_ESTABLISHED})
 | ||
| 		if err != nil {
 | ||
| 
 | ||
| 			returnErr = utils.ErrInErr{ErrDesc: "write to crumfurs err", ErrDetail: err}
 | ||
| 			goto errorPart
 | ||
| 		}
 | ||
| 
 | ||
| 		targetAddr.Name = CRUMFURS_Established_Str // 使用这个特殊的办法来告诉调用者,预留了 CRUMFURS 信道,防止其关闭上层连接导致 CRUMFURS 信道 被关闭。
 | ||
| 
 | ||
| 		theCRUMFURS := &CRUMFURS{
 | ||
| 			Conn: underlay,
 | ||
| 		}
 | ||
| 
 | ||
| 		s.mux4Hashes.Lock()
 | ||
| 
 | ||
| 		s.userCRUMFURS[thisUUIDBytes] = theCRUMFURS
 | ||
| 
 | ||
| 		s.mux4Hashes.Unlock()
 | ||
| 
 | ||
| 		return
 | ||
| 
 | ||
| 	case CmdTCP, CmdUDP:
 | ||
| 
 | ||
| 		portbs := readbuf.Next(2)
 | ||
| 
 | ||
| 		if err != nil || len(portbs) != 2 {
 | ||
| 
 | ||
| 			returnErr = errors.New("fallback, reason 3")
 | ||
| 			goto errorPart
 | ||
| 		}
 | ||
| 
 | ||
| 		targetAddr.Port = int(binary.BigEndian.Uint16(portbs))
 | ||
| 
 | ||
| 		if commandByte == CmdUDP {
 | ||
| 			targetAddr.Network = "udp"
 | ||
| 		}
 | ||
| 
 | ||
| 		var ip_or_domain_bytesLength byte = 0
 | ||
| 
 | ||
| 		addrTypeByte, err := readbuf.ReadByte()
 | ||
| 
 | ||
| 		if err != nil {
 | ||
| 
 | ||
| 			returnErr = errors.New("fallback, reason 4")
 | ||
| 			goto errorPart
 | ||
| 		}
 | ||
| 
 | ||
| 		switch addrTypeByte {
 | ||
| 		case netLayer.AtypIP4:
 | ||
| 
 | ||
| 			ip_or_domain_bytesLength = net.IPv4len
 | ||
| 			targetAddr.IP = utils.GetBytes(net.IPv4len)
 | ||
| 
 | ||
| 		case netLayer.AtypDomain:
 | ||
| 			// 解码域名的长度
 | ||
| 
 | ||
| 			domainNameLenByte, err := readbuf.ReadByte()
 | ||
| 
 | ||
| 			if err != nil {
 | ||
| 
 | ||
| 				returnErr = errors.New("fallback, reason 5")
 | ||
| 				goto errorPart
 | ||
| 			}
 | ||
| 
 | ||
| 			ip_or_domain_bytesLength = domainNameLenByte
 | ||
| 		case netLayer.AtypIP6:
 | ||
| 
 | ||
| 			ip_or_domain_bytesLength = net.IPv6len
 | ||
| 			targetAddr.IP = utils.GetBytes(net.IPv6len)
 | ||
| 		default:
 | ||
| 
 | ||
| 			returnErr = fmt.Errorf("unknown address type %v", addrTypeByte)
 | ||
| 			goto errorPart
 | ||
| 		}
 | ||
| 
 | ||
| 		ip_or_domain := utils.GetBytes(int(ip_or_domain_bytesLength))
 | ||
| 
 | ||
| 		_, err = readbuf.Read(ip_or_domain)
 | ||
| 
 | ||
| 		if err != nil {
 | ||
| 			returnErr = errors.New("fallback, reason 6")
 | ||
| 			return
 | ||
| 		}
 | ||
| 
 | ||
| 		if targetAddr.IP != nil {
 | ||
| 			copy(targetAddr.IP, ip_or_domain)
 | ||
| 		} else {
 | ||
| 			targetAddr.Name = string(ip_or_domain)
 | ||
| 		}
 | ||
| 
 | ||
| 		utils.PutBytes(ip_or_domain)
 | ||
| 
 | ||
| 	default:
 | ||
| 
 | ||
| 		returnErr = errors.New("invalid vless command")
 | ||
| 		goto errorPart
 | ||
| 	}
 | ||
| 
 | ||
| 	return &UserConn{
 | ||
| 		Conn:              underlay,
 | ||
| 		optionalReader:    io.MultiReader(readbuf, underlay),
 | ||
| 		remainFirstBufLen: readbuf.Len(),
 | ||
| 		uuid:              thisUUIDBytes,
 | ||
| 		version:           int(version),
 | ||
| 		isUDP:             targetAddr.IsUDP(),
 | ||
| 		underlayIsBasic:   netLayer.IsBasicConn(underlay),
 | ||
| 		isServerEnd:       true,
 | ||
| 	}, targetAddr, nil
 | ||
| 
 | ||
| }
 | ||
| 
 | ||
| func (s *Server) Get_CRUMFURS(id string) *CRUMFURS {
 | ||
| 	bs, err := utils.StrToUUID(id)
 | ||
| 	if err != nil {
 | ||
| 		return nil
 | ||
| 	}
 | ||
| 	return s.userCRUMFURS[bs]
 | ||
| }
 | ||
| 
 | ||
| type CRUMFURS struct {
 | ||
| 	net.Conn
 | ||
| 	hasAdvancedLayer bool //在用ws或grpc时,这个开关保持打开
 | ||
| }
 | ||
| 
 | ||
| func (c *CRUMFURS) WriteUDPResponse(a net.UDPAddr, b []byte) (err error) {
 | ||
| 	atype := netLayer.AtypIP4
 | ||
| 	if len(a.IP) > 4 {
 | ||
| 		atype = netLayer.AtypIP6
 | ||
| 	}
 | ||
| 	buf := utils.GetBuf()
 | ||
| 
 | ||
| 	buf.WriteByte(atype)
 | ||
| 	buf.Write(a.IP)
 | ||
| 	buf.WriteByte(byte(int16(a.Port) >> 8))
 | ||
| 	buf.WriteByte(byte(int16(a.Port) << 8 >> 8))
 | ||
| 
 | ||
| 	if !c.hasAdvancedLayer {
 | ||
| 		lb := int16(len(b))
 | ||
| 
 | ||
| 		buf.WriteByte(byte(lb >> 8))
 | ||
| 		buf.WriteByte(byte(lb << 8 >> 8))
 | ||
| 	}
 | ||
| 	buf.Write(b)
 | ||
| 
 | ||
| 	_, err = c.Write(buf.Bytes())
 | ||
| 
 | ||
| 	utils.PutBuf(buf)
 | ||
| 	return
 | ||
| }
 | 
