diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 98dcca07d..ff8488bc0 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -35,15 +35,59 @@ func NewDokodemoDoor(dispatcher app.PacketDispatcher, config *json.DokodemoConfi } func (this *DokodemoDoor) Listen(port uint16) error { + this.accepting = true + if this.config.Network.HasNetwork(v2net.TCPNetwork) { err := this.ListenTCP(port) if err != nil { return err } } + if this.config.Network.HasNetwork(v2net.UDPNetwork) { + err := this.ListenUDP(port) + if err != nil { + return err + } + } return nil } +func (this *DokodemoDoor) ListenUDP(port uint16) error { + udpConn, err := net.ListenUDP("udp", &net.UDPAddr{ + 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 + } + go this.handleUDPPackets(udpConn) + return nil +} + +func (this *DokodemoDoor) handleUDPPackets(udpConn *net.UDPConn) { + defer udpConn.Close() + for this.accepting { + buffer := alloc.NewBuffer() + nBytes, addr, err := udpConn.ReadFromUDP(buffer.Value) + buffer.Slice(0, nBytes) + if err != nil { + buffer.Release() + log.Error("Dokodemo failed to read from UDP: %v", err) + return + } + + packet := v2net.NewPacket(v2net.NewUDPDestination(this.address), buffer, false) + ray := this.dispatcher.DispatchToOutbound(packet) + close(ray.InboundInput()) + + for payload := range ray.InboundOutput() { + udpConn.WriteToUDP(payload.Value, addr) + } + } +} + func (this *DokodemoDoor) ListenTCP(port uint16) error { tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, @@ -54,7 +98,6 @@ func (this *DokodemoDoor) ListenTCP(port uint16) error { log.Error("Dokodemo failed to listen on port %d: %v", port, err) return err } - this.accepting = true go this.AcceptTCPConnections(tcpListener) return nil } diff --git a/proxy/dokodemo/dokodemo_test.go b/proxy/dokodemo/dokodemo_test.go index 763e849b7..f01e71d32 100644 --- a/proxy/dokodemo/dokodemo_test.go +++ b/proxy/dokodemo/dokodemo_test.go @@ -11,6 +11,7 @@ import ( "github.com/v2ray/v2ray-core/proxy/dokodemo/config/json" _ "github.com/v2ray/v2ray-core/proxy/freedom" "github.com/v2ray/v2ray-core/testing/servers/tcp" + "github.com/v2ray/v2ray-core/testing/servers/udp" "github.com/v2ray/v2ray-core/testing/unit" ) @@ -75,3 +76,64 @@ func TestDokodemoTCP(t *testing.T) { assert.String("Processed: " + data2Send).Equals(string(response[:nBytes])) } + +func TestDokodemoUDP(t *testing.T) { + assert := unit.Assert(t) + + port := v2nettesting.PickPort() + + data2Send := "Data to be sent to remote." + + udpServer := &udp.Server{ + Port: port, + MsgProcessor: func(data []byte) []byte { + buffer := make([]byte, 0, 2048) + buffer = append(buffer, []byte("Processed: ")...) + buffer = append(buffer, data...) + return buffer + }, + } + _, err := udpServer.Start() + assert.Error(err).IsNil() + + pointPort := v2nettesting.PickPort() + networkList := v2netjson.NetworkList([]string{"udp"}) + config := mocks.Config{ + PortValue: pointPort, + InboundConfigValue: &mocks.ConnectionConfig{ + ProtocolValue: "dokodemo-door", + SettingsValue: &json.DokodemoConfig{ + Host: "127.0.0.1", + Port: int(port), + Network: &networkList, + Timeout: 0, + }, + }, + OutboundConfigValue: &mocks.ConnectionConfig{ + ProtocolValue: "freedom", + SettingsValue: nil, + }, + } + + point, err := point.NewPoint(&config) + assert.Error(err).IsNil() + + err = point.Start() + assert.Error(err).IsNil() + + udpClient, err := net.DialUDP("udp", nil, &net.UDPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(pointPort), + Zone: "", + }) + assert.Error(err).IsNil() + + udpClient.Write([]byte(data2Send)) + + response := make([]byte, 1024) + nBytes, err := udpClient.Read(response) + assert.Error(err).IsNil() + udpClient.Close() + + assert.String("Processed: " + data2Send).Equals(string(response[:nBytes])) +}