From 0ca7dab49bb468967ab967df9d5a1fda980fc0bf Mon Sep 17 00:00:00 2001 From: V2Ray Date: Sat, 12 Sep 2015 11:51:42 +0200 Subject: [PATCH] massive refactoring for configuration --- io/config/json/json.go | 37 ------------------- io/socks/socks.go | 7 ++-- log/log.go | 5 +++ net/freedom/freedom.go | 6 ++-- net/freedom/freedomfactory.go | 4 +-- net/socks/config.go | 22 ++++++++++++ net/socks/socks.go | 41 ++++++++++++--------- net/socks/socksfactory.go | 4 +-- net/vmess/config.go | 40 +++++++++++++++++++++ net/vmess/vmessin.go | 22 ++++++------ net/vmess/vmessout.go | 34 +++++++++++++----- release/server/main.go | 5 +++ release/server/socks/main.go | 37 ------------------- vconfig.go | 28 +++++++-------- vpoint.go | 67 +++++++++++++++++++++++++++++------ 15 files changed, 212 insertions(+), 147 deletions(-) delete mode 100644 io/config/json/json.go create mode 100644 net/socks/config.go create mode 100644 net/vmess/config.go create mode 100644 release/server/main.go delete mode 100644 release/server/socks/main.go diff --git a/io/config/json/json.go b/io/config/json/json.go deleted file mode 100644 index 98735c733..000000000 --- a/io/config/json/json.go +++ /dev/null @@ -1,37 +0,0 @@ -// Package json contains io library for VConfig in Json format. -package json - -import ( - "encoding/json" - _ "fmt" - - "github.com/v2ray/v2ray-core" -) - -type JsonVUser struct { - id string `json:"id"` - email string `json:"email"` -} - -type JsonVConfig struct { - Port uint8 `json:"port"` - Clients []JsonVUser `json:"users"` - Protocol string `json:"protocol"` -} - -type JsonVConfigUnmarshaller struct { -} - -func StringToVUser(id string) (u core.VUser, err error) { - return -} - -func (*JsonVConfigUnmarshaller) Unmarshall(data []byte) (*core.VConfig, error) { - var jsonConfig JsonVConfig - err := json.Unmarshal(data, &jsonConfig) - if err != nil { - return nil, err - } - var vconfig = new(core.VConfig) - return vconfig, nil -} diff --git a/io/socks/socks.go b/io/socks/socks.go index 2e633ba45..d9cc0e8d1 100644 --- a/io/socks/socks.go +++ b/io/socks/socks.go @@ -13,9 +13,10 @@ import ( const ( socksVersion = uint8(5) - AuthNotRequired = byte(0x00) - AuthGssApi = byte(0x01) - AuthUserPass = byte(0x02) + AuthNotRequired = byte(0x00) + AuthGssApi = byte(0x01) + AuthUserPass = byte(0x02) + AuthNoMatchingMethod = byte(0xFF) ) // Authentication request header of Socks5 protocol diff --git a/log/log.go b/log/log.go index 7c5a7fbb0..45f936214 100644 --- a/log/log.go +++ b/log/log.go @@ -28,6 +28,11 @@ func writeLog(data string, level LogLevel) { log.Print(data) } +func Debug(format string, v ...interface{}) { + data := fmt.Sprintf(format, v) + writeLog("[Debug]"+data, DebugLevel) +} + func Info(format string, v ...interface{}) { data := fmt.Sprintf(format, v) writeLog("[Info]"+data, InfoLevel) diff --git a/net/freedom/freedom.go b/net/freedom/freedom.go index 7727c69a6..8d5242229 100644 --- a/net/freedom/freedom.go +++ b/net/freedom/freedom.go @@ -10,13 +10,11 @@ import ( ) type VFreeConnection struct { - vPoint *core.VPoint - dest v2net.VAddress + dest v2net.VAddress } -func NewVFreeConnection(vp *core.VPoint, dest v2net.VAddress) *VFreeConnection { +func NewVFreeConnection(dest v2net.VAddress) *VFreeConnection { conn := new(VFreeConnection) - conn.vPoint = vp conn.dest = dest return conn } diff --git a/net/freedom/freedomfactory.go b/net/freedom/freedomfactory.go index b07367f99..a9ed27081 100644 --- a/net/freedom/freedomfactory.go +++ b/net/freedom/freedomfactory.go @@ -8,6 +8,6 @@ import ( type FreedomFactory struct { } -func (factory FreedomFactory) Create(vp *core.VPoint, dest v2net.VAddress) (core.OutboundConnectionHandler, error) { - return NewVFreeConnection(vp, dest), nil +func (factory FreedomFactory) Create(vp *core.VPoint, config []byte, dest v2net.VAddress) (core.OutboundConnectionHandler, error) { + return NewVFreeConnection(dest), nil } diff --git a/net/socks/config.go b/net/socks/config.go new file mode 100644 index 000000000..4e0d61eb3 --- /dev/null +++ b/net/socks/config.go @@ -0,0 +1,22 @@ +package socks + +import ( + "encoding/json" +) + +const ( + JsonAuthMethodNoAuth = "noauth" + JsonAuthMethodUserPass = "password" +) + +type SocksConfig struct { + AuthMethod string `json:"auth"` + Username string `json:"user"` + Password string `json:"pass"` +} + +func loadConfig(rawConfig []byte) (SocksConfig, error) { + config := SocksConfig{} + err := json.Unmarshal(rawConfig, &config) + return config, err +} diff --git a/net/socks/socks.go b/net/socks/socks.go index 7478cb7e2..f4c341319 100644 --- a/net/socks/socks.go +++ b/net/socks/socks.go @@ -3,12 +3,12 @@ package socks import ( "errors" "io" - "log" "net" "strconv" "github.com/v2ray/v2ray-core" socksio "github.com/v2ray/v2ray-core/io/socks" + "github.com/v2ray/v2ray-core/log" ) var ( @@ -20,18 +20,24 @@ var ( type SocksServer struct { accepting bool vPoint *core.VPoint + config SocksConfig } -func NewSocksServer(vp *core.VPoint) *SocksServer { +func NewSocksServer(vp *core.VPoint, 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 (server *SocksServer) Listen(port uint16) error { listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port))) if err != nil { - return err + return log.Error("Error on listening port %d: %v", port, err) } server.accepting = true go server.AcceptConnections(listener) @@ -42,8 +48,7 @@ func (server *SocksServer) AcceptConnections(listener net.Listener) error { for server.accepting { connection, err := listener.Accept() if err != nil { - log.Print(err) - return err + return log.Error("Error on accepting socks connection: %v", err) } go server.HandleConnection(connection) } @@ -55,14 +60,19 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error { auth, err := socksio.ReadAuthentication(connection) if err != nil { - log.Print(err) - return err + return log.Error("Error on reading authentication: %v", err) } - log.Print(auth) - if !auth.HasAuthMethod(socksio.AuthNotRequired) { - // TODO send response with FF - log.Print(ErrorAuthenticationFailed) + expectedAuthMethod := socksio.AuthNotRequired + if server.config.AuthMethod == JsonAuthMethodUserPass { + expectedAuthMethod = socksio.AuthUserPass + } + + if !auth.HasAuthMethod(expectedAuthMethod) { + authResponse := socksio.NewAuthenticationResponse(socksio.AuthNoMatchingMethod) + socksio.WriteAuthentication(connection, authResponse) + + log.Info("Client doesn't support allowed any auth methods.") return ErrorAuthenticationFailed } @@ -71,8 +81,7 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error { request, err := socksio.ReadRequest(connection) if err != nil { - log.Print(err) - return err + return log.Error("Error on reading socks request: %v", err) } response := socksio.NewSocks5Response() @@ -81,7 +90,7 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error { response := socksio.NewSocks5Response() response.Error = socksio.ErrorCommandNotSupported socksio.WriteResponse(connection, response) - log.Print(ErrorCommandNotSupported) + log.Info("Unsupported socks command %d", request.Command) return ErrorCommandNotSupported } @@ -114,7 +123,7 @@ func (server *SocksServer) dumpInput(conn net.Conn, input chan<- []byte, finish for { buffer := make([]byte, 256) nBytes, err := conn.Read(buffer) - log.Printf("Reading %d bytes, with error %v", nBytes, err) + log.Debug("Reading %d bytes, with error %v", nBytes, err) if err == io.EOF { close(input) finish <- true @@ -132,7 +141,7 @@ func (server *SocksServer) dumpOutput(conn net.Conn, output <-chan []byte, finis break } nBytes, _ := conn.Write(buffer) - log.Printf("Writing %d bytes", nBytes) + log.Debug("Writing %d bytes", nBytes) } } diff --git a/net/socks/socksfactory.go b/net/socks/socksfactory.go index 928bb2c74..8c7635676 100644 --- a/net/socks/socksfactory.go +++ b/net/socks/socksfactory.go @@ -7,6 +7,6 @@ import ( type SocksServerFactory struct { } -func (factory SocksServerFactory) Create(vp *core.VPoint) (core.InboundConnectionHandler, error) { - return NewSocksServer(vp), nil +func (factory SocksServerFactory) Create(vp *core.VPoint, config []byte) (core.InboundConnectionHandler, error) { + return NewSocksServer(vp, config), nil } diff --git a/net/vmess/config.go b/net/vmess/config.go new file mode 100644 index 000000000..b6c426e6e --- /dev/null +++ b/net/vmess/config.go @@ -0,0 +1,40 @@ +package vmess + +import ( + "encoding/json" + + "github.com/v2ray/v2ray-core" + v2net "github.com/v2ray/v2ray-core/net" +) + +type VMessInboundConfig struct { + AllowedClients []core.VUser `json:"clients"` +} + +func loadInboundConfig(rawConfig []byte) (VMessInboundConfig, error) { + config := VMessInboundConfig{} + err := json.Unmarshal(rawConfig, &config) + return config, err +} + +type VNextConfig struct { + Address string `json:"address"` + Port uint16 `json:"port"` + Users []core.VUser `json:"users"` +} + +func (config VNextConfig) ToVNextServer() VNextServer { + return VNextServer{ + v2net.DomainAddress(config.Address, config.Port), + config.Users} +} + +type VMessOutboundConfig struct { + VNextList []VNextConfig +} + +func loadOutboundConfig(rawConfig []byte) (VMessOutboundConfig, error) { + config := VMessOutboundConfig{} + err := json.Unmarshal(rawConfig, &config) + return config, err +} diff --git a/net/vmess/vmessin.go b/net/vmess/vmessin.go index 3447c1482..9e041040b 100644 --- a/net/vmess/vmessin.go +++ b/net/vmess/vmessin.go @@ -8,6 +8,7 @@ import ( "github.com/v2ray/v2ray-core" v2io "github.com/v2ray/v2ray-core/io" vmessio "github.com/v2ray/v2ray-core/io/vmess" + "github.com/v2ray/v2ray-core/log" ) type VMessInboundHandler struct { @@ -89,6 +90,7 @@ func (handler *VMessInboundHandler) dumpInput(reader io.Reader, input chan<- []b buffer := make([]byte, BufferSize) nBytes, err := reader.Read(buffer) if err == io.EOF { + close(input) finish <- true break } @@ -114,18 +116,16 @@ func (handler *VMessInboundHandler) waitForFinish(finish <-chan bool) { } type VMessInboundHandlerFactory struct { - allowedClients *core.VUserSet } -func NewVMessInboundHandlerFactory(clients []core.VUser) *VMessInboundHandlerFactory { - factory := new(VMessInboundHandlerFactory) - factory.allowedClients = core.NewVUserSet() - for _, user := range clients { - factory.allowedClients.AddUser(user) +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)) } - return factory -} - -func (factory *VMessInboundHandlerFactory) Create(vp *core.VPoint) *VMessInboundHandler { - return NewVMessInboundHandler(vp, factory.allowedClients) + allowedClients := core.NewVUserSet() + for _, user := range config.AllowedClients { + allowedClients.AddUser(user) + } + return NewVMessInboundHandler(vp, allowedClients) } diff --git a/net/vmess/vmessout.go b/net/vmess/vmessout.go index 49c07fc9c..567809321 100644 --- a/net/vmess/vmessout.go +++ b/net/vmess/vmessout.go @@ -10,28 +10,37 @@ import ( "github.com/v2ray/v2ray-core" v2io "github.com/v2ray/v2ray-core/io" vmessio "github.com/v2ray/v2ray-core/io/vmess" + "github.com/v2ray/v2ray-core/log" v2net "github.com/v2ray/v2ray-core/net" ) -type VMessOutboundHandler struct { - vPoint *core.VPoint - dest v2net.VAddress +// VNext is the next VPoint server in the connection chain. +type VNextServer struct { + Address v2net.VAddress // Address of VNext server + Users []core.VUser // User accounts for accessing VNext. } -func NewVMessOutboundHandler(vp *core.VPoint, dest v2net.VAddress) *VMessOutboundHandler { +type VMessOutboundHandler struct { + vPoint *core.VPoint + dest v2net.VAddress + vNextList []VNextServer +} + +func NewVMessOutboundHandler(vp *core.VPoint, vNextList []VNextServer, dest v2net.VAddress) *VMessOutboundHandler { handler := new(VMessOutboundHandler) handler.vPoint = vp handler.dest = dest + handler.vNextList = vNextList return handler } func (handler *VMessOutboundHandler) pickVNext() (v2net.VAddress, core.VUser) { - vNextLen := len(handler.vPoint.Config.VNextList) + vNextLen := len(handler.vNextList) if vNextLen == 0 { panic("Zero vNext is configured.") } vNextIndex := mrand.Intn(vNextLen) - vNext := handler.vPoint.Config.VNextList[vNextIndex] + vNext := handler.vNextList[vNextIndex] vNextUserLen := len(vNext.Users) if vNextUserLen == 0 { panic("Zero User account.") @@ -91,6 +100,7 @@ func (handler *VMessOutboundHandler) dumpOutput(reader io.Reader, output chan<- buffer := make([]byte, BufferSize) nBytes, err := reader.Read(buffer) if err == io.EOF { + close(output) finish <- true break } @@ -118,6 +128,14 @@ func (handler *VMessOutboundHandler) waitForFinish(finish <-chan bool) { type VMessOutboundHandlerFactory struct { } -func (factory *VMessOutboundHandlerFactory) Create(vp *core.VPoint, destination v2net.VAddress) *VMessOutboundHandler { - return NewVMessOutboundHandler(vp, destination) +func (factory *VMessOutboundHandlerFactory) Create(vp *core.VPoint, rawConfig []byte, destination v2net.VAddress) *VMessOutboundHandler { + 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) } diff --git a/release/server/main.go b/release/server/main.go new file mode 100644 index 000000000..790580777 --- /dev/null +++ b/release/server/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + +} diff --git a/release/server/socks/main.go b/release/server/socks/main.go deleted file mode 100644 index 116f00faa..000000000 --- a/release/server/socks/main.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "log" - - "github.com/v2ray/v2ray-core" - "github.com/v2ray/v2ray-core/net/freedom" - "github.com/v2ray/v2ray-core/net/socks" -) - -func main() { - port := uint16(8888) - - uuid := "2418d087-648d-4990-86e8-19dca1d006d3" - vid, err := core.UUIDToVID(uuid) - if err != nil { - log.Fatal(err) - } - - config := core.VConfig{ - port, - []core.VUser{core.VUser{vid}}, - "", - []core.VNext{}} - - vpoint, err := core.NewVPoint(&config, socks.SocksServerFactory{}, freedom.FreedomFactory{}) - if err != nil { - log.Fatal(err) - } - err = vpoint.Start() - if err != nil { - log.Fatal(err) - } - - finish := make(chan bool) - <-finish -} diff --git a/vconfig.go b/vconfig.go index 6d57bf8cc..b8f8a4162 100644 --- a/vconfig.go +++ b/vconfig.go @@ -1,32 +1,28 @@ package core import ( - v2net "github.com/v2ray/v2ray-core/net" + "encoding/json" ) // VUser is the user account that is used for connection to a VPoint type VUser struct { - Id VID // The ID of this VUser. + Id VID `json:"id"` // The ID of this VUser. } -// VNext is the next VPoint server in the connection chain. -type VNext struct { - Address v2net.VAddress // Address of VNext server - Users []VUser // User accounts for accessing VNext. +type VConnectionConfig struct { + Protocol string `json:"protocol"` + File string `json:"file"` } // VConfig is the config for VPoint server. type VConfig struct { - Port uint16 // Port of this VPoint server. - AllowedClients []VUser - ClientProtocol string - VNextList []VNext + Port uint16 `json:"port"` // Port of this VPoint server. + InboundConfig VConnectionConfig `json:"inbound"` + OutboundConfig VConnectionConfig `json:"outbound"` } -type VConfigMarshaller interface { - Marshal(config VConfig) ([]byte, error) -} - -type VConfigUnmarshaller interface { - Unmarshal(data []byte) (VConfig, error) +func LoadVConfig(rawConfig []byte) (VConfig, error) { + config := VConfig{} + err := json.Unmarshal(rawConfig, &config) + return config, err } diff --git a/vpoint.go b/vpoint.go index de5695c94..5a3fb4506 100644 --- a/vpoint.go +++ b/vpoint.go @@ -1,31 +1,76 @@ package core import ( - "fmt" + "io/ioutil" + "github.com/v2ray/v2ray-core/log" v2net "github.com/v2ray/v2ray-core/net" ) +var ( + inboundFactories = make(map[string]InboundConnectionHandlerFactory) + outboundFactories = make(map[string]OutboundConnectionHandlerFactory) +) + +func RegisterInboundConnectionHandlerFactory(name string, factory InboundConnectionHandlerFactory) error { + // TODO check name + inboundFactories[name] = factory + return nil +} + +func RegisterOutboundConnectionHandlerFactory(name string, factory OutboundConnectionHandlerFactory) error { + // TODO check name + outboundFactories[name] = factory + return nil +} + // VPoint is an single server in V2Ray system. type VPoint struct { - Config VConfig + port uint16 ichFactory InboundConnectionHandlerFactory + ichConfig []byte ochFactory OutboundConnectionHandlerFactory + ochConfig []byte } // NewVPoint returns a new VPoint server based on given configuration. // The server is not started at this point. -func NewVPoint(config *VConfig, ichFactory InboundConnectionHandlerFactory, ochFactory OutboundConnectionHandlerFactory) (*VPoint, error) { +func NewVPoint(config VConfig) (*VPoint, error) { var vpoint = new(VPoint) - vpoint.Config = *config + 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 } type InboundConnectionHandlerFactory interface { - Create(vPoint *VPoint) (InboundConnectionHandler, error) + Create(vp *VPoint, config []byte) (InboundConnectionHandler, error) } type InboundConnectionHandler interface { @@ -33,7 +78,7 @@ type InboundConnectionHandler interface { } type OutboundConnectionHandlerFactory interface { - Create(vPoint *VPoint, dest v2net.VAddress) (OutboundConnectionHandler, error) + Create(VP *VPoint, config []byte, dest v2net.VAddress) (OutboundConnectionHandler, error) } type OutboundConnectionHandler interface { @@ -43,21 +88,21 @@ type OutboundConnectionHandler interface { // Start starts the VPoint server, and return any error during the process. // In the case of any errors, the state of the server is unpredicatable. func (vp *VPoint) Start() error { - if vp.Config.Port <= 0 { - return fmt.Errorf("Invalid port %d", vp.Config.Port) + if vp.port <= 0 { + return log.Error("Invalid port %d", vp.port) } - inboundConnectionHandler, err := vp.ichFactory.Create(vp) + inboundConnectionHandler, err := vp.ichFactory.Create(vp, vp.ichConfig) if err != nil { return err } - err = inboundConnectionHandler.Listen(vp.Config.Port) + err = inboundConnectionHandler.Listen(vp.port) return nil } func (vp *VPoint) NewInboundConnectionAccepted(destination v2net.VAddress) InboundVRay { ray := NewVRay() // TODO: handle error - och, _ := vp.ochFactory.Create(vp, destination) + och, _ := vp.ochFactory.Create(vp, vp.ochConfig, destination) _ = och.Start(ray) return ray }