diff --git a/app/proxyman/mux/frame.go b/app/proxyman/mux/frame.go index f6c64bc9e..13c5ec9eb 100644 --- a/app/proxyman/mux/frame.go +++ b/app/proxyman/mux/frame.go @@ -1,6 +1,8 @@ package mux import ( + "io" + "v2ray.com/core/common" "v2ray.com/core/common/bitmask" "v2ray.com/core/common/buf" @@ -86,18 +88,36 @@ func (f FrameMetadata) WriteTo(b *buf.Buffer) error { return nil } -// ReadFrameFrom reads a FrameMetadata from the given buffer. -// Visible for testing only. -func ReadFrameFrom(b *buf.Buffer) (*FrameMetadata, error) { - if b.Len() < 4 { - return nil, newError("insufficient buffer: ", b.Len()) +// ReadFrom reads FrameMetadata from the given reader. +func (f *FrameMetadata) ReadFrom(reader io.Reader) error { + metaLen, err := serial.ReadUint16(reader) + if err != nil { + return err + } + if metaLen > 512 { + return newError("invalid metalen ", metaLen).AtError() } - f := &FrameMetadata{ - SessionID: serial.BytesToUint16(b.BytesTo(2)), - SessionStatus: SessionStatus(b.Byte(2)), - Option: bitmask.Byte(b.Byte(3)), + b := buf.New() + defer b.Release() + + if err := b.Reset(buf.ReadFullFrom(reader, int32(metaLen))); err != nil { + return err } + return f.ReadFromBuffer(b) +} + +// ReadFromBuffer reads a FrameMetadata from the given buffer. +// Visible for testing only. +func (f *FrameMetadata) ReadFromBuffer(b *buf.Buffer) error { + if b.Len() < 4 { + return newError("insufficient buffer: ", b.Len()) + } + + f.SessionID = serial.BytesToUint16(b.BytesTo(2)) + f.SessionStatus = SessionStatus(b.Byte(2)) + f.Option = bitmask.Byte(b.Byte(3)) + f.Target.Network = net.Network_Unknown if f.SessionStatus == SessionStatusNew { network := TargetNetwork(b.Byte(4)) @@ -105,7 +125,7 @@ func ReadFrameFrom(b *buf.Buffer) (*FrameMetadata, error) { addr, port, err := addrParser.ReadAddressPort(nil, b) if err != nil { - return nil, newError("failed to parse address and port").Base(err) + return newError("failed to parse address and port").Base(err) } switch network { @@ -114,9 +134,9 @@ func ReadFrameFrom(b *buf.Buffer) (*FrameMetadata, error) { case TargetNetworkUDP: f.Target = net.UDPDestination(addr, port) default: - return nil, newError("unknown network type: ", network) + return newError("unknown network type: ", network) } } - return f, nil + return nil } diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index 1f5b4209f..732c6d20c 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -264,8 +264,9 @@ func (m *Client) fetchOutput() { reader := &buf.BufferedReader{Reader: m.link.Reader} + var meta FrameMetadata for { - meta, err := ReadMetadata(reader) + err := meta.ReadFrom(reader) if err != nil { if errors.Cause(err) != io.EOF { newError("failed to read metadata").Base(err).WriteToLog() @@ -275,15 +276,16 @@ func (m *Client) fetchOutput() { switch meta.SessionStatus { case SessionStatusKeepAlive: - err = m.handleStatueKeepAlive(meta, reader) + err = m.handleStatueKeepAlive(&meta, reader) case SessionStatusEnd: - err = m.handleStatusEnd(meta, reader) + err = m.handleStatusEnd(&meta, reader) case SessionStatusNew: - err = m.handleStatusNew(meta, reader) + err = m.handleStatusNew(&meta, reader) case SessionStatusKeep: - err = m.handleStatusKeep(meta, reader) + err = m.handleStatusKeep(&meta, reader) default: - newError("unknown status: ", meta.SessionStatus).AtError().WriteToLog() + status := meta.SessionStatus + newError("unknown status: ", status).AtError().WriteToLog() return } @@ -437,22 +439,24 @@ func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.Buffered } func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedReader) error { - meta, err := ReadMetadata(reader) + var meta FrameMetadata + err := meta.ReadFrom(reader) if err != nil { return newError("failed to read metadata").Base(err) } switch meta.SessionStatus { case SessionStatusKeepAlive: - err = w.handleStatusKeepAlive(meta, reader) + err = w.handleStatusKeepAlive(&meta, reader) case SessionStatusEnd: - err = w.handleStatusEnd(meta, reader) + err = w.handleStatusEnd(&meta, reader) case SessionStatusNew: - err = w.handleStatusNew(ctx, meta, reader) + err = w.handleStatusNew(ctx, &meta, reader) case SessionStatusKeep: - err = w.handleStatusKeep(meta, reader) + err = w.handleStatusKeep(&meta, reader) default: - return newError("unknown status: ", meta.SessionStatus).AtError() + status := meta.SessionStatus + return newError("unknown status: ", status).AtError() } if err != nil { diff --git a/app/proxyman/mux/mux_test.go b/app/proxyman/mux/mux_test.go index 0bbd1202b..f70e74cac 100644 --- a/app/proxyman/mux/mux_test.go +++ b/app/proxyman/mux/mux_test.go @@ -61,7 +61,8 @@ func TestReaderWriter(t *testing.T) { bytesReader := &buf.BufferedReader{Reader: pReader} - meta, err := ReadMetadata(bytesReader) + var meta FrameMetadata + err := meta.ReadFrom(bytesReader) assert(err, IsNil) assert(meta.SessionID, Equals, uint16(1)) assert(byte(meta.SessionStatus), Equals, byte(SessionStatusNew)) @@ -73,14 +74,14 @@ func TestReaderWriter(t *testing.T) { assert(len(data), Equals, 1) assert(data[0].String(), Equals, "abcd") - meta, err = ReadMetadata(bytesReader) + err = meta.ReadFrom(bytesReader) assert(err, IsNil) assert(byte(meta.SessionStatus), Equals, byte(SessionStatusNew)) assert(meta.SessionID, Equals, uint16(2)) assert(byte(meta.Option), Equals, byte(0)) assert(meta.Target, Equals, dest2) - meta, err = ReadMetadata(bytesReader) + err = meta.ReadFrom(bytesReader) assert(err, IsNil) assert(byte(meta.SessionStatus), Equals, byte(SessionStatusKeep)) assert(meta.SessionID, Equals, uint16(1)) @@ -91,7 +92,7 @@ func TestReaderWriter(t *testing.T) { assert(len(data), Equals, 1) assert(data[0].String(), Equals, "efgh") - meta, err = ReadMetadata(bytesReader) + err = meta.ReadFrom(bytesReader) assert(err, IsNil) assert(byte(meta.SessionStatus), Equals, byte(SessionStatusNew)) assert(meta.SessionID, Equals, uint16(3)) @@ -103,19 +104,19 @@ func TestReaderWriter(t *testing.T) { assert(len(data), Equals, 1) assert(data[0].String(), Equals, "x") - meta, err = ReadMetadata(bytesReader) + err = meta.ReadFrom(bytesReader) assert(err, IsNil) assert(byte(meta.SessionStatus), Equals, byte(SessionStatusEnd)) assert(meta.SessionID, Equals, uint16(1)) assert(byte(meta.Option), Equals, byte(0)) - meta, err = ReadMetadata(bytesReader) + err = meta.ReadFrom(bytesReader) assert(err, IsNil) assert(byte(meta.SessionStatus), Equals, byte(SessionStatusEnd)) assert(meta.SessionID, Equals, uint16(3)) assert(byte(meta.Option), Equals, byte(0)) - meta, err = ReadMetadata(bytesReader) + err = meta.ReadFrom(bytesReader) assert(err, IsNil) assert(byte(meta.SessionStatus), Equals, byte(SessionStatusKeep)) assert(meta.SessionID, Equals, uint16(2)) @@ -126,7 +127,7 @@ func TestReaderWriter(t *testing.T) { assert(len(data), Equals, 1) assert(data[0].String(), Equals, "y") - meta, err = ReadMetadata(bytesReader) + err = meta.ReadFrom(bytesReader) assert(err, IsNil) assert(byte(meta.SessionStatus), Equals, byte(SessionStatusEnd)) assert(meta.SessionID, Equals, uint16(2)) @@ -134,7 +135,6 @@ func TestReaderWriter(t *testing.T) { pWriter.Close() - meta, err = ReadMetadata(bytesReader) + err = meta.ReadFrom(bytesReader) assert(err, IsNotNil) - assert(meta, IsNil) } diff --git a/app/proxyman/mux/reader.go b/app/proxyman/mux/reader.go index 6757ca2f4..fcd243bff 100644 --- a/app/proxyman/mux/reader.go +++ b/app/proxyman/mux/reader.go @@ -8,25 +8,6 @@ import ( "v2ray.com/core/common/serial" ) -// ReadMetadata reads FrameMetadata from the given reader. -func ReadMetadata(reader io.Reader) (*FrameMetadata, error) { - metaLen, err := serial.ReadUint16(reader) - if err != nil { - return nil, err - } - if metaLen > 512 { - return nil, newError("invalid metalen ", metaLen).AtError() - } - - b := buf.New() - defer b.Release() - - if err := b.Reset(buf.ReadFullFrom(reader, int32(metaLen))); err != nil { - return nil, err - } - return ReadFrameFrom(b) -} - // PacketReader is an io.Reader that reads whole chunk of Mux frames every time. type PacketReader struct { reader io.Reader