func (handler *VMessOutboundHandler) startCommunicate(request *vmessio.VMessRequest, dest v2net.Address, ray core.OutboundRay) error { conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{dest.IP, int(dest.Port), ""}) log.Debug("VMessOutbound dialing tcp: %s", dest.String()) if err != nil { log.Error("Failed to open tcp (%s): %v", dest.String(), err) return err } defer conn.Close() input := ray.OutboundInput() output := ray.OutboundOutput() requestWriter := vmessio.NewVMessRequestWriter() err = requestWriter.Write(conn, request) if err != nil { log.Error("Failed to write VMess request: %v", err) close(output) return err } requestKey := request.RequestKey[:] requestIV := request.RequestIV[:] responseKey := md5.Sum(requestKey) responseIV := md5.Sum(requestIV) response := vmessio.VMessResponse{} nBytes, err := conn.Read(response[:]) if err != nil { close(output) log.Error("Failed to read VMess response (%d bytes): %v", nBytes, err) return err } log.Debug("Got response %v", response) // TODO: check response encryptRequestWriter, err := v2io.NewAesEncryptWriter(requestKey, requestIV, conn) if err != nil { close(output) log.Error("Failed to create encrypt writer: %v", err) return err } decryptResponseReader, err := v2io.NewAesDecryptReader(responseKey[:], responseIV[:], conn) if err != nil { close(output) log.Error("Failed to create decrypt reader: %v", err) return err } readFinish := make(chan bool) writeFinish := make(chan bool) go handler.dumpInput(encryptRequestWriter, input, readFinish) go handler.dumpOutput(decryptResponseReader, output, writeFinish) <-readFinish conn.CloseWrite() log.Debug("VMessOut closing write") <-writeFinish return nil }
func handleRequest(conn *net.TCPConn, request *vmessio.VMessRequest, input <-chan []byte, finish chan<- bool) error { defer close(finish) encryptRequestWriter, err := v2io.NewAesEncryptWriter(request.RequestKey[:], request.RequestIV[:], conn) if err != nil { log.Error("Failed to create encrypt writer: %v", err) return err } buffer, err := request.ToBytes(v2hash.NewTimeHash(v2hash.HMACHash{}), v2math.GenerateRandomInt64InRange) if err != nil { log.Error("VMessOut: Failed to serialize VMess request: %v", err) } //conn.Write(buffer) data, open := <-input if open { encryptRequestWriter.Crypt(data) buffer = append(buffer, data...) } _, err = conn.Write(buffer) if err != nil { log.Error("VMessOut: Failed to write VMess request: %v", err) } if !open { return nil } v2net.ChanToWriter(encryptRequestWriter, input) return nil }
// NewPoint returns a new Point server based on given configuration. // The server is not started at this point. func NewPoint(config Config) (*Point, error) { var vpoint = new(Point) vpoint.port = config.Port ichFactory, ok := inboundFactories[config.InboundConfig.Protocol] if !ok { panic(log.Error("Unknown inbound connection handler factory %s", config.InboundConfig.Protocol)) } vpoint.ichFactory = ichFactory if len(config.InboundConfig.File) > 0 { ichConfig, err := ioutil.ReadFile(config.InboundConfig.File) if err != nil { panic(log.Error("Unable to read config file %v", err)) } vpoint.ichConfig = ichConfig } ochFactory, ok := outboundFactories[config.OutboundConfig.Protocol] if !ok { panic(log.Error("Unknown outbound connection handler factory %s", config.OutboundConfig.Protocol)) } vpoint.ochFactory = ochFactory if len(config.OutboundConfig.File) > 0 { ochConfig, err := ioutil.ReadFile(config.OutboundConfig.File) if err != nil { panic(log.Error("Unable to read config file %v", err)) } vpoint.ochConfig = ochConfig } return vpoint, nil }
func handleRequest(conn *net.TCPConn, request *vmessio.VMessRequest, input <-chan []byte, finish chan<- bool) { defer close(finish) encryptRequestWriter, err := v2io.NewAesEncryptWriter(request.RequestKey[:], request.RequestIV[:], conn) if err != nil { log.Error("VMessOut: Failed to create encrypt writer: %v", err) return } buffer, err := request.ToBytes(v2hash.NewTimeHash(v2hash.HMACHash{}), v2math.GenerateRandomInt64InRange) if err != nil { log.Error("VMessOut: Failed to serialize VMess request: %v", err) return } // Send first packet of payload together with request, in favor of small requests. payload, open := <-input if open { encryptRequestWriter.Crypt(payload) buffer = append(buffer, payload...) _, err = conn.Write(buffer) if err != nil { log.Error("VMessOut: Failed to write VMess request: %v", err) return } v2net.ChanToWriter(encryptRequestWriter, input) } return }
func handleResponse(conn *net.TCPConn, request *vmessio.VMessRequest, output chan<- []byte, finish chan<- bool) { defer close(finish) defer close(output) responseKey := md5.Sum(request.RequestKey[:]) responseIV := md5.Sum(request.RequestIV[:]) decryptResponseReader, err := v2io.NewAesDecryptReader(responseKey[:], responseIV[:], conn) if err != nil { log.Error("VMessOut: Failed to create decrypt reader: %v", err) return } response := vmessio.VMessResponse{} nBytes, err := decryptResponseReader.Read(response[:]) if err != nil { log.Error("VMessOut: Failed to read VMess response (%d bytes): %v", nBytes, err) return } if !bytes.Equal(response[:], request.ResponseHeader[:]) { log.Warning("VMessOut: unexepcted response header. The connection is probably hijacked.") return } v2net.ReaderToChan(output, decryptResponseReader) return }
func main() { flag.Parse() if *version { fmt.Printf("V2Ray version %s (%s): %s", core.Version, core.Codename, core.Intro) fmt.Println() return } switch *logLevel { case "debug": log.SetLogLevel(log.DebugLevel) case "info": log.SetLogLevel(log.InfoLevel) case "warning": log.SetLogLevel(log.WarningLevel) case "error": log.SetLogLevel(log.ErrorLevel) } if configFile == nil || len(*configFile) == 0 { panic(log.Error("Config file is not set.")) } rawVConfig, err := ioutil.ReadFile(*configFile) if err != nil { panic(log.Error("Failed to read config file (%s): %v", *configFile, err)) } vconfig, err := core.LoadConfig(rawVConfig) if err != nil { panic(log.Error("Failed to parse Config: %v", err)) } if !filepath.IsAbs(vconfig.InboundConfig.File) && len(vconfig.InboundConfig.File) > 0 { vconfig.InboundConfig.File = filepath.Join(filepath.Dir(*configFile), vconfig.InboundConfig.File) } if !filepath.IsAbs(vconfig.OutboundConfig.File) && len(vconfig.OutboundConfig.File) > 0 { vconfig.OutboundConfig.File = filepath.Join(filepath.Dir(*configFile), vconfig.OutboundConfig.File) } vPoint, err := core.NewPoint(vconfig) if err != nil { panic(log.Error("Failed to create Point server: %v", err)) } err = vPoint.Start() if err != nil { log.Error("Error starting Point server: %v", err) } finish := make(chan bool) <-finish }
func (handler *VMessInboundHandler) HandleConnection(connection net.Conn) error { defer connection.Close() reader := vmessio.NewVMessRequestReader(handler.clients) request, err := reader.Read(connection) if err != nil { log.Debug("Failed to parse VMess request: %v", err) return err } log.Debug("Received request for %s", request.Address.String()) response := vmessio.NewVMessResponse(request) nBytes, err := connection.Write(response[:]) if err != nil { return log.Error("Failed to write VMess response (%d bytes): %v", nBytes, err) } requestKey := request.RequestKey[:] requestIV := request.RequestIV[:] responseKey := md5.Sum(requestKey) responseIV := md5.Sum(requestIV) requestReader, err := v2io.NewAesDecryptReader(requestKey, requestIV, connection) if err != nil { return log.Error("Failed to create decrypt reader: %v", err) } responseWriter, err := v2io.NewAesEncryptWriter(responseKey[:], responseIV[:], connection) if err != nil { return log.Error("Failed to create encrypt writer: %v", err) } ray := handler.vPoint.NewInboundConnectionAccepted(request.Address) input := ray.InboundInput() output := ray.InboundOutput() readFinish := make(chan bool) writeFinish := make(chan bool) go handler.dumpInput(requestReader, input, readFinish) go handler.dumpOutput(responseWriter, output, writeFinish) <-writeFinish if tcpConn, ok := connection.(*net.TCPConn); ok { log.Debug("VMessIn closing write") tcpConn.CloseWrite() } <-readFinish return nil }
func (factory *VMessInboundHandlerFactory) Create(vp *core.Point, rawConfig []byte) (core.InboundConnectionHandler, error) { config, err := loadInboundConfig(rawConfig) if err != nil { panic(log.Error("Failed to load VMess inbound config: %v", err)) } allowedClients := core.NewTimedUserSet() for _, client := range config.AllowedClients { user, err := client.ToUser() if err != nil { panic(log.Error("Failed to parse user id %s: %v", client.Id, err)) } allowedClients.AddUser(user) } return NewVMessInboundHandler(vp, allowedClients), nil }
func startCommunicate(request *vmessio.VMessRequest, dest v2net.Address, ray core.OutboundRay) error { input := ray.OutboundInput() output := ray.OutboundOutput() conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{dest.IP, int(dest.Port), ""}) log.Debug("VMessOutbound dialing tcp: %s", dest.String()) if err != nil { log.Error("Failed to open tcp (%s): %v", dest.String(), err) close(output) return err } defer conn.Close() defer close(output) requestFinish := make(chan bool) responseFinish := make(chan bool) go handleRequest(conn, request, input, requestFinish) go handleResponse(conn, request, output, responseFinish) <-requestFinish conn.CloseWrite() <-responseFinish return nil }
// TODO: leverage a full functional UUID library func UUIDToID(uuid string) (v ID, err error) { text := []byte(uuid) if len(text) < 32 { err = log.Error("uuid: invalid UUID string: %s", text) return } b := v[:] for _, byteGroup := range byteGroups { if text[0] == '-' { text = text[1:] } _, err = hex.Decode(b[:byteGroup/2], text[:byteGroup]) if err != nil { return } text = text[byteGroup:] b = b[byteGroup/2:] } return }
func NewID(id string) (ID, error) { idBytes, err := UUIDToID(id) if err != nil { return ID{}, log.Error("Failed to parse id %s", id) } hasher := hmac.New(md5.New, idBytes) return ID{id, idBytes, hasher}, nil }
func (config VNextConfig) ToVNextServer() VNextServer { users := make([]core.User, 0, len(config.Users)) for _, user := range config.Users { vuser, err := user.ToUser() if err != nil { panic(log.Error("Failed to convert %v to User.", user)) } users = append(users, vuser) } ip := net.ParseIP(config.Address) if ip == nil { panic(log.Error("Unable to parse VNext IP: %s", config.Address)) } return VNextServer{ v2net.IPAddress(ip, config.Port), users} }
func handleRequest(conn *net.TCPConn, request *vmessio.VMessRequest, input <-chan []byte, finish chan<- bool) error { defer close(finish) requestWriter := vmessio.NewVMessRequestWriter(v2hash.NewTimeHash(v2hash.HMACHash{}), v2math.GenerateRandomInt64InRange) err := requestWriter.Write(conn, request) if err != nil { log.Error("Failed to write VMess request: %v", err) return err } encryptRequestWriter, err := v2io.NewAesEncryptWriter(request.RequestKey[:], request.RequestIV[:], conn) if err != nil { log.Error("Failed to create encrypt writer: %v", err) return err } v2net.ChanToWriter(encryptRequestWriter, input) return nil }
func (server *SocksServer) Listen(port uint16) error { listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port))) if err != nil { return log.Error("Error on listening port %d: %v", port, err) } server.accepting = true go server.AcceptConnections(listener) return nil }
func (server *SocksServer) AcceptConnections(listener net.Listener) { for server.accepting { connection, err := listener.Accept() if err != nil { log.Error("Error on accepting socks connection: %v", err) } go server.HandleConnection(connection) } }
// Read reads blocks from underlying reader, the length of blocks must be // a multiply of BlockSize() func (reader CryptionReader) Read(blocks []byte) (int, error) { nBytes, err := reader.reader.Read(blocks) if nBytes > 0 { reader.stream.XORKeyStream(blocks[:nBytes], blocks[:nBytes]) } if err != nil && err != io.EOF { log.Error("Error reading blocks: %v", err) } return nBytes, err }
func NewSocksServer(vp *core.Point, rawConfig []byte) *SocksServer { server := new(SocksServer) server.vPoint = vp config, err := loadConfig(rawConfig) if err != nil { panic(log.Error("Unable to load socks config: %v", err)) } server.config = config return server }
func NewSocksServer(vp *core.Point, rawConfig []byte) *SocksServer { config, err := loadConfig(rawConfig) if err != nil { panic(log.Error("Unable to load socks config: %v", err)) } return &SocksServer{ vPoint: vp, config: config, } }
func (handler *VMessInboundHandler) AcceptConnections(listener net.Listener) error { for handler.accepting { connection, err := listener.Accept() if err != nil { return log.Error("Failed to accpet connection: %s", err.Error()) } go handler.HandleConnection(connection) } return nil }
func (handler *VMessInboundHandler) HandleConnection(connection net.Conn) error { defer connection.Close() reader := vmessio.NewVMessRequestReader(handler.clients) request, err := reader.Read(connection) if err != nil { return err } log.Debug("Received request for %s", request.Address.String()) response := vmessio.NewVMessResponse(request) nBytes, err := connection.Write(response[:]) log.Debug("Writing VMess response %v", response) if err != nil { return log.Error("Failed to write VMess response (%d bytes): %v", nBytes, err) } requestKey := request.RequestKey[:] requestIV := request.RequestIV[:] responseKey := md5.Sum(requestKey) responseIV := md5.Sum(requestIV) requestReader, err := v2io.NewAesDecryptReader(requestKey, requestIV, connection) if err != nil { return log.Error("Failed to create decrypt reader: %v", err) } responseWriter, err := v2io.NewAesEncryptWriter(responseKey[:], responseIV[:], connection) if err != nil { return log.Error("Failed to create encrypt writer: %v", err) } ray := handler.vPoint.NewInboundConnectionAccepted(request.Address) input := ray.InboundInput() output := ray.InboundOutput() finish := make(chan bool, 2) go handler.dumpInput(requestReader, input, finish) go handler.dumpOutput(responseWriter, output, finish) handler.waitForFinish(finish) return nil }
func (handler *VMessInboundHandler) Listen(port uint16) error { listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port))) if err != nil { return log.Error("Unable to listen tcp:%d", port) } handler.accepting = true go handler.AcceptConnections(listener) return nil }
func (handler *VMessInboundHandler) HandleConnection(connection net.Conn) error { defer connection.Close() reader := vmessio.NewVMessRequestReader(handler.clients) request, err := reader.Read(connection) if err != nil { log.Debug("Failed to parse VMess request: %v", err) return err } log.Debug("Received request for %s", request.Address.String()) ray := handler.vPoint.NewInboundConnectionAccepted(request.Address) input := ray.InboundInput() output := ray.InboundOutput() readFinish := make(chan bool) writeFinish := make(chan bool) go handleInput(request, connection, input, readFinish) responseKey := md5.Sum(request.RequestKey[:]) responseIV := md5.Sum(request.RequestIV[:]) response := vmessio.NewVMessResponse(request) responseWriter, err := v2io.NewAesEncryptWriter(responseKey[:], responseIV[:], connection) if err != nil { return log.Error("Failed to create encrypt writer: %v", err) } //responseWriter.Write(response[:]) // Optimize for small response packet buffer := make([]byte, 0, 1024) buffer = append(buffer, response[:]...) data, open := <-output if open { buffer = append(buffer, data...) } responseWriter.Write(buffer) if open { go handleOutput(request, responseWriter, output, writeFinish) } else { close(writeFinish) } <-writeFinish if tcpConn, ok := connection.(*net.TCPConn); ok { log.Debug("VMessIn closing write") tcpConn.CloseWrite() } <-readFinish return nil }
// Read reads blocks from underlying reader, the length of blocks must be // a multiply of BlockSize() func (reader CryptionReader) Read(blocks []byte) (int, error) { nBytes, err := reader.reader.Read(blocks) log.Debug("CryptionReader: Read %d bytes", nBytes) if nBytes > 0 { reader.stream.XORKeyStream(blocks[:nBytes], blocks[:nBytes]) } if err != nil { log.Error("Error reading blocks: %v", err) } return nBytes, err }
// Start starts the Point server, and return any error during the process. // In the case of any errors, the state of the server is unpredicatable. func (vp *Point) Start() error { if vp.port <= 0 { return log.Error("Invalid port %d", vp.port) } inboundConnectionHandler, err := vp.ichFactory.Create(vp, vp.ichConfig) if err != nil { return err } err = inboundConnectionHandler.Listen(vp.port) return nil }
func (factory *VMessOutboundHandlerFactory) Create(vp *core.Point, rawConfig []byte, destination v2net.Address) (core.OutboundConnectionHandler, error) { config, err := loadOutboundConfig(rawConfig) if err != nil { panic(log.Error("Failed to load VMess outbound config: %v", err)) } servers := make([]VNextServer, 0, len(config.VNextList)) for _, server := range config.VNextList { servers = append(servers, server.ToVNextServer()) } return NewVMessOutboundHandler(vp, servers, destination), nil }
func (factory *VMessInboundHandlerFactory) Create(vp *core.VPoint, rawConfig []byte) *VMessInboundHandler { config, err := loadInboundConfig(rawConfig) if err != nil { panic(log.Error("Failed to load VMess inbound config: %v", err)) } allowedClients := core.NewVUserSet() for _, user := range config.AllowedClients { allowedClients.AddUser(user) } return NewVMessInboundHandler(vp, allowedClients) }
func handleInput(request *vmessio.VMessRequest, reader io.Reader, input chan<- []byte, finish chan<- bool) { defer close(input) defer close(finish) requestReader, err := v2io.NewAesDecryptReader(request.RequestKey[:], request.RequestIV[:], reader) if err != nil { log.Error("Failed to create decrypt reader: %v", err) return } v2net.ReaderToChan(input, requestReader) }
func (config VNextConfig) ToVNextServer() VNextServer { users := make([]core.VUser, 0, len(config.Users)) for _, user := range config.Users { vuser, err := user.ToVUser() if err != nil { panic(log.Error("Failed to convert %v to VUser.", user)) } users = append(users, vuser) } return VNextServer{ v2net.DomainAddress(config.Address, config.Port), users} }
func NewID(id string) (ID, error) { idBytes, err := UUIDToID(id) if err != nil { return ID{}, log.Error("Failed to parse id %s", id) } md5hash := md5.New() md5hash.Write(idBytes) md5hash.Write([]byte("c48619fe-8f02-49e0-b9e9-edf763e17e21")) cmdKey := md5.Sum(nil) return ID{id, idBytes, cmdKey[:]}, nil }
func main() { flag.Parse() log.SetLogLevel(log.DebugLevel) if configFile == nil || len(*configFile) == 0 { panic(log.Error("Config file is not set.")) } rawVConfig, err := ioutil.ReadFile(*configFile) if err != nil { panic(log.Error("Failed to read config file (%s): %v", *configFile, err)) } vconfig, err := core.LoadConfig(rawVConfig) if err != nil { panic(log.Error("Failed to parse Config: %v", err)) } if !path.IsAbs(vconfig.InboundConfig.File) && len(vconfig.InboundConfig.File) > 0 { vconfig.InboundConfig.File = path.Join(path.Dir(*configFile), vconfig.InboundConfig.File) } if !path.IsAbs(vconfig.OutboundConfig.File) && len(vconfig.OutboundConfig.File) > 0 { vconfig.OutboundConfig.File = path.Join(path.Dir(*configFile), vconfig.OutboundConfig.File) } vPoint, err := core.NewPoint(vconfig) if err != nil { panic(log.Error("Failed to create Point server: %v", err)) } err = vPoint.Start() if err != nil { log.Error("Error starting Point server: %v", err) } finish := make(chan bool) <-finish }