diff --git a/common/net/json/network.go b/common/net/json/network.go new file mode 100644 index 000000000..ce8e5357a --- /dev/null +++ b/common/net/json/network.go @@ -0,0 +1,32 @@ +package json + +import ( + "encoding/json" + "strings" + + v2net "github.com/v2ray/v2ray-core/common/net" +) + +type NetworkList []string + +func (this *NetworkList) UnmarshalJSON(data []byte) error { + var strList []string + err := json.Unmarshal(data, &strList) + if err != nil { + return err + } + *this = make([]string, len(strList)) + for idx, str := range strList { + (*this)[idx] = strings.ToLower(str) + } + return nil +} + +func (this *NetworkList) HasNetwork(network v2net.Network) bool { + for _, value := range *this { + if value == string(network) { + return true + } + } + return false +} diff --git a/common/net/network.go b/common/net/network.go new file mode 100644 index 000000000..bfc57e6df --- /dev/null +++ b/common/net/network.go @@ -0,0 +1,12 @@ +package net + +const ( + TCPNetwork = Network("tcp") + UDPNetwork = Network("udp") +) + +type Network string + +type NetworkList interface { + HasNetwork(Network) bool +} diff --git a/common/net/timed_io.go b/common/net/timed_io.go index 795165ca5..a56034638 100644 --- a/common/net/timed_io.go +++ b/common/net/timed_io.go @@ -22,10 +22,14 @@ func NewTimeOutReader(timeout int, connection net.Conn) *TimeOutReader { } func (reader *TimeOutReader) Read(p []byte) (n int, err error) { - deadline := time.Duration(reader.timeout) * time.Second - reader.connection.SetReadDeadline(time.Now().Add(deadline)) + if reader.timeout > 0 { + deadline := time.Duration(reader.timeout) * time.Second + reader.connection.SetReadDeadline(time.Now().Add(deadline)) + } n, err = reader.connection.Read(p) - reader.connection.SetReadDeadline(emptyTime) + if reader.timeout > 0 { + reader.connection.SetReadDeadline(emptyTime) + } return } diff --git a/proxy/dokodemo/config/json/json.go b/proxy/dokodemo/config/json/json.go new file mode 100644 index 000000000..ccc814dcf --- /dev/null +++ b/proxy/dokodemo/config/json/json.go @@ -0,0 +1,23 @@ +package json + +import ( + v2net "github.com/v2ray/v2ray-core/common/net" + v2netjson "github.com/v2ray/v2ray-core/common/net/json" + "github.com/v2ray/v2ray-core/config" + "github.com/v2ray/v2ray-core/config/json" +) + +type DokodemoConfig struct { + Host string `json:"address"` + Port int `json:"port"` + Network *v2netjson.NetworkList `json:"network"` + Timeout int `json:"timeout"` + + address v2net.Address +} + +func init() { + json.RegisterConfigType("dokodemo-door", config.TypeInbound, func() interface{} { + return new(DokodemoConfig) + }) +} diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go new file mode 100644 index 000000000..98dcca07d --- /dev/null +++ b/proxy/dokodemo/dokodemo.go @@ -0,0 +1,102 @@ +package dokodemo + +import ( + "io" + "net" + "sync" + + "github.com/v2ray/v2ray-core/app" + "github.com/v2ray/v2ray-core/common/alloc" + "github.com/v2ray/v2ray-core/common/log" + v2net "github.com/v2ray/v2ray-core/common/net" + "github.com/v2ray/v2ray-core/common/retry" + "github.com/v2ray/v2ray-core/proxy/dokodemo/config/json" +) + +type DokodemoDoor struct { + config *json.DokodemoConfig + accepting bool + address v2net.Address + dispatcher app.PacketDispatcher +} + +func NewDokodemoDoor(dispatcher app.PacketDispatcher, config *json.DokodemoConfig) *DokodemoDoor { + d := &DokodemoDoor{ + config: config, + dispatcher: dispatcher, + } + ip := net.ParseIP(config.Host) + if ip != nil { + d.address = v2net.IPAddress(ip, uint16(config.Port)) + } else { + d.address = v2net.DomainAddress(config.Host, uint16(config.Port)) + } + return d +} + +func (this *DokodemoDoor) Listen(port uint16) error { + if this.config.Network.HasNetwork(v2net.TCPNetwork) { + err := this.ListenTCP(port) + if err != nil { + return err + } + } + return nil +} + +func (this *DokodemoDoor) ListenTCP(port uint16) error { + tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{ + IP: []byte{0, 0, 0, 0}, + Port: int(port), + Zone: "", + }) + if err != nil { + log.Error("Dokodemo failed to listen on port %d: %v", port, err) + return err + } + this.accepting = true + go this.AcceptTCPConnections(tcpListener) + return nil +} + +func (this *DokodemoDoor) AcceptTCPConnections(tcpListener *net.TCPListener) { + for this.accepting { + retry.Timed(100, 100).On(func() error { + connection, err := tcpListener.AcceptTCP() + if err != nil { + log.Error("Dokodemo failed to accept new connections: %v", err) + return err + } + go this.HandleTCPConnection(connection) + return nil + }) + } +} + +func (this *DokodemoDoor) HandleTCPConnection(conn *net.TCPConn) { + defer conn.Close() + + packet := v2net.NewPacket(v2net.NewTCPDestination(this.address), nil, true) + ray := this.dispatcher.DispatchToOutbound(packet) + + var inputFinish, outputFinish sync.Mutex + inputFinish.Lock() + outputFinish.Lock() + + reader := v2net.NewTimeOutReader(this.config.Timeout, conn) + go dumpInput(reader, ray.InboundInput(), &inputFinish) + go dumpOutput(conn, ray.InboundOutput(), &outputFinish) + + outputFinish.Lock() +} + +func dumpInput(reader io.Reader, input chan<- *alloc.Buffer, finish *sync.Mutex) { + v2net.ReaderToChan(input, reader) + finish.Unlock() + close(input) +} + +func dumpOutput(writer io.Writer, output <-chan *alloc.Buffer, finish *sync.Mutex) { + v2net.ChanToWriter(writer, output) + finish.Unlock() +} diff --git a/proxy/dokodemo/dokodemo_factory.go b/proxy/dokodemo/dokodemo_factory.go new file mode 100644 index 000000000..b84521534 --- /dev/null +++ b/proxy/dokodemo/dokodemo_factory.go @@ -0,0 +1,19 @@ +package dokodemo + +import ( + "github.com/v2ray/v2ray-core/app" + "github.com/v2ray/v2ray-core/proxy/common/connhandler" + "github.com/v2ray/v2ray-core/proxy/dokodemo/config/json" +) + +type DokodemoDoorFactory struct { +} + +func (this DokodemoDoorFactory) Create(dispatcher app.PacketDispatcher, rawConfig interface{}) (connhandler.InboundConnectionHandler, error) { + config := rawConfig.(*json.DokodemoConfig) + return NewDokodemoDoor(dispatcher, config), nil +} + +func init() { + connhandler.RegisterInboundConnectionHandlerFactory("dokodemo-door", DokodemoDoorFactory{}) +} diff --git a/release/server/main.go b/release/server/main.go index 2aab87808..fb39d8968 100644 --- a/release/server/main.go +++ b/release/server/main.go @@ -12,6 +12,8 @@ import ( jsonconf "github.com/v2ray/v2ray-core/config/json" // The following are neccesary as they register handlers in their init functions. + _ "github.com/v2ray/v2ray-core/proxy/dokodemo" + _ "github.com/v2ray/v2ray-core/proxy/dokodemo/config/json" _ "github.com/v2ray/v2ray-core/proxy/freedom" _ "github.com/v2ray/v2ray-core/proxy/freedom/config/json" _ "github.com/v2ray/v2ray-core/proxy/socks"