From d065e722a1e6f88f7efc15ad1be3ba3f175e3315 Mon Sep 17 00:00:00 2001 From: Shelikhoo Date: Mon, 12 Feb 2018 11:55:39 +0800 Subject: [PATCH 001/183] Added Additional Debug output and error message for #854 --- transport/internet/udp/hub.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go index 5fb267591..99fb004e4 100644 --- a/transport/internet/udp/hub.go +++ b/transport/internet/udp/hub.go @@ -150,6 +150,11 @@ func (h *Hub) start() { payload.source = net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)) if h.option.ReceiveOriginalDest && noob > 0 { payload.originalDest = RetrieveOriginalDest(oobBytes[:noob]) + if !payload.originalDest.IsValid() { + newError("failed to read UDP Original Destination").WriteToLog() + } else { + newError("UDP Original Destination: ", payload.originalDest.String()).AtDebug().WriteToLog() + } } h.queue.Enqueue(payload) } From f4ec85d3209d2f4a2828644d832d787d5a295039 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Feb 2018 11:40:42 +0100 Subject: [PATCH 002/183] fix loading of json config --- main/config_json.go | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/main/config_json.go b/main/config_json.go index 5e294dddc..3a8b5819c 100644 --- a/main/config_json.go +++ b/main/config_json.go @@ -1,23 +1,35 @@ package main import ( + "context" "io" "os" "os/exec" "v2ray.com/core" + "v2ray.com/core/common" "v2ray.com/core/common/platform" + "v2ray.com/core/common/signal" ) +type logWriter struct{} + +func (*logWriter) Write(b []byte) (int, error) { + n, err := os.Stderr.Write(b) + if err == nil { + os.Stderr.WriteString(platform.LineSeparator()) + } + return n, err +} + func jsonToProto(input io.Reader) (*core.Config, error) { v2ctl := platform.GetToolLocation("v2ctl") - _, err := os.Stat(v2ctl) - if err != nil { + if _, err := os.Stat(v2ctl); err != nil { return nil, err } cmd := exec.Command(v2ctl, "config") cmd.Stdin = input - cmd.Stderr = os.Stderr + cmd.Stderr = &logWriter{} cmd.SysProcAttr = getSysProcAttr() stdoutReader, err := cmd.StdoutPipe() @@ -30,19 +42,34 @@ func jsonToProto(input io.Reader) (*core.Config, error) { return nil, err } - config, err := core.LoadConfig(core.ConfigFormat_Protobuf, stdoutReader) + var config *core.Config - cmd.Wait() + loadTask := signal.ExecuteAsync(func() error { + c, err := core.LoadConfig(core.ConfigFormat_Protobuf, stdoutReader) + if err != nil { + return err + } + config = c + return nil + }) - return config, err + waitTask := signal.ExecuteAsync(func() error { + return cmd.Wait() + }) + + if err := signal.ErrorOrFinish2(context.Background(), loadTask, waitTask); err != nil { + return nil, err + } + + return config, nil } func init() { - core.RegisterConfigLoader(core.ConfigFormat_JSON, func(input io.Reader) (*core.Config, error) { + common.Must(core.RegisterConfigLoader(core.ConfigFormat_JSON, func(input io.Reader) (*core.Config, error) { config, err := jsonToProto(input) if err != nil { return nil, newError("failed to execute v2ctl to convert config file.").Base(err).AtWarning() } return config, nil - }) + })) } From 94e930175ab2372ce1285ba3c73472c965066464 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Feb 2018 11:52:57 +0100 Subject: [PATCH 003/183] update log --- transport/internet/udp/hub.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go index 99fb004e4..ede84112c 100644 --- a/transport/internet/udp/hub.go +++ b/transport/internet/udp/hub.go @@ -150,10 +150,10 @@ func (h *Hub) start() { payload.source = net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)) if h.option.ReceiveOriginalDest && noob > 0 { payload.originalDest = RetrieveOriginalDest(oobBytes[:noob]) - if !payload.originalDest.IsValid() { - newError("failed to read UDP Original Destination").WriteToLog() + if payload.originalDest.IsValid() { + newError("UDP original destination: ", payload.originalDest).AtDebug().WriteToLog() } else { - newError("UDP Original Destination: ", payload.originalDest.String()).AtDebug().WriteToLog() + newError("failed to read UDP original destination").WriteToLog() } } h.queue.Enqueue(payload) From d801be71eaa3bcd65e7d2125a8a93951297ec7a0 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Feb 2018 11:53:41 +0100 Subject: [PATCH 004/183] comments --- transport/internet/udp/hub.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go index ede84112c..5651d1f50 100644 --- a/transport/internet/udp/hub.go +++ b/transport/internet/udp/hub.go @@ -112,6 +112,7 @@ func ListenUDP(address net.Address, port net.Port, option ListenOption) (*Hub, e return hub, nil } +// Close implements net.Listener. func (h *Hub) Close() error { h.conn.Close() return nil @@ -161,6 +162,7 @@ func (h *Hub) start() { h.queue.Close() } +// Addr implements net.Listener. func (h *Hub) Addr() net.Addr { return h.conn.LocalAddr() } From 03ba5ef4cb141b6fc482b83fd0eac0c69eb86f1e Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Feb 2018 12:28:18 +0100 Subject: [PATCH 005/183] properly close shadowsocks client udp connection --- proxy/shadowsocks/client.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index 3ce880c68..d622ba065 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -141,6 +141,8 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale }) requestDone := signal.ExecuteAsync(func() error { + defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) + if err := buf.Copy(outboundRay.OutboundInput(), writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all UDP request").Base(err) } @@ -149,13 +151,14 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale responseDone := signal.ExecuteAsync(func() error { defer outboundRay.OutboundOutput().Close() + defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) reader := &UDPReader{ Reader: conn, User: user, } - if err := buf.Copy(reader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer), buf.IgnoreReaderError()); err != nil { + if err := buf.Copy(reader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all UDP response").Base(err) } return nil From 7391b2439e8d893659408c18a452ae48e501f156 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Feb 2018 13:14:11 +0100 Subject: [PATCH 006/183] simplify udp hub --- app/proxyman/inbound/worker.go | 5 +- transport/internet/kcp/listener.go | 2 +- transport/internet/udp/hub.go | 113 ++++++++++++----------------- 3 files changed, 47 insertions(+), 73 deletions(-) diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 804092fec..296eb0f85 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -244,10 +244,7 @@ func (w *udpWorker) removeConn(id connID) { func (w *udpWorker) Start() error { w.activeConn = make(map[connID]*udpConn, 16) w.done = signal.NewDone() - h, err := udp.ListenUDP(w.address, w.port, udp.ListenOption{ - Callback: w.callback, - ReceiveOriginalDest: w.recvOrigDest, - }) + h, err := udp.ListenUDP(w.address, w.port, w.callback, udp.HubReceiveOriginalDestination(w.recvOrigDest)) if err != nil { return err } diff --git a/transport/internet/kcp/listener.go b/transport/internet/kcp/listener.go index 3fdcbf9f4..f35f57eff 100644 --- a/transport/internet/kcp/listener.go +++ b/transport/internet/kcp/listener.go @@ -61,7 +61,7 @@ func NewListener(ctx context.Context, address net.Address, port net.Port, addCon l.tlsConfig = config.GetTLSConfig() } - hub, err := udp.ListenUDP(address, port, udp.ListenOption{Callback: l.OnReceive, Concurrency: 2}) + hub, err := udp.ListenUDP(address, port, l.OnReceive, udp.HubCapacity(64)) if err != nil { return nil, err } diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go index 5651d1f50..52b2b510f 100644 --- a/transport/internet/udp/hub.go +++ b/transport/internet/udp/hub.go @@ -2,7 +2,6 @@ package udp import ( "v2ray.com/core/common/buf" - "v2ray.com/core/common/dice" "v2ray.com/core/common/net" ) @@ -16,71 +15,28 @@ type Payload struct { // PayloadHandler is function to handle Payload. type PayloadHandler func(payload *buf.Buffer, source net.Destination, originalDest net.Destination) -// PayloadQueue is a queue of Payload. -type PayloadQueue struct { - queue []chan Payload - callback PayloadHandler -} +type HubOption func(h *Hub) -// NewPayloadQueue returns a new PayloadQueue. -func NewPayloadQueue(option ListenOption) *PayloadQueue { - queue := &PayloadQueue{ - callback: option.Callback, - queue: make([]chan Payload, option.Concurrency), - } - for i := range queue.queue { - queue.queue[i] = make(chan Payload, 64) - go queue.Dequeue(queue.queue[i]) - } - return queue -} - -// Enqueue adds the payload to the end of this queue. -func (q *PayloadQueue) Enqueue(payload Payload) { - size := len(q.queue) - idx := 0 - if size > 1 { - idx = dice.Roll(size) - } - for i := 0; i < size; i++ { - select { - case q.queue[idx%size] <- payload: - return - default: - idx++ - } +func HubCapacity(cap int) HubOption { + return func(h *Hub) { + h.capacity = cap } } -func (q *PayloadQueue) Dequeue(queue <-chan Payload) { - for payload := range queue { - q.callback(payload.payload, payload.source, payload.originalDest) +func HubReceiveOriginalDestination(r bool) HubOption { + return func(h *Hub) { + h.recvOrigDest = r } } -func (q *PayloadQueue) Close() error { - for _, queue := range q.queue { - close(queue) - } - return nil -} - -type ListenOption struct { - Callback PayloadHandler - ReceiveOriginalDest bool - Concurrency int -} - type Hub struct { - conn *net.UDPConn - queue *PayloadQueue - option ListenOption + conn *net.UDPConn + callback PayloadHandler + capacity int + recvOrigDest bool } -func ListenUDP(address net.Address, port net.Port, option ListenOption) (*Hub, error) { - if option.Concurrency < 1 { - option.Concurrency = 1 - } +func ListenUDP(address net.Address, port net.Port, callback PayloadHandler, options ...HubOption) (*Hub, error) { udpConn, err := net.ListenUDP("udp", &net.UDPAddr{ IP: address.IP(), Port: int(port), @@ -89,7 +45,17 @@ func ListenUDP(address net.Address, port net.Port, option ListenOption) (*Hub, e return nil, err } newError("listening UDP on ", address, ":", port).WriteToLog() - if option.ReceiveOriginalDest { + hub := &Hub{ + conn: udpConn, + capacity: 16, + callback: callback, + recvOrigDest: false, + } + for _, opt := range options { + opt(hub) + } + + if hub.recvOrigDest { rawConn, err := udpConn.SyscallConn() if err != nil { return nil, newError("failed to get fd").Base(err) @@ -103,12 +69,11 @@ func ListenUDP(address net.Address, port net.Port, option ListenOption) (*Hub, e return nil, newError("failed to control socket").Base(err) } } - hub := &Hub{ - conn: udpConn, - queue: NewPayloadQueue(option), - option: option, - } - go hub.start() + + c := make(chan *Payload, hub.capacity) + + go hub.start(c) + go hub.process(c) return hub, nil } @@ -125,7 +90,15 @@ func (h *Hub) WriteTo(payload []byte, dest net.Destination) (int, error) { }) } -func (h *Hub) start() { +func (h *Hub) process(c <-chan *Payload) { + for p := range c { + h.callback(p.payload, p.source, p.originalDest) + } +} + +func (h *Hub) start(c chan<- *Payload) { + defer close(c) + oobBytes := make([]byte, 256) for { @@ -145,11 +118,11 @@ func (h *Hub) start() { break } - payload := Payload{ + payload := &Payload{ payload: buffer, } payload.source = net.UDPDestination(net.IPAddress(addr.IP), net.Port(addr.Port)) - if h.option.ReceiveOriginalDest && noob > 0 { + if h.recvOrigDest && noob > 0 { payload.originalDest = RetrieveOriginalDest(oobBytes[:noob]) if payload.originalDest.IsValid() { newError("UDP original destination: ", payload.originalDest).AtDebug().WriteToLog() @@ -157,9 +130,13 @@ func (h *Hub) start() { newError("failed to read UDP original destination").WriteToLog() } } - h.queue.Enqueue(payload) + + select { + case c <- payload: + default: + } + } - h.queue.Close() } // Addr implements net.Listener. From ae4dece6b0470f8031113067ec814d802c815b6a Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Feb 2018 14:35:42 +0100 Subject: [PATCH 007/183] explictly use the io.Writer instance for writing net.Buffers --- common/buf/writer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/buf/writer.go b/common/buf/writer.go index 85771cff5..4d26fb979 100644 --- a/common/buf/writer.go +++ b/common/buf/writer.go @@ -22,7 +22,7 @@ func (w *BufferToBytesWriter) WriteMultiBuffer(mb MultiBuffer) error { defer mb.Release() bs := mb.ToNetBuffers() - _, err := bs.WriteTo(w) + _, err := bs.WriteTo(w.Writer) return err } From ae395bbe1f8a576c2b9e9ae434b9af9010d5ced9 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Feb 2018 15:08:20 +0100 Subject: [PATCH 008/183] increase udp hub capacity --- app/proxyman/inbound/worker.go | 2 +- transport/internet/kcp/listener.go | 2 +- transport/internet/udp/hub.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 296eb0f85..044678089 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -244,7 +244,7 @@ func (w *udpWorker) removeConn(id connID) { func (w *udpWorker) Start() error { w.activeConn = make(map[connID]*udpConn, 16) w.done = signal.NewDone() - h, err := udp.ListenUDP(w.address, w.port, w.callback, udp.HubReceiveOriginalDestination(w.recvOrigDest)) + h, err := udp.ListenUDP(w.address, w.port, w.callback, udp.HubReceiveOriginalDestination(w.recvOrigDest), udp.HubCapacity(256)) if err != nil { return err } diff --git a/transport/internet/kcp/listener.go b/transport/internet/kcp/listener.go index f35f57eff..12786cc02 100644 --- a/transport/internet/kcp/listener.go +++ b/transport/internet/kcp/listener.go @@ -61,7 +61,7 @@ func NewListener(ctx context.Context, address net.Address, port net.Port, addCon l.tlsConfig = config.GetTLSConfig() } - hub, err := udp.ListenUDP(address, port, l.OnReceive, udp.HubCapacity(64)) + hub, err := udp.ListenUDP(address, port, l.OnReceive, udp.HubCapacity(1024)) if err != nil { return nil, err } diff --git a/transport/internet/udp/hub.go b/transport/internet/udp/hub.go index 52b2b510f..d92df47d9 100644 --- a/transport/internet/udp/hub.go +++ b/transport/internet/udp/hub.go @@ -47,7 +47,7 @@ func ListenUDP(address net.Address, port net.Port, callback PayloadHandler, opti newError("listening UDP on ", address, ":", port).WriteToLog() hub := &Hub{ conn: udpConn, - capacity: 16, + capacity: 256, callback: callback, recvOrigDest: false, } From 31d1fb6cc3afeb95c4654659fe664fe86d849659 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Feb 2018 21:35:10 +0100 Subject: [PATCH 009/183] fix lint errors --- app/proxyman/inbound/worker.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 044678089..b7d84cf61 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -62,7 +62,9 @@ func (w *tcpWorker) callback(conn internet.Connection) { newError("connection ends").Base(err).WriteToLog() } cancel() - conn.Close() + if err := conn.Close(); err != nil { + newError("failed to close connection").Base(err).WriteToLog() + } } func (w *tcpWorker) Proxy() proxy.Inbound { @@ -128,7 +130,7 @@ func (c *udpConn) Write(buf []byte) (int, error) { } func (c *udpConn) Close() error { - common.Close(c.done) + common.Must(c.done.Close()) return nil } @@ -254,11 +256,15 @@ func (w *udpWorker) Start() error { } func (w *udpWorker) Close() error { + w.Lock() + defer w.Unlock() + if w.hub != nil { w.hub.Close() - w.done.Close() - common.Close(w.proxy) } + + common.Must(w.done.Close()) + common.Close(w.proxy) return nil } From ccb2a9f168cf95e635ba44051e8a4bed5e03115f Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 13 Feb 2018 11:15:04 +0100 Subject: [PATCH 010/183] comments --- common/buf/copy.go | 1 + common/buf/io.go | 1 + common/buf/reader.go | 16 +++++++++------- common/buf/writer.go | 3 +++ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/common/buf/copy.go b/common/buf/copy.go index d4f65235d..7d19efdda 100644 --- a/common/buf/copy.go +++ b/common/buf/copy.go @@ -36,6 +36,7 @@ func (h *copyHandler) writeTo(writer Writer, mb MultiBuffer) error { return err } +// SizeCounter is for counting bytes copied by Copy(). type SizeCounter struct { Size int64 } diff --git a/common/buf/io.go b/common/buf/io.go index 17debb2b5..e3c8aa338 100644 --- a/common/buf/io.go +++ b/common/buf/io.go @@ -67,6 +67,7 @@ func NewWriter(writer io.Writer) Writer { } } +// NewSequentialWriter returns a Writer that write Buffers in a MultiBuffer sequentially. func NewSequentialWriter(writer io.Writer) Writer { return &seqWriter{ writer: writer, diff --git a/common/buf/reader.go b/common/buf/reader.go index 574bd5f61..45dc4ef32 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -12,6 +12,7 @@ type BytesToBufferReader struct { buffer []byte } +// NewBytesToBufferReader returns a new BytesToBufferReader. func NewBytesToBufferReader(reader io.Reader) Reader { return &BytesToBufferReader{ Reader: reader, @@ -52,19 +53,14 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) { return nil, err } -var ( - _ Reader = (*BufferedReader)(nil) - _ io.Reader = (*BufferedReader)(nil) - _ io.ByteReader = (*BufferedReader)(nil) - _ io.WriterTo = (*BufferedReader)(nil) -) - +// BufferedReader is a Reader that keeps its internal buffer. type BufferedReader struct { stream Reader leftOver MultiBuffer buffered bool } +// NewBufferedReader returns a new BufferedReader. func NewBufferedReader(reader Reader) *BufferedReader { return &BufferedReader{ stream: reader, @@ -72,20 +68,24 @@ func NewBufferedReader(reader Reader) *BufferedReader { } } +// SetBuffered sets whether to keep the interal buffer. func (r *BufferedReader) SetBuffered(f bool) { r.buffered = f } +// IsBuffered returns true if internal buffer is used. func (r *BufferedReader) IsBuffered() bool { return r.buffered } +// ReadByte implements io.ByteReader. func (r *BufferedReader) ReadByte() (byte, error) { var b [1]byte _, err := r.Read(b[:]) return b[0], err } +// Read implements io.Reader. It reads from internal buffer first (if available) and then reads from the underlying reader. func (r *BufferedReader) Read(b []byte) (int, error) { if r.leftOver != nil { nBytes, _ := r.leftOver.Read(b) @@ -113,6 +113,7 @@ func (r *BufferedReader) Read(b []byte) (int, error) { return 0, err } +// ReadMultiBuffer implements Reader. func (r *BufferedReader) ReadMultiBuffer() (MultiBuffer, error) { if r.leftOver != nil { mb := r.leftOver @@ -164,6 +165,7 @@ func (r *BufferedReader) writeToInternal(writer io.Writer) (int64, error) { } } +// WriteTo implements io.WriterTo. func (r *BufferedReader) WriteTo(writer io.Writer) (int64, error) { nBytes, err := r.writeToInternal(writer) if errors.Cause(err) == io.EOF { diff --git a/common/buf/writer.go b/common/buf/writer.go index 4d26fb979..05798cf8b 100644 --- a/common/buf/writer.go +++ b/common/buf/writer.go @@ -11,6 +11,7 @@ type BufferToBytesWriter struct { io.Writer } +// NewBufferToBytesWriter returns a new BufferToBytesWriter. func NewBufferToBytesWriter(writer io.Writer) *BufferToBytesWriter { return &BufferToBytesWriter{ Writer: writer, @@ -49,6 +50,7 @@ func NewBufferedWriter(writer Writer) *BufferedWriter { } } +// WriteByte implements io.ByteWriter. func (w *BufferedWriter) WriteByte(c byte) error { _, err := w.Write([]byte{c}) return err @@ -121,6 +123,7 @@ func (w *BufferedWriter) Flush() error { return nil } +// SetBuffered sets whether the internal buffer is used. If set to false, Flush() will be called to clear the buffer. func (w *BufferedWriter) SetBuffered(f bool) error { w.buffered = f if !f { From 1e0b35f8692f18d5b7e6c76b13ac5f6ad171b4da Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 14 Feb 2018 13:41:21 +0100 Subject: [PATCH 011/183] fix nil reference in udp worker --- app/proxyman/inbound/worker.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index b7d84cf61..1c63be3d2 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -263,7 +263,10 @@ func (w *udpWorker) Close() error { w.hub.Close() } - common.Must(w.done.Close()) + if w.done != nil { + common.Must(w.done.Close()) + } + common.Close(w.proxy) return nil } From c48fa50ab1d145906ba6cc4c3f14761e9def5955 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 14 Feb 2018 17:35:09 +0100 Subject: [PATCH 012/183] logger service --- app/log/command/command.go | 50 ++++++++++ app/log/command/command_test.go | 31 ++++++ app/log/command/config.pb.go | 143 ++++++++++++++++++++++++++++ app/log/command/config.proto | 18 ++++ app/log/command/errors.generated.go | 5 + app/log/log.go | 57 +++++++---- common/interfaces.go | 6 ++ common/log/logger.go | 8 ++ v2ray.go | 12 +++ 9 files changed, 313 insertions(+), 17 deletions(-) create mode 100644 app/log/command/command.go create mode 100644 app/log/command/command_test.go create mode 100644 app/log/command/config.pb.go create mode 100644 app/log/command/config.proto create mode 100644 app/log/command/errors.generated.go diff --git a/app/log/command/command.go b/app/log/command/command.go new file mode 100644 index 000000000..84d3078c9 --- /dev/null +++ b/app/log/command/command.go @@ -0,0 +1,50 @@ +package command + +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg command -path App,Log,Command + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" + + "v2ray.com/core" + "v2ray.com/core/app/log" + "v2ray.com/core/common" +) + +type LoggerServer struct { + V *core.Instance +} + +func (s *LoggerServer) RestartLogger(ctx context.Context, request *RestartLoggerRequest) (*RestartLoggerResponse, error) { + logger := s.V.GetFeature((*log.Instance)(nil)) + if logger == nil { + return nil, newError("unable to get logger instance") + } + if err := logger.Close(); err != nil { + return nil, newError("failed to close logger").Base(err) + } + if err := logger.Start(); err != nil { + return nil, newError("failed to start logger").Base(err) + } + return &RestartLoggerResponse{}, nil +} + +type service struct { + v *core.Instance +} + +func (s *service) Register(server *grpc.Server) { + RegisterLoggerServiceServer(server, &LoggerServer{ + V: s.v, + }) +} + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { + s := core.FromContext(ctx) + if s == nil { + return nil, newError("V is not in context.") + } + return &service{v: s}, nil + })) +} diff --git a/app/log/command/command_test.go b/app/log/command/command_test.go new file mode 100644 index 000000000..44ebb15f0 --- /dev/null +++ b/app/log/command/command_test.go @@ -0,0 +1,31 @@ +package command_test + +import ( + "context" + "testing" + + "v2ray.com/core" + "v2ray.com/core/app/log" + . "v2ray.com/core/app/log/command" + "v2ray.com/core/common/serial" + . "v2ray.com/ext/assert" +) + +func TestLoggerRestart(t *testing.T) { + assert := With(t) + + v, err := core.New(&core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{}), + }, + }) + + assert(err, IsNil) + assert(v.Start(), IsNil) + + server := &LoggerServer{ + V: v, + } + _, err = server.RestartLogger(context.Background(), &RestartLoggerRequest{}) + assert(err, IsNil) +} diff --git a/app/log/command/config.pb.go b/app/log/command/config.pb.go new file mode 100644 index 000000000..c7f0f5424 --- /dev/null +++ b/app/log/command/config.pb.go @@ -0,0 +1,143 @@ +package command + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Config struct { +} + +func (m *Config) Reset() { *m = Config{} } +func (m *Config) String() string { return proto.CompactTextString(m) } +func (*Config) ProtoMessage() {} +func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type RestartLoggerRequest struct { +} + +func (m *RestartLoggerRequest) Reset() { *m = RestartLoggerRequest{} } +func (m *RestartLoggerRequest) String() string { return proto.CompactTextString(m) } +func (*RestartLoggerRequest) ProtoMessage() {} +func (*RestartLoggerRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type RestartLoggerResponse struct { +} + +func (m *RestartLoggerResponse) Reset() { *m = RestartLoggerResponse{} } +func (m *RestartLoggerResponse) String() string { return proto.CompactTextString(m) } +func (*RestartLoggerResponse) ProtoMessage() {} +func (*RestartLoggerResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func init() { + proto.RegisterType((*Config)(nil), "v2ray.core.app.log.command.Config") + proto.RegisterType((*RestartLoggerRequest)(nil), "v2ray.core.app.log.command.RestartLoggerRequest") + proto.RegisterType((*RestartLoggerResponse)(nil), "v2ray.core.app.log.command.RestartLoggerResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for LoggerService service + +type LoggerServiceClient interface { + RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) +} + +type loggerServiceClient struct { + cc *grpc.ClientConn +} + +func NewLoggerServiceClient(cc *grpc.ClientConn) LoggerServiceClient { + return &loggerServiceClient{cc} +} + +func (c *loggerServiceClient) RestartLogger(ctx context.Context, in *RestartLoggerRequest, opts ...grpc.CallOption) (*RestartLoggerResponse, error) { + out := new(RestartLoggerResponse) + err := grpc.Invoke(ctx, "/v2ray.core.app.log.command.LoggerService/RestartLogger", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for LoggerService service + +type LoggerServiceServer interface { + RestartLogger(context.Context, *RestartLoggerRequest) (*RestartLoggerResponse, error) +} + +func RegisterLoggerServiceServer(s *grpc.Server, srv LoggerServiceServer) { + s.RegisterService(&_LoggerService_serviceDesc, srv) +} + +func _LoggerService_RestartLogger_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RestartLoggerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoggerServiceServer).RestartLogger(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v2ray.core.app.log.command.LoggerService/RestartLogger", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoggerServiceServer).RestartLogger(ctx, req.(*RestartLoggerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _LoggerService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v2ray.core.app.log.command.LoggerService", + HandlerType: (*LoggerServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RestartLogger", + Handler: _LoggerService_RestartLogger_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "v2ray.com/core/app/log/command/config.proto", +} + +func init() { proto.RegisterFile("v2ray.com/core/app/log/command/config.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 210 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x2e, 0x33, 0x2a, 0x4a, + 0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0xcf, + 0xc9, 0x4f, 0xd7, 0x4f, 0xce, 0xcf, 0xcd, 0x4d, 0xcc, 0x4b, 0xd1, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, + 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x82, 0x29, 0x2e, 0x4a, 0xd5, 0x4b, 0x2c, + 0x28, 0xd0, 0xcb, 0xc9, 0x4f, 0xd7, 0x83, 0x2a, 0x54, 0xe2, 0xe0, 0x62, 0x73, 0x06, 0xab, 0x55, + 0x12, 0xe3, 0x12, 0x09, 0x4a, 0x2d, 0x2e, 0x49, 0x2c, 0x2a, 0xf1, 0xc9, 0x4f, 0x4f, 0x4f, 0x2d, + 0x0a, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x51, 0x12, 0xe7, 0x12, 0x45, 0x13, 0x2f, 0x2e, 0xc8, + 0xcf, 0x2b, 0x4e, 0x35, 0x6a, 0x67, 0xe4, 0xe2, 0x85, 0x08, 0x05, 0xa7, 0x16, 0x95, 0x65, 0x26, + 0xa7, 0x0a, 0x95, 0x71, 0xf1, 0xa2, 0x28, 0x15, 0x32, 0xd0, 0xc3, 0x6d, 0xb5, 0x1e, 0x36, 0xdb, + 0xa4, 0x0c, 0x49, 0xd0, 0x01, 0x71, 0x87, 0x12, 0x83, 0x93, 0x07, 0x97, 0x5c, 0x72, 0x7e, 0x2e, + 0x1e, 0x9d, 0x01, 0x8c, 0x51, 0xec, 0x50, 0xe6, 0x2a, 0x26, 0xa9, 0x30, 0xa3, 0xa0, 0xc4, 0x4a, + 0x3d, 0x67, 0x90, 0x3a, 0xc7, 0x82, 0x02, 0x3d, 0x9f, 0xfc, 0x74, 0x3d, 0x67, 0x88, 0x64, 0x12, + 0x1b, 0x38, 0xc4, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x37, 0xc7, 0xfc, 0xda, 0x60, 0x01, + 0x00, 0x00, +} diff --git a/app/log/command/config.proto b/app/log/command/config.proto new file mode 100644 index 000000000..39e2f33f3 --- /dev/null +++ b/app/log/command/config.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package v2ray.core.app.log.command; +option csharp_namespace = "V2Ray.Core.App.Log.Command"; +option go_package = "command"; +option java_package = "com.v2ray.core.app.log.command"; +option java_multiple_files = true; + +message Config { +} + +message RestartLoggerRequest {} + +message RestartLoggerResponse{} + +service LoggerService { + rpc RestartLogger(RestartLoggerRequest) returns (RestartLoggerResponse) {} +} \ No newline at end of file diff --git a/app/log/command/errors.generated.go b/app/log/command/errors.generated.go new file mode 100644 index 000000000..644816857 --- /dev/null +++ b/app/log/command/errors.generated.go @@ -0,0 +1,5 @@ +package command + +import "v2ray.com/core/common/errors" + +func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Log", "Command") } diff --git a/app/log/log.go b/app/log/log.go index c2dc5e579..30041f863 100644 --- a/app/log/log.go +++ b/app/log/log.go @@ -6,6 +6,7 @@ import ( "context" "sync" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/log" ) @@ -26,13 +27,10 @@ func New(ctx context.Context, config *Config) (*Instance, error) { active: true, } - if err := g.initAccessLogger(); err != nil { - return nil, newError("failed to initialize access logger").Base(err).AtWarning() + v := core.FromContext(ctx) + if v != nil { + common.Must(v.RegisterFeature((*log.Handler)(nil), g)) } - if err := g.initErrorLogger(); err != nil { - return nil, newError("failed to initialize error logger").Base(err).AtWarning() - } - log.RegisterHandler(g) return g, nil } @@ -67,24 +65,40 @@ func (g *Instance) initErrorLogger() error { return nil } -// Start implements app.Application.Start(). -func (g *Instance) Start() error { - g.Lock() - defer g.Unlock() - g.active = true - return nil +func (*Instance) Type() interface{} { + return (*Instance)(nil) } -func (g *Instance) isActive() bool { - g.RLock() - defer g.RUnlock() +// Start implements app.Application.Start(). +func (g *Instance) Start() error { + newError("Logger starting").AtDebug().WriteToLog() - return g.active + g.Lock() + defer g.Unlock() + + if g.active { + return nil + } + + g.active = true + + if err := g.initAccessLogger(); err != nil { + return newError("failed to initialize access logger").Base(err).AtWarning() + } + if err := g.initErrorLogger(); err != nil { + return newError("failed to initialize error logger").Base(err).AtWarning() + } + log.RegisterHandler(g) + + return nil } // Handle implements log.Handler. func (g *Instance) Handle(msg log.Message) { - if !g.isActive() { + g.RLock() + defer g.RUnlock() + + if !g.active { return } @@ -104,11 +118,20 @@ func (g *Instance) Handle(msg log.Message) { // Close implement app.Application.Close(). func (g *Instance) Close() error { + newError("Logger closing").AtDebug().WriteToLog() + g.Lock() defer g.Unlock() + if !g.active { + return nil + } + g.active = false + common.Close(g.accessLogger) + common.Close(g.errorLogger) + return nil } diff --git a/common/interfaces.go b/common/interfaces.go index d5cc177e6..f08235de5 100644 --- a/common/interfaces.go +++ b/common/interfaces.go @@ -21,3 +21,9 @@ type Runnable interface { Closable } + +// HasType is the interface for objects that knows its type. +type HasType interface { + // Type returns the type of the object. + Type() interface{} +} diff --git a/common/log/logger.go b/common/log/logger.go index a07100bc9..b3008720b 100644 --- a/common/log/logger.go +++ b/common/log/logger.go @@ -23,6 +23,7 @@ type generalLogger struct { creator WriterCreator buffer chan Message access *signal.Semaphore + done *signal.Done } // NewLogger returns a generic log handler that can handle all type of messages. @@ -31,6 +32,7 @@ func NewLogger(logWriterCreator WriterCreator) Handler { creator: logWriterCreator, buffer: make(chan Message, 16), access: signal.NewSemaphore(1), + done: signal.NewDone(), } } @@ -49,6 +51,8 @@ func (l *generalLogger) run() { for { select { + case <-l.done.C(): + return case msg := <-l.buffer: logger.Write(msg.String() + platform.LineSeparator()) dataWritten = true @@ -74,6 +78,10 @@ func (l *generalLogger) Handle(msg Message) { } } +func (l *generalLogger) Close() error { + return l.done.Close() +} + type consoleLogWriter struct { logger *log.Logger } diff --git a/v2ray.go b/v2ray.go index a95a14b5e..1d35af7cf 100644 --- a/v2ray.go +++ b/v2ray.go @@ -163,6 +163,18 @@ func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error return nil } +// GetFeature returns a feature that was registered in this Instance. Nil if not found. +func (s *Instance) GetFeature(featureType interface{}) Feature { + for _, f := range s.features { + if hasType, ok := f.(common.HasType); ok { + if hasType.Type() == featureType { + return f + } + } + } + return nil +} + // DNSClient returns the DNSClient used by this Instance. The returned DNSClient is always functional. func (s *Instance) DNSClient() DNSClient { return &(s.dnsClient) From 42e9af63aa0f12b1789615bec6ba0c2c9ba4aca6 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 14 Feb 2018 21:59:58 +0100 Subject: [PATCH 013/183] details about GetFeature --- v2ray.go | 1 + 1 file changed, 1 insertion(+) diff --git a/v2ray.go b/v2ray.go index 1d35af7cf..df4b04207 100644 --- a/v2ray.go +++ b/v2ray.go @@ -164,6 +164,7 @@ func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error } // GetFeature returns a feature that was registered in this Instance. Nil if not found. +// The returned Feature must implement common.HasType and whose type equals the given feature type. func (s *Instance) GetFeature(featureType interface{}) Feature { for _, f := range s.features { if hasType, ok := f.(common.HasType); ok { From 84f8192b61e9791c89ac2a0e136f9335bbe0621f Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 14 Feb 2018 22:00:08 +0100 Subject: [PATCH 014/183] fix default logger --- app/log/log.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/app/log/log.go b/app/log/log.go index 30041f863..d5205d104 100644 --- a/app/log/log.go +++ b/app/log/log.go @@ -24,8 +24,9 @@ type Instance struct { func New(ctx context.Context, config *Config) (*Instance, error) { g := &Instance{ config: config, - active: true, + active: false, } + log.RegisterHandler(g) v := core.FromContext(ctx) if v != nil { @@ -65,14 +66,12 @@ func (g *Instance) initErrorLogger() error { return nil } +// Type implements common.HasType. func (*Instance) Type() interface{} { return (*Instance)(nil) } -// Start implements app.Application.Start(). -func (g *Instance) Start() error { - newError("Logger starting").AtDebug().WriteToLog() - +func (g *Instance) startInternal() error { g.Lock() defer g.Unlock() @@ -88,7 +87,17 @@ func (g *Instance) Start() error { if err := g.initErrorLogger(); err != nil { return newError("failed to initialize error logger").Base(err).AtWarning() } - log.RegisterHandler(g) + + return nil +} + +// Start implements app.Application.Start(). +func (g *Instance) Start() error { + if err := g.startInternal(); err != nil { + return err + } + + newError("Logger started").AtDebug().WriteToLog() return nil } @@ -130,7 +139,10 @@ func (g *Instance) Close() error { g.active = false common.Close(g.accessLogger) + g.accessLogger = nil + common.Close(g.errorLogger) + g.errorLogger = nil return nil } From 94125236e66c2ae77600c4f8b8cef9c8e7c99a52 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 14 Feb 2018 22:00:15 +0100 Subject: [PATCH 015/183] include log command --- main/distro/all/all.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main/distro/all/all.go b/main/distro/all/all.go index c66e22acb..8f5524beb 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -10,6 +10,7 @@ import ( // Default commander and all its services. _ "v2ray.com/core/app/commander" + _ "v2ray.com/core/app/log/command" _ "v2ray.com/core/app/proxyman/command" // Other optional features. From 2a07838bb933188a6463ccea46d64b5242654589 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 14 Feb 2018 23:57:40 +0100 Subject: [PATCH 016/183] extendable config format --- config.go | 87 +++++++++++++++++++++++++++++++++++++++++++++ config.pb.go | 79 ++++++++++++++-------------------------- config.proto | 6 ---- loader.go | 46 ------------------------ main/config_json.go | 18 ++++++---- main/main.go | 10 +++--- 6 files changed, 129 insertions(+), 117 deletions(-) create mode 100644 config.go delete mode 100644 loader.go diff --git a/config.go b/config.go new file mode 100644 index 000000000..fc45ac1ab --- /dev/null +++ b/config.go @@ -0,0 +1,87 @@ +package core + +import ( + "io" + "strings" + + "github.com/golang/protobuf/proto" + "v2ray.com/core/common" + "v2ray.com/core/common/buf" +) + +type ConfigFormat struct { + Name string + Extension []string + Loader ConfigLoader +} + +// ConfigLoader is an utility to load V2Ray config from external source. +type ConfigLoader func(input io.Reader) (*Config, error) + +var ( + configLoaderByName = make(map[string]*ConfigFormat) + configLoaderByExt = make(map[string]*ConfigFormat) +) + +// RegisterConfigLoader add a new ConfigLoader. +func RegisterConfigLoader(format *ConfigFormat) error { + name := strings.ToLower(format.Name) + if _, found := configLoaderByName[name]; found { + return newError(format.Name, " already registered.") + } + configLoaderByName[name] = format + + for _, ext := range format.Extension { + lext := strings.ToLower(ext) + if f, found := configLoaderByExt[lext]; found { + return newError(ext, " already registered to ", f.Name) + } + configLoaderByExt[lext] = format + } + + return nil +} + +func getExtension(filename string) string { + idx := strings.LastIndexByte(filename, '.') + if idx == -1 { + return "" + } + return filename[idx+1:] +} + +// LoadConfig loads config with given format from given source. +func LoadConfig(formatName string, filename string, input io.Reader) (*Config, error) { + ext := getExtension(filename) + if len(ext) > 0 { + if f, found := configLoaderByExt[ext]; found { + return f.Loader(input) + } + } + + if f, found := configLoaderByName[formatName]; found { + return f.Loader(input) + } + + return nil, newError("Unable to load config in ", formatName).AtWarning() +} + +func loadProtobufConfig(input io.Reader) (*Config, error) { + config := new(Config) + data, err := buf.ReadAllToBytes(input) + if err != nil { + return nil, err + } + if err := proto.Unmarshal(data, config); err != nil { + return nil, err + } + return config, nil +} + +func init() { + common.Must(RegisterConfigLoader(&ConfigFormat{ + Name: "Protobuf", + Extension: []string{"pb"}, + Loader: loadProtobufConfig, + })) +} diff --git a/config.pb.go b/config.pb.go index 1429f6e22..ac34e2404 100644 --- a/config.pb.go +++ b/config.pb.go @@ -17,28 +17,6 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package -// Configuration serialization format. -type ConfigFormat int32 - -const ( - ConfigFormat_Protobuf ConfigFormat = 0 - ConfigFormat_JSON ConfigFormat = 1 -) - -var ConfigFormat_name = map[int32]string{ - 0: "Protobuf", - 1: "JSON", -} -var ConfigFormat_value = map[string]int32{ - "Protobuf": 0, - "JSON": 1, -} - -func (x ConfigFormat) String() string { - return proto.EnumName(ConfigFormat_name, int32(x)) -} -func (ConfigFormat) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - // Master config of V2Ray. V2Ray takes this config as input and functions accordingly. type Config struct { // Inbound handler configurations. Must have at least one item. @@ -186,39 +164,36 @@ func init() { proto.RegisterType((*Config)(nil), "v2ray.core.Config") proto.RegisterType((*InboundHandlerConfig)(nil), "v2ray.core.InboundHandlerConfig") proto.RegisterType((*OutboundHandlerConfig)(nil), "v2ray.core.OutboundHandlerConfig") - proto.RegisterEnum("v2ray.core.ConfigFormat", ConfigFormat_name, ConfigFormat_value) } func init() { proto.RegisterFile("v2ray.com/core/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 436 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x93, 0x41, 0x6f, 0xd3, 0x30, - 0x14, 0xc7, 0x71, 0x13, 0xba, 0xf6, 0x6d, 0x94, 0x60, 0x01, 0xb2, 0x06, 0x87, 0x50, 0x69, 0x53, - 0xc5, 0xc1, 0x45, 0xe5, 0x82, 0x26, 0x71, 0x61, 0x08, 0xc1, 0xa4, 0xd1, 0x29, 0x45, 0x1c, 0xb8, - 0x4c, 0x6e, 0xfa, 0x56, 0x45, 0x5a, 0xec, 0xc8, 0x76, 0xa7, 0xe6, 0x2b, 0xf1, 0x3d, 0xb8, 0xf1, - 0x8d, 0xb8, 0xa0, 0xc4, 0x49, 0x93, 0x41, 0x0f, 0x14, 0x69, 0xa7, 0xc4, 0x79, 0xfe, 0xfd, 0xdf, - 0xfb, 0x25, 0x31, 0x3c, 0xbb, 0x99, 0x68, 0x91, 0xf3, 0x58, 0xa5, 0xe3, 0x58, 0x69, 0x1c, 0xc7, - 0x4a, 0x5e, 0x25, 0x4b, 0x9e, 0x69, 0x65, 0x15, 0x85, 0xba, 0xa8, 0xf1, 0xf0, 0xd5, 0x5f, 0x1b, - 0xd3, 0x54, 0xc9, 0xb1, 0x41, 0x9d, 0x88, 0xeb, 0xb1, 0xcd, 0x33, 0x5c, 0x5c, 0xa6, 0x68, 0x8c, - 0x58, 0xa2, 0xa3, 0x0f, 0x8f, 0xfe, 0x20, 0xac, 0x16, 0xd2, 0x64, 0x4a, 0xdb, 0x5b, 0x4d, 0x86, - 0x3f, 0x3a, 0xd0, 0x3d, 0x2d, 0x1f, 0xd0, 0x13, 0xd8, 0x4b, 0xe4, 0x5c, 0xad, 0xe4, 0x82, 0x91, - 0xd0, 0x1b, 0xed, 0x4f, 0x42, 0xde, 0x4c, 0xc0, 0x3f, 0xb9, 0xd2, 0x47, 0x21, 0x17, 0xd7, 0xa8, - 0x1d, 0x12, 0xd5, 0x00, 0x7d, 0x0b, 0x3d, 0xb5, 0xb2, 0x0e, 0xee, 0x94, 0xf0, 0x8b, 0x36, 0x3c, - 0xad, 0x6a, 0xb7, 0xe9, 0x0d, 0x42, 0xdf, 0x80, 0x27, 0xb2, 0x8c, 0xf9, 0x25, 0x79, 0xdc, 0x26, - 0x9d, 0x28, 0x77, 0xa2, 0xfc, 0x4b, 0x21, 0x7a, 0xee, 0x3c, 0xa3, 0x02, 0xa1, 0x27, 0xd0, 0xdf, - 0x98, 0xb1, 0xfb, 0x21, 0x19, 0xed, 0x4f, 0x9e, 0xb7, 0xf9, 0x4d, 0x91, 0x57, 0x4d, 0x9b, 0xed, - 0xf4, 0x3d, 0xf4, 0x71, 0x6d, 0x51, 0x9a, 0x44, 0x49, 0xd6, 0xdd, 0xa9, 0x77, 0x03, 0x9e, 0xf9, - 0x3d, 0x2f, 0xf0, 0x87, 0x3f, 0x09, 0x3c, 0xde, 0xf6, 0x8a, 0x68, 0x00, 0x9e, 0x15, 0x4b, 0x46, - 0x42, 0x32, 0xea, 0x47, 0xc5, 0x2d, 0x9d, 0xc1, 0x23, 0x8d, 0x31, 0x26, 0x37, 0xa8, 0x2f, 0x0d, - 0x5a, 0x9b, 0xc8, 0xa5, 0x61, 0x9d, 0x72, 0xf4, 0x7f, 0x6d, 0x1f, 0xd4, 0x01, 0xb3, 0x8a, 0xa7, - 0xe7, 0x30, 0xc8, 0xb4, 0x5a, 0xe7, 0x4d, 0xa2, 0xb7, 0x53, 0xe2, 0x83, 0x92, 0xae, 0xe3, 0x86, - 0xbf, 0x08, 0x3c, 0xd9, 0xfa, 0xd1, 0xb6, 0xf8, 0x4c, 0xe1, 0xa1, 0x41, 0xb9, 0xf8, 0x7f, 0x9b, - 0x81, 0xc3, 0xef, 0xc8, 0x85, 0x3e, 0x85, 0x2e, 0xae, 0xb3, 0x44, 0x23, 0xf3, 0x43, 0x32, 0xf2, - 0xa2, 0x6a, 0x45, 0x19, 0xec, 0x15, 0x21, 0x28, 0xdd, 0x8f, 0xd3, 0x8f, 0xea, 0xe5, 0xcb, 0x63, - 0x38, 0x70, 0xb6, 0x1f, 0x94, 0x4e, 0x85, 0xa5, 0x07, 0xd0, 0xbb, 0x28, 0x4e, 0xcb, 0x7c, 0x75, - 0x15, 0xdc, 0xa3, 0x3d, 0xf0, 0xcf, 0x66, 0xd3, 0xcf, 0x01, 0x79, 0x77, 0x04, 0x83, 0x58, 0xa5, - 0xad, 0xa9, 0x2e, 0xc8, 0x37, 0xbf, 0xb8, 0x7e, 0xef, 0xc0, 0xd7, 0x49, 0x24, 0x72, 0x7e, 0xaa, - 0x34, 0xce, 0xbb, 0xe5, 0x51, 0x7b, 0xfd, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08, 0x59, 0x1b, - 0xee, 0x03, 0x00, 0x00, + // 405 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x93, 0x41, 0x6f, 0x94, 0x40, + 0x1c, 0xc5, 0x33, 0x0b, 0x6e, 0xcb, 0xbf, 0x71, 0x5d, 0x27, 0x6a, 0x26, 0xd5, 0x03, 0x6e, 0x52, + 0xc3, 0x69, 0x30, 0x78, 0x31, 0x4d, 0xbc, 0x58, 0x0f, 0x6a, 0xd2, 0xd4, 0x50, 0xe3, 0xc1, 0x4b, + 0x33, 0x85, 0xbf, 0x84, 0xa4, 0xcc, 0x90, 0x99, 0x69, 0xb3, 0x7c, 0x25, 0xbf, 0x87, 0x37, 0xbf, + 0x91, 0x17, 0x03, 0x03, 0x0b, 0xd5, 0x3d, 0xb8, 0x26, 0x3d, 0xc1, 0xf0, 0xe6, 0xf7, 0xde, 0xff, + 0x01, 0x03, 0x4f, 0x6f, 0x12, 0x2d, 0x1a, 0x9e, 0xa9, 0x2a, 0xce, 0x94, 0xc6, 0x38, 0x53, 0xf2, + 0x5b, 0x59, 0xf0, 0x5a, 0x2b, 0xab, 0x28, 0x0c, 0xa2, 0xc6, 0xc3, 0x97, 0x7f, 0x6d, 0xac, 0x2a, + 0x25, 0x63, 0x83, 0xba, 0x14, 0x57, 0xb1, 0x6d, 0x6a, 0xcc, 0x2f, 0x2a, 0x34, 0x46, 0x14, 0xe8, + 0xe8, 0xc3, 0xa3, 0x3f, 0x08, 0xab, 0x85, 0x34, 0xb5, 0xd2, 0xf6, 0x56, 0xc8, 0xea, 0xc7, 0x0c, + 0xe6, 0x27, 0xdd, 0x03, 0x7a, 0x0c, 0x7b, 0xa5, 0xbc, 0x54, 0xd7, 0x32, 0x67, 0x24, 0xf4, 0xa2, + 0x83, 0x24, 0xe4, 0xe3, 0x04, 0xfc, 0x83, 0x93, 0xde, 0x0b, 0x99, 0x5f, 0xa1, 0x76, 0x48, 0x3a, + 0x00, 0xf4, 0x0d, 0xec, 0xab, 0x6b, 0xeb, 0xe0, 0x59, 0x07, 0x3f, 0x9f, 0xc2, 0x67, 0xbd, 0x76, + 0x9b, 0xde, 0x20, 0xf4, 0x35, 0x78, 0xa2, 0xae, 0x99, 0xdf, 0x91, 0x2f, 0xa6, 0xa4, 0x2b, 0xca, + 0x5d, 0x51, 0xfe, 0xb9, 0x2d, 0x7a, 0xea, 0x7a, 0xa6, 0x2d, 0x42, 0x8f, 0x21, 0xd8, 0x34, 0x63, + 0xf7, 0x42, 0x12, 0x1d, 0x24, 0xcf, 0xa6, 0xfc, 0x46, 0xe4, 0x7d, 0xe8, 0xb8, 0x9d, 0xbe, 0x83, + 0x00, 0xd7, 0x16, 0xa5, 0x29, 0x95, 0x64, 0xf3, 0x9d, 0xb2, 0x47, 0xf0, 0xa3, 0xbf, 0xef, 0x2d, + 0xfd, 0xd5, 0x4f, 0x02, 0x8f, 0xb6, 0xbd, 0x22, 0xba, 0x04, 0xcf, 0x8a, 0x82, 0x91, 0x90, 0x44, + 0x41, 0xda, 0xde, 0xd2, 0x73, 0x78, 0xa8, 0x31, 0xc3, 0xf2, 0x06, 0xf5, 0x85, 0x41, 0x6b, 0x4b, + 0x59, 0x18, 0x36, 0xeb, 0x46, 0xff, 0xd7, 0xf8, 0xe5, 0x60, 0x70, 0xde, 0xf3, 0xf4, 0x14, 0x16, + 0xb5, 0x56, 0xeb, 0x66, 0x74, 0xf4, 0x76, 0x72, 0xbc, 0xdf, 0xd1, 0x83, 0xdd, 0xea, 0x17, 0x81, + 0xc7, 0x5b, 0x3f, 0xda, 0x96, 0x3e, 0x67, 0xf0, 0xc0, 0xa0, 0xcc, 0xff, 0xbf, 0xcd, 0xc2, 0xe1, + 0x77, 0xd4, 0x85, 0x3e, 0x81, 0x39, 0xae, 0xeb, 0x52, 0x23, 0xf3, 0x43, 0x12, 0x79, 0x69, 0xbf, + 0xa2, 0x0c, 0xf6, 0x5a, 0x13, 0x94, 0xee, 0xc7, 0x09, 0xd2, 0x61, 0xf9, 0xf6, 0x08, 0x16, 0x99, + 0xaa, 0x26, 0x69, 0x9f, 0xc8, 0x57, 0xbf, 0xbd, 0x7e, 0x9f, 0xc1, 0x97, 0x24, 0x15, 0x0d, 0x3f, + 0x51, 0x1a, 0x2f, 0xe7, 0xdd, 0x11, 0x7a, 0xf5, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x15, 0xee, + 0x31, 0xc6, 0x03, 0x00, 0x00, } diff --git a/config.proto b/config.proto index 6101c0fa4..31bfb64b7 100644 --- a/config.proto +++ b/config.proto @@ -9,12 +9,6 @@ option java_multiple_files = true; import "v2ray.com/core/common/serial/typed_message.proto"; import "v2ray.com/core/transport/config.proto"; -// Configuration serialization format. -enum ConfigFormat { - Protobuf = 0; - JSON = 1; -} - // Master config of V2Ray. V2Ray takes this config as input and functions accordingly. message Config { // Inbound handler configurations. Must have at least one item. diff --git a/loader.go b/loader.go deleted file mode 100644 index 1dab37dd2..000000000 --- a/loader.go +++ /dev/null @@ -1,46 +0,0 @@ -package core - -import ( - "io" - - "v2ray.com/core/common" - "v2ray.com/core/common/buf" - - "github.com/golang/protobuf/proto" -) - -// ConfigLoader is an utility to load V2Ray config from external source. -type ConfigLoader func(input io.Reader) (*Config, error) - -var configLoaderCache = make(map[ConfigFormat]ConfigLoader) - -// RegisterConfigLoader add a new ConfigLoader. -func RegisterConfigLoader(format ConfigFormat, loader ConfigLoader) error { - configLoaderCache[format] = loader - return nil -} - -// LoadConfig loads config with given format from given source. -func LoadConfig(format ConfigFormat, input io.Reader) (*Config, error) { - loader, found := configLoaderCache[format] - if !found { - return nil, newError(ConfigFormat_name[int32(format)], " is not loadable.").AtWarning() - } - return loader(input) -} - -func loadProtobufConfig(input io.Reader) (*Config, error) { - config := new(Config) - data, err := buf.ReadAllToBytes(input) - if err != nil { - return nil, err - } - if err := proto.Unmarshal(data, config); err != nil { - return nil, err - } - return config, nil -} - -func init() { - common.Must(RegisterConfigLoader(ConfigFormat_Protobuf, loadProtobufConfig)) -} diff --git a/main/config_json.go b/main/config_json.go index 3a8b5819c..dfc2db674 100644 --- a/main/config_json.go +++ b/main/config_json.go @@ -45,7 +45,7 @@ func jsonToProto(input io.Reader) (*core.Config, error) { var config *core.Config loadTask := signal.ExecuteAsync(func() error { - c, err := core.LoadConfig(core.ConfigFormat_Protobuf, stdoutReader) + c, err := core.LoadConfig("protobuf", "", stdoutReader) if err != nil { return err } @@ -65,11 +65,15 @@ func jsonToProto(input io.Reader) (*core.Config, error) { } func init() { - common.Must(core.RegisterConfigLoader(core.ConfigFormat_JSON, func(input io.Reader) (*core.Config, error) { - config, err := jsonToProto(input) - if err != nil { - return nil, newError("failed to execute v2ctl to convert config file.").Base(err).AtWarning() - } - return config, nil + common.Must(core.RegisterConfigLoader(&core.ConfigFormat{ + Name: "JSON", + Extension: []string{"json"}, + Loader: func(input io.Reader) (*core.Config, error) { + config, err := jsonToProto(input) + if err != nil { + return nil, newError("failed to execute v2ctl to convert config file.").Base(err).AtWarning() + } + return config, nil + }, })) } diff --git a/main/main.go b/main/main.go index b3f533714..82ebe9f6a 100644 --- a/main/main.go +++ b/main/main.go @@ -49,14 +49,12 @@ func getConfigFilePath() string { return "" } -func GetConfigFormat() core.ConfigFormat { +func GetConfigFormat() string { switch strings.ToLower(*format) { - case "json": - return core.ConfigFormat_JSON case "pb", "protobuf": - return core.ConfigFormat_Protobuf + return "protobuf" default: - return core.ConfigFormat_JSON + return "json" } } @@ -74,7 +72,7 @@ func startV2Ray() (core.Server, error) { defer file.Close() configInput = file } - config, err := core.LoadConfig(GetConfigFormat(), configInput) + config, err := core.LoadConfig(GetConfigFormat(), configFile, configInput) if err != nil { return nil, newError("failed to read config file: ", configFile).Base(err) } From e9e6b21252fa9805892f14119f503c866febfc69 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 15 Feb 2018 21:11:43 +0100 Subject: [PATCH 017/183] Update version --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index a3696c660..4138bafa0 100644 --- a/core.go +++ b/core.go @@ -18,7 +18,7 @@ import ( ) var ( - version = "3.9" + version = "3.10" build = "Custom" codename = "die Commanderin" intro = "An unified platform for anti-censorship." From 242e65e42486a79be13fceaa50263076e13c1a9a Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 16 Feb 2018 14:03:55 +0100 Subject: [PATCH 018/183] move Println to main --- core.go | 14 +++++++------- main/main.go | 9 ++++++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/core.go b/core.go index 4138bafa0..c0c4c364a 100644 --- a/core.go +++ b/core.go @@ -12,9 +12,7 @@ package core //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg core -path Core import ( - "fmt" - - "v2ray.com/core/common/platform" + "v2ray.com/core/common/serial" ) var ( @@ -30,10 +28,12 @@ func Version() string { return version } -// PrintVersion prints current version into console. -func PrintVersion() { - fmt.Printf("V2Ray %s (%s) %s%s", Version(), codename, build, platform.LineSeparator()) - fmt.Printf("%s%s", intro, platform.LineSeparator()) +// VersionStatement returns a list of strings representing the full version info. +func VersionStatement() []string { + return []string{ + serial.Concat("V2Ray ", Version(), "(", codename, ")", build), + intro, + } } /* diff --git a/main/main.go b/main/main.go index 82ebe9f6a..e3b323da9 100644 --- a/main/main.go +++ b/main/main.go @@ -85,10 +85,17 @@ func startV2Ray() (core.Server, error) { return server, nil } +func printVersion() { + version := core.VersionStatement() + for _, s := range version { + fmt.Println(s) + } +} + func main() { flag.Parse() - core.PrintVersion() + printVersion() if *version { return From 3d8f837c07595a5b66bf110bd971e96fb15d106e Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 16 Feb 2018 14:04:02 +0100 Subject: [PATCH 019/183] comments --- config.go | 1 + config.pb.go | 2 +- config.proto | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config.go b/config.go index fc45ac1ab..345af0c5e 100644 --- a/config.go +++ b/config.go @@ -9,6 +9,7 @@ import ( "v2ray.com/core/common/buf" ) +// ConfigFormat is a configurable format of V2Ray config file. type ConfigFormat struct { Name string Extension []string diff --git a/config.pb.go b/config.pb.go index ac34e2404..f63e1f5ab 100644 --- a/config.pb.go +++ b/config.pb.go @@ -17,7 +17,7 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package -// Master config of V2Ray. V2Ray takes this config as input and functions accordingly. +// Config is the master config of V2Ray. V2Ray takes this config as input and functions accordingly. type Config struct { // Inbound handler configurations. Must have at least one item. Inbound []*InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbound" json:"inbound,omitempty"` diff --git a/config.proto b/config.proto index 31bfb64b7..de8461f45 100644 --- a/config.proto +++ b/config.proto @@ -9,7 +9,7 @@ option java_multiple_files = true; import "v2ray.com/core/common/serial/typed_message.proto"; import "v2ray.com/core/transport/config.proto"; -// Master config of V2Ray. V2Ray takes this config as input and functions accordingly. +// Config is the master config of V2Ray. V2Ray takes this config as input and functions accordingly. message Config { // Inbound handler configurations. Must have at least one item. repeated InboundHandlerConfig inbound = 1; From 07ca87990d9bb59b5f3df83585a1b1e244766408 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 16 Feb 2018 15:17:09 +0100 Subject: [PATCH 020/183] update coverall script --- testing/coverage/coverall | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/coverage/coverall b/testing/coverage/coverall index e6e85800e..9db519d06 100755 --- a/testing/coverage/coverall +++ b/testing/coverage/coverall @@ -45,7 +45,8 @@ cat ${COVERAGE_FILE} | sort -t: -k1 | grep -vw "testing" | grep -v ".pb.go" | gr echo "mode: set" | cat - ${COV_SORTED} > ${COVERAGE_FILE} if [ "$FAIL" -eq 0 ]; then - bash <(curl -s https://codecov.io/bash) -f ${COVERAGE_FILE} || echo "Codecov did not collect coverage reports." + echo "Uploading coverage datea to codecov." + bash <(curl -s https://codecov.io/bash) -f ${COVERAGE_FILE} -v || echo "Codecov did not collect coverage reports." fi exit $FAIL From 95be224caf24649592f7b8578c3c236f2ef2ac3d Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 16 Feb 2018 15:29:22 +0100 Subject: [PATCH 021/183] fix coverage test --- testing/scenarios/common_coverage.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/scenarios/common_coverage.go b/testing/scenarios/common_coverage.go index c29aceeae..d0c794410 100644 --- a/testing/scenarios/common_coverage.go +++ b/testing/scenarios/common_coverage.go @@ -26,7 +26,8 @@ func RunV2RayProtobuf(config []byte) *exec.Cmd { covDir := filepath.Join(os.Getenv("GOPATH"), "out", "v2ray", "cov") os.MkdirAll(covDir, os.ModeDir) - profile := uuid.New().String() + ".out" + randomID := uuid.New() + profile := randomID.String() + ".out" proc := exec.Command(testBinaryPath, "-config=stdin:", "-format=pb", "-test.run", "TestRunMainForCoverage", "-test.coverprofile", profile, "-test.outputdir", covDir) proc.Stdin = bytes.NewBuffer(config) proc.Stderr = os.Stderr From 2789798390ae2bea27f6c3b8e589d7a87d8acd71 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 16 Feb 2018 15:54:53 +0100 Subject: [PATCH 022/183] remove clock feature --- clock.go | 59 -------------------------------------------------------- v2ray.go | 8 -------- 2 files changed, 67 deletions(-) delete mode 100644 clock.go diff --git a/clock.go b/clock.go deleted file mode 100644 index 54bcb905b..000000000 --- a/clock.go +++ /dev/null @@ -1,59 +0,0 @@ -package core - -import ( - "sync" - "time" -) - -// Clock is a V2Ray feature that returns current time. -type Clock interface { - Feature - - // Now returns current time. - Now() time.Time -} - -type syncClock struct { - sync.RWMutex - Clock -} - -func (c *syncClock) Now() time.Time { - c.RLock() - defer c.RUnlock() - - if c.Clock == nil { - return time.Now() - } - - return c.Clock.Now() -} - -func (c *syncClock) Start() error { - c.RLock() - defer c.RUnlock() - - if c.Clock == nil { - return nil - } - - return c.Clock.Start() -} - -func (c *syncClock) Close() error { - c.RLock() - defer c.RUnlock() - - if c.Clock == nil { - return nil - } - - return c.Clock.Close() -} - -func (c *syncClock) Set(clock Clock) { - c.Lock() - defer c.Unlock() - - c.Clock = clock -} diff --git a/v2ray.go b/v2ray.go index df4b04207..5538da7ee 100644 --- a/v2ray.go +++ b/v2ray.go @@ -28,7 +28,6 @@ type Instance struct { router syncRouter ihm syncInboundHandlerManager ohm syncOutboundHandlerManager - clock syncClock cmd syncCommander access sync.Mutex @@ -148,8 +147,6 @@ func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error s.ihm.Set(instance.(InboundHandlerManager)) case OutboundHandlerManager, *OutboundHandlerManager: s.ohm.Set(instance.(OutboundHandlerManager)) - case Clock, *Clock: - s.clock.Set(instance.(Clock)) case Commander, *Commander: s.cmd.Set(instance.(Commander)) } @@ -206,11 +203,6 @@ func (s *Instance) OutboundHandlerManager() OutboundHandlerManager { return &(s.ohm) } -// Clock returns the Clock used by this Instance. The returned Clock is always functional. -func (s *Instance) Clock() Clock { - return &(s.clock) -} - // Commander returns the Commander used by this Instance. The returned Commander is always functional. func (s *Instance) Commander() Commander { return &(s.cmd) From 5bd3cee37104d91b6bb76488b09a6a70dc13d430 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 17 Feb 2018 21:22:28 +0100 Subject: [PATCH 023/183] gofmt --- app/log/command/errors.generated.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/log/command/errors.generated.go b/app/log/command/errors.generated.go index 644816857..ac6c3bb2b 100644 --- a/app/log/command/errors.generated.go +++ b/app/log/command/errors.generated.go @@ -2,4 +2,6 @@ package command import "v2ray.com/core/common/errors" -func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Log", "Command") } +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).Path("App", "Log", "Command") +} From ab9ca3b842fd11c136c8cb990145caba54d745e7 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 17 Feb 2018 21:22:51 +0100 Subject: [PATCH 024/183] move json config into a separate package --- main/distro/all/all.go | 3 +++ main/{ => json}/config_json.go | 4 +++- main/{ => json}/config_json_other.go | 2 +- main/{ => json}/config_json_windows.go | 2 +- main/json/errors.generated.go | 5 +++++ 5 files changed, 13 insertions(+), 3 deletions(-) rename main/{ => json}/config_json.go (93%) rename main/{ => json}/config_json_other.go (88%) rename main/{ => json}/config_json_windows.go (91%) create mode 100644 main/json/errors.generated.go diff --git a/main/distro/all/all.go b/main/distro/all/all.go index 8f5524beb..321c60150 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -42,4 +42,7 @@ import ( _ "v2ray.com/core/transport/internet/headers/srtp" _ "v2ray.com/core/transport/internet/headers/utp" _ "v2ray.com/core/transport/internet/headers/wechat" + + // JSON config format + _ "v2ray.com/core/main/json" ) diff --git a/main/config_json.go b/main/json/config_json.go similarity index 93% rename from main/config_json.go rename to main/json/config_json.go index dfc2db674..a98e1ca7c 100644 --- a/main/config_json.go +++ b/main/json/config_json.go @@ -1,4 +1,6 @@ -package main +package json + +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg json -path Main,Json import ( "context" diff --git a/main/config_json_other.go b/main/json/config_json_other.go similarity index 88% rename from main/config_json_other.go rename to main/json/config_json_other.go index b1502710e..2c52fd109 100644 --- a/main/config_json_other.go +++ b/main/json/config_json_other.go @@ -1,6 +1,6 @@ // +build !windows -package main +package json import "syscall" diff --git a/main/config_json_windows.go b/main/json/config_json_windows.go similarity index 91% rename from main/config_json_windows.go rename to main/json/config_json_windows.go index 01c2b53ba..78f63c795 100644 --- a/main/config_json_windows.go +++ b/main/json/config_json_windows.go @@ -1,6 +1,6 @@ // +build windows -package main +package json import "syscall" diff --git a/main/json/errors.generated.go b/main/json/errors.generated.go new file mode 100644 index 000000000..faf694b90 --- /dev/null +++ b/main/json/errors.generated.go @@ -0,0 +1,5 @@ +package json + +import "v2ray.com/core/common/errors" + +func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("Main", "Json") } From 512fa40a4a651ca0b628193f6234fe1ec33468e4 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 17 Feb 2018 21:54:59 +0100 Subject: [PATCH 025/183] try Go 1.10 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e5f3318f8..6cbc591a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: required language: go go: -- 1.9.4 +- 1.10 go_import_path: v2ray.com/core git: depth: 5 From baeef07abb190f48b6a160c120dccec59c647973 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 17 Feb 2018 21:59:52 +0100 Subject: [PATCH 026/183] fix yaml syntax --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6cbc591a2..7096eff13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: required language: go go: -- 1.10 +- "1.10" go_import_path: v2ray.com/core git: depth: 5 From 39cfc982b5ef64d223a24de45105cf5ed973f0f8 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 17 Feb 2018 23:37:09 +0100 Subject: [PATCH 027/183] test case for policy --- app/policy/config.go | 18 ------------------ app/policy/manager.go | 14 +++++--------- app/policy/manager_test.go | 37 +++++++++++++++++++++++++++++++++++++ policy.go | 2 +- 4 files changed, 43 insertions(+), 28 deletions(-) create mode 100644 app/policy/manager_test.go diff --git a/app/policy/config.go b/app/policy/config.go index 4d2f9452d..589a2c48e 100644 --- a/app/policy/config.go +++ b/app/policy/config.go @@ -14,24 +14,6 @@ func (s *Second) Duration() time.Duration { return time.Second * time.Duration(s.Value) } -// OverrideWith overrides current Policy with another one. -func (p *Policy) OverrideWith(another *Policy) { - if another.Timeout != nil { - if another.Timeout.Handshake != nil { - p.Timeout.Handshake = another.Timeout.Handshake - } - if another.Timeout.ConnectionIdle != nil { - p.Timeout.ConnectionIdle = another.Timeout.ConnectionIdle - } - if another.Timeout.UplinkOnly != nil { - p.Timeout.UplinkOnly = another.Timeout.UplinkOnly - } - if another.Timeout.DownlinkOnly != nil { - p.Timeout.DownlinkOnly = another.Timeout.DownlinkOnly - } - } -} - func (p *Policy) ToCorePolicy() core.Policy { var cp core.Policy if p.Timeout != nil { diff --git a/app/policy/manager.go b/app/policy/manager.go index 14f7b7b9b..14a1c4c79 100644 --- a/app/policy/manager.go +++ b/app/policy/manager.go @@ -19,19 +19,15 @@ func New(ctx context.Context, config *Config) (*Instance, error) { } if len(config.Level) > 0 { for lv, p := range config.Level { - dp := core.DefaultPolicy() - dp.OverrideWith(p.ToCorePolicy()) - m.levels[lv] = dp + m.levels[lv] = p.ToCorePolicy().OverrideWith(core.DefaultPolicy()) } } v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context.") - } - - if err := v.RegisterFeature((*core.PolicyManager)(nil), m); err != nil { - return nil, newError("unable to register PolicyManager in core").Base(err).AtError() + if v != nil { + if err := v.RegisterFeature((*core.PolicyManager)(nil), m); err != nil { + return nil, newError("unable to register PolicyManager in core").Base(err).AtError() + } } return m, nil diff --git a/app/policy/manager_test.go b/app/policy/manager_test.go new file mode 100644 index 000000000..d56afb86f --- /dev/null +++ b/app/policy/manager_test.go @@ -0,0 +1,37 @@ +package policy_test + +import ( + "context" + "testing" + "time" + + "v2ray.com/core" + . "v2ray.com/core/app/policy" + . "v2ray.com/ext/assert" +) + +func TestPolicy(t *testing.T) { + assert := With(t) + + manager, err := New(context.Background(), &Config{ + Level: map[uint32]*Policy{ + 0: &Policy{ + Timeout: &Policy_Timeout{ + Handshake: &Second{ + Value: 2, + }, + }, + }, + }, + }) + assert(err, IsNil) + + pDefault := core.DefaultPolicy() + + p0 := manager.ForLevel(0) + assert(p0.Timeouts.Handshake, Equals, 2*time.Second) + assert(p0.Timeouts.ConnectionIdle, Equals, pDefault.Timeouts.ConnectionIdle) + + p1 := manager.ForLevel(1) + assert(p1.Timeouts.Handshake, Equals, pDefault.Timeouts.Handshake) +} diff --git a/policy.go b/policy.go index a07173206..a333a84b0 100644 --- a/policy.go +++ b/policy.go @@ -43,7 +43,7 @@ type Policy struct { // OverrideWith overrides the current Policy with another one. All values with default value will be overridden. func (p Policy) OverrideWith(another Policy) Policy { - p.Timeouts.OverrideWith(another.Timeouts) + p.Timeouts = p.Timeouts.OverrideWith(another.Timeouts) return p } From e49d49bb9da58747e7fa1bd3bfa683949aa4e4f7 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 19 Feb 2018 16:52:51 +0100 Subject: [PATCH 028/183] test case for adding and removing user by command --- testing/scenarios/command_test.go | 216 ++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go index 27165a368..f27b49238 100644 --- a/testing/scenarios/command_test.go +++ b/testing/scenarios/command_test.go @@ -3,8 +3,11 @@ package scenarios import ( "context" "fmt" + "io" "testing" + "v2ray.com/core/app/policy" + "google.golang.org/grpc" "v2ray.com/core" "v2ray.com/core/app/commander" @@ -12,9 +15,14 @@ import ( "v2ray.com/core/app/proxyman/command" "v2ray.com/core/app/router" "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" "v2ray.com/core/common/serial" + "v2ray.com/core/common/uuid" "v2ray.com/core/proxy/dokodemo" "v2ray.com/core/proxy/freedom" + "v2ray.com/core/proxy/vmess" + "v2ray.com/core/proxy/vmess/inbound" + "v2ray.com/core/proxy/vmess/outbound" "v2ray.com/core/testing/servers/tcp" . "v2ray.com/ext/assert" ) @@ -128,3 +136,211 @@ func TestCommanderRemoveHandler(t *testing.T) { CloseAllServers(servers) } + +func TestCommanderAddRemoveUser(t *testing.T) { + assert := With(t) + + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + assert(err, IsNil) + defer tcpServer.Close() + + u1 := protocol.NewID(uuid.New()) + u2 := protocol.NewID(uuid.New()) + + cmdPort := pickPort() + serverPort := pickPort() + serverConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&commander.Config{ + Tag: "api", + Service: []*serial.TypedMessage{ + serial.ToTypedMessage(&command.Config{}), + }, + }), + serial.ToTypedMessage(&router.Config{ + Rule: []*router.RoutingRule{ + { + InboundTag: []string{"api"}, + Tag: "api", + }, + }, + }), + serial.ToTypedMessage(&policy.Config{ + Level: map[uint32]*policy.Policy{ + 0: &policy.Policy{ + Timeout: &policy.Policy_Timeout{ + UplinkOnly: &policy.Second{Value: 0}, + DownlinkOnly: &policy.Second{Value: 0}, + }, + }, + }, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + Tag: "v", + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(serverPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: u1.String(), + AlterId: 64, + }), + }, + }, + }), + }, + { + Tag: "api", + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(cmdPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + clientPort := pickPort() + clientConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&policy.Config{ + Level: map[uint32]*policy.Policy{ + 0: &policy.Policy{ + Timeout: &policy.Policy_Timeout{ + UplinkOnly: &policy.Second{Value: 0}, + DownlinkOnly: &policy.Second{Value: 0}, + }, + }, + }, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + Tag: "d", + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(clientPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: u2.String(), + AlterId: 64, + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }), + }, + }, + }, + }, + }), + }, + }, + } + + servers, err := InitializeServerConfigs(serverConfig, clientConfig) + assert(err, IsNil) + + { + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert(err, IsNil) + + payload := "commander request." + nBytes, err := conn.Write([]byte(payload)) + assert(err, IsNil) + assert(nBytes, Equals, len(payload)) + + response := make([]byte, 1024) + nBytes, err = conn.Read(response) + assert(nBytes, Equals, 0) + assert(err, Equals, io.EOF) + assert(conn.Close(), IsNil) + } + + cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure()) + assert(err, IsNil) + + hsClient := command.NewHandlerServiceClient(cmdConn) + resp, err := hsClient.AlterInbound(context.Background(), &command.AlterInboundRequest{ + Tag: "v", + Operation: serial.ToTypedMessage( + &command.AddUserOperation{ + User: &protocol.User{ + Email: "test@v2ray.com", + Account: serial.ToTypedMessage(&vmess.Account{ + Id: u2.String(), + AlterId: 64, + }), + }, + }), + }) + assert(err, IsNil) + assert(resp, IsNotNil) + + { + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert(err, IsNil) + + payload := "commander request." + nBytes, err := conn.Write([]byte(payload)) + assert(err, IsNil) + assert(nBytes, Equals, len(payload)) + + response := make([]byte, 1024) + nBytes, err = conn.Read(response) + assert(err, IsNil) + assert(response[:nBytes], Equals, xor([]byte(payload))) + assert(conn.Close(), IsNil) + } + + resp, err = hsClient.AlterInbound(context.Background(), &command.AlterInboundRequest{ + Tag: "v", + Operation: serial.ToTypedMessage(&command.RemoveUserOperation{Email: "test@v2ray.com"}), + }) + assert(resp, IsNotNil) + assert(err, IsNil) + + CloseAllServers(servers) +} From e7858e78d5df2ee77ca87a7b7251671580f986db Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 19 Feb 2018 16:53:07 +0100 Subject: [PATCH 029/183] properly set timeout in dokodemo door --- proxy/dokodemo/dokodemo.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 80e952390..8f834b9a2 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -82,6 +82,7 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in requestDone := signal.ExecuteAsync(func() error { defer inboundRay.InboundInput().Close() + defer timer.SetTimeout(d.policy().Timeouts.DownlinkOnly) chunkReader := buf.NewReader(conn) @@ -89,12 +90,12 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in return newError("failed to transport request").Base(err) } - timer.SetTimeout(d.policy().Timeouts.DownlinkOnly) - return nil }) responseDone := signal.ExecuteAsync(func() error { + defer timer.SetTimeout(d.policy().Timeouts.UplinkOnly) + var writer buf.Writer if network == net.Network_TCP { writer = buf.NewWriter(conn) @@ -116,8 +117,6 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in return newError("failed to transport response").Base(err) } - timer.SetTimeout(d.policy().Timeouts.UplinkOnly) - return nil }) From 226d65c6c7f85bd64747f1fdaaa14b25d70c64d4 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 19 Feb 2018 17:50:21 +0100 Subject: [PATCH 030/183] fix error handling in vmess outbound --- proxy/vmess/outbound/outbound.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index c2070aed3..3225cb7ac 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -109,6 +109,8 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) requestDone := signal.ExecuteAsync(func() error { + defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) + writer := buf.NewBufferedWriter(buf.NewWriter(conn)) if err := session.EncodeRequestHeader(request, writer); err != nil { return newError("failed to encode request").Base(err).AtWarning() @@ -139,23 +141,23 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial return err } } - timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) + return nil }) responseDone := signal.ExecuteAsync(func() error { - defer output.Close() defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) reader := buf.NewBufferedReader(buf.NewReader(conn)) header, err := session.DecodeResponseHeader(reader) if err != nil { - return err + return newError("failed to read header").Base(err) } v.handleCommand(rec.Destination(), header.Command) reader.SetBuffered(false) bodyReader := session.DecodeResponseBody(request, reader) + return buf.Copy(bodyReader, output, buf.UpdateActivity(timer)) }) From f1231822f721286c7ece0c71244d9813e59e0807 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 19 Feb 2018 17:50:36 +0100 Subject: [PATCH 031/183] fix error handling in ray --- transport/ray/direct.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/transport/ray/direct.go b/transport/ray/direct.go index 53af0f4d5..9be2aff64 100644 --- a/transport/ray/direct.go +++ b/transport/ray/direct.go @@ -85,14 +85,14 @@ func (s *Stream) getData() (buf.MultiBuffer, error) { return mb, nil } - if s.close { - return nil, io.EOF - } - if s.err { return nil, io.ErrClosedPipe } + if s.close { + return nil, io.EOF + } + return nil, nil } @@ -121,7 +121,7 @@ func (s *Stream) ReadMultiBuffer() (buf.MultiBuffer, error) { select { case <-s.ctx.Done(): - return nil, io.EOF + return nil, s.ctx.Err() case <-s.writeSignal.Wait(): } } @@ -142,7 +142,7 @@ func (s *Stream) ReadTimeout(timeout time.Duration) (buf.MultiBuffer, error) { select { case <-s.ctx.Done(): - return nil, io.EOF + return nil, s.ctx.Err() case <-time.After(timeout): return nil, buf.ErrReadTimeout case <-s.writeSignal.Wait(): @@ -167,7 +167,7 @@ func (s *Stream) waitForStreamSize() error { for s.Size() >= streamSizeLimit { select { case <-s.ctx.Done(): - return io.ErrClosedPipe + return s.ctx.Err() case <-s.readSignal.Wait(): if s.err || s.close { return io.ErrClosedPipe @@ -227,7 +227,9 @@ func (s *Stream) CloseError() { s.data = nil s.size = 0 } + s.access.Unlock() + s.readSignal.Signal() s.writeSignal.Signal() - s.access.Unlock() + } From b3e46f5d076559b2c627d45ded3aab0bf1057a9a Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 19 Feb 2018 17:50:53 +0100 Subject: [PATCH 032/183] fix error handling in buf.Copy --- common/buf/copy.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/buf/copy.go b/common/buf/copy.go index 7d19efdda..7d487c174 100644 --- a/common/buf/copy.go +++ b/common/buf/copy.go @@ -92,7 +92,9 @@ func copyInternal(reader Reader, writer Writer, handler *copyHandler) error { buffer.Release() return werr } - } else if err != nil { + } + + if err != nil { return err } } From 446059f8dcd3ab0f394a9381e0e2c94b2234f77e Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 19 Feb 2018 17:51:10 +0100 Subject: [PATCH 033/183] fix error handling in outbound handler --- app/proxyman/outbound/handler.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index d3c237e16..63a4ec55b 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -2,13 +2,11 @@ package outbound import ( "context" - "io" "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/app/proxyman/mux" "v2ray.com/core/common" - "v2ray.com/core/common/errors" "v2ray.com/core/common/net" "v2ray.com/core/proxy" "v2ray.com/core/transport/internet" @@ -89,7 +87,7 @@ func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) { } else { err := h.proxy.Process(ctx, outboundRay, h) // Ensure outbound ray is properly closed. - if err != nil && errors.Cause(err) != io.EOF { + if err != nil { newError("failed to process outbound traffic").Base(err).WriteToLog() outboundRay.OutboundOutput().CloseError() } else { From 1f8fcb558db67f035b29a9d452b7e6bdcae8d480 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 19 Feb 2018 21:38:04 +0100 Subject: [PATCH 034/183] fix error handling in freedom, shadowsocks and socks --- proxy/freedom/freedom.go | 8 +++++--- proxy/shadowsocks/client.go | 2 -- proxy/socks/client.go | 30 +++++++++++++++++++++++++----- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index ef87340f4..80292fb60 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -114,6 +114,8 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial timer := signal.CancelAfterInactivity(ctx, cancel, h.policy().Timeouts.ConnectionIdle) requestDone := signal.ExecuteAsync(func() error { + defer timer.SetTimeout(h.policy().Timeouts.DownlinkOnly) + var writer buf.Writer if destination.Network == net.Network_TCP { writer = buf.NewWriter(conn) @@ -123,18 +125,18 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial if err := buf.Copy(input, writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to process request").Base(err) } - timer.SetTimeout(h.policy().Timeouts.DownlinkOnly) + return nil }) responseDone := signal.ExecuteAsync(func() error { - defer output.Close() + defer timer.SetTimeout(h.policy().Timeouts.UplinkOnly) v2reader := buf.NewReader(conn) if err := buf.Copy(v2reader, output, buf.UpdateActivity(timer)); err != nil { return newError("failed to process response").Base(err) } - timer.SetTimeout(h.policy().Timeouts.UplinkOnly) + return nil }) diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index d622ba065..58e659db3 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -115,7 +115,6 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale }) responseDone := signal.ExecuteAsync(func() error { - defer outboundRay.OutboundOutput().Close() defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) responseReader, err := ReadTCPResponse(user, conn) @@ -150,7 +149,6 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale }) responseDone := signal.ExecuteAsync(func() error { - defer outboundRay.OutboundOutput().Close() defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) reader := &UDPReader{ diff --git a/proxy/socks/client.go b/proxy/socks/client.go index ea97b8362..78191e1e6 100644 --- a/proxy/socks/client.go +++ b/proxy/socks/client.go @@ -4,6 +4,7 @@ import ( "context" "time" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" @@ -17,7 +18,8 @@ import ( // Client is a Socks5 client. type Client struct { - serverPicker protocol.ServerPicker + serverPicker protocol.ServerPicker + policyManager core.PolicyManager } // NewClient create a new Socks5 client based on the given config. @@ -30,8 +32,14 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { return nil, newError("0 target server") } + v := core.FromContext(ctx) + if v == nil { + return nil, newError("V is not in context") + } + return &Client{ - serverPicker: protocol.NewRoundRobinServerPicker(serverList), + serverPicker: protocol.NewRoundRobinServerPicker(serverList), + policyManager: v.PolicyManager(), }, nil } @@ -63,6 +71,8 @@ func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy. defer conn.Close() + p := c.policyManager.ForLevel(0) + request := &protocol.RequestHeader{ Version: socks5Version, Command: protocol.RequestCommandTCP, @@ -76,24 +86,33 @@ func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy. user := server.PickUser() if user != nil { request.User = user + p = c.policyManager.ForLevel(user.Level) } + if err := conn.SetDeadline(time.Now().Add(p.Timeouts.Handshake)); err != nil { + newError("failed to set deadline for handshake").Base(err).WriteToLog() + } udpRequest, err := ClientHandshake(request, conn, conn) if err != nil { return newError("failed to establish connection to server").AtWarning().Base(err) } + if err := conn.SetDeadline(time.Time{}); err != nil { + newError("failed to clear deadline after handshake").Base(err).WriteToLog() + } + ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, time.Minute*5) + timer := signal.CancelAfterInactivity(ctx, cancel, p.Timeouts.ConnectionIdle) var requestFunc func() error var responseFunc func() error if request.Command == protocol.RequestCommandTCP { requestFunc = func() error { + defer timer.SetTimeout(p.Timeouts.DownlinkOnly) return buf.Copy(ray.OutboundInput(), buf.NewWriter(conn), buf.UpdateActivity(timer)) } responseFunc = func() error { - defer ray.OutboundOutput().Close() + defer timer.SetTimeout(p.Timeouts.UplinkOnly) return buf.Copy(buf.NewReader(conn), ray.OutboundOutput(), buf.UpdateActivity(timer)) } } else if request.Command == protocol.RequestCommandUDP { @@ -103,10 +122,11 @@ func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy. } defer udpConn.Close() requestFunc = func() error { + defer timer.SetTimeout(p.Timeouts.DownlinkOnly) return buf.Copy(ray.OutboundInput(), buf.NewSequentialWriter(NewUDPWriter(request, udpConn)), buf.UpdateActivity(timer)) } responseFunc = func() error { - defer ray.OutboundOutput().Close() + defer timer.SetTimeout(p.Timeouts.UplinkOnly) reader := &UDPReader{reader: udpConn} return buf.Copy(reader, ray.OutboundOutput(), buf.UpdateActivity(timer)) } From b3fd320be7f6808284270965fe739f2b12aa358c Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 19 Feb 2018 23:01:00 +0100 Subject: [PATCH 035/183] more test cases --- common/log/logger_test.go | 40 ++++++++++++++++++++++++++++++++++++ common/serial/string_test.go | 28 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 common/log/logger_test.go create mode 100644 common/serial/string_test.go diff --git a/common/log/logger_test.go b/common/log/logger_test.go new file mode 100644 index 000000000..03018e2c0 --- /dev/null +++ b/common/log/logger_test.go @@ -0,0 +1,40 @@ +package log_test + +import ( + "io/ioutil" + "os" + "testing" + "time" + + "v2ray.com/core/common" + "v2ray.com/core/common/buf" + . "v2ray.com/core/common/log" + . "v2ray.com/ext/assert" +) + +func TestFileLogger(t *testing.T) { + assert := With(t) + + f, err := ioutil.TempFile("", "vtest") + assert(err, IsNil) + path := f.Name() + common.Must(f.Close()) + + creator, err := CreateFileLogWriter(path) + assert(err, IsNil) + + handler := NewLogger(creator) + handler.Handle(&GeneralMessage{Content: "Test Log"}) + time.Sleep(2 * time.Second) + + common.Must(common.Close(handler)) + + f, err = os.Open(path) + assert(err, IsNil) + + b, err := buf.ReadAllToBytes(f) + assert(err, IsNil) + assert(string(b), HasSubstring, "Test Log") + + common.Must(f.Close()) +} diff --git a/common/serial/string_test.go b/common/serial/string_test.go new file mode 100644 index 000000000..47d044683 --- /dev/null +++ b/common/serial/string_test.go @@ -0,0 +1,28 @@ +package serial_test + +import ( + "errors" + "testing" + + . "v2ray.com/core/common/serial" + . "v2ray.com/ext/assert" +) + +func TestToString(t *testing.T) { + assert := With(t) + + s := "a" + data := []struct { + Value interface{} + String string + }{ + {Value: s, String: s}, + {Value: &s, String: s}, + {Value: errors.New("t"), String: "t"}, + {Value: []byte{'b', 'c'}, String: "[62,63]"}, + } + + for _, c := range data { + assert(ToString(c.Value), Equals, c.String) + } +} From c25a76a0cffabaf239cfc2bc9f26cac9ebd88e4b Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 20 Feb 2018 13:53:07 +0100 Subject: [PATCH 036/183] integration test case for policy --- app/policy/config.go | 34 +++++++ app/policy/manager.go | 10 +- policy.go | 27 +---- testing/scenarios/policy_test.go | 168 +++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+), 29 deletions(-) create mode 100644 testing/scenarios/policy_test.go diff --git a/app/policy/config.go b/app/policy/config.go index 589a2c48e..4fc14f8c5 100644 --- a/app/policy/config.go +++ b/app/policy/config.go @@ -14,6 +14,40 @@ func (s *Second) Duration() time.Duration { return time.Second * time.Duration(s.Value) } +func defaultPolicy() *Policy { + p := core.DefaultPolicy() + + return &Policy{ + Timeout: &Policy_Timeout{ + Handshake: &Second{Value: uint32(p.Timeouts.Handshake / time.Second)}, + ConnectionIdle: &Second{Value: uint32(p.Timeouts.ConnectionIdle / time.Second)}, + UplinkOnly: &Second{Value: uint32(p.Timeouts.UplinkOnly / time.Second)}, + DownlinkOnly: &Second{Value: uint32(p.Timeouts.DownlinkOnly / time.Second)}, + }, + } +} + +func (p *Policy_Timeout) overrideWith(another *Policy_Timeout) { + if another.Handshake != nil { + p.Handshake = &Second{Value: another.Handshake.Value} + } + if another.ConnectionIdle != nil { + p.ConnectionIdle = &Second{Value: another.ConnectionIdle.Value} + } + if another.UplinkOnly != nil { + p.UplinkOnly = &Second{Value: another.UplinkOnly.Value} + } + if another.DownlinkOnly != nil { + p.DownlinkOnly = &Second{Value: another.DownlinkOnly.Value} + } +} + +func (p *Policy) overrideWith(another *Policy) { + if another.Timeout != nil { + p.Timeout.overrideWith(another.Timeout) + } +} + func (p *Policy) ToCorePolicy() core.Policy { var cp core.Policy if p.Timeout != nil { diff --git a/app/policy/manager.go b/app/policy/manager.go index 14a1c4c79..20e785154 100644 --- a/app/policy/manager.go +++ b/app/policy/manager.go @@ -9,17 +9,19 @@ import ( // Instance is an instance of Policy manager. type Instance struct { - levels map[uint32]core.Policy + levels map[uint32]*Policy } // New creates new Policy manager instance. func New(ctx context.Context, config *Config) (*Instance, error) { m := &Instance{ - levels: make(map[uint32]core.Policy), + levels: make(map[uint32]*Policy), } if len(config.Level) > 0 { for lv, p := range config.Level { - m.levels[lv] = p.ToCorePolicy().OverrideWith(core.DefaultPolicy()) + pp := defaultPolicy() + pp.overrideWith(p) + m.levels[lv] = pp } } @@ -36,7 +38,7 @@ func New(ctx context.Context, config *Config) (*Instance, error) { // ForLevel implements core.PolicyManager. func (m *Instance) ForLevel(level uint32) core.Policy { if p, ok := m.levels[level]; ok { - return p + return p.ToCorePolicy() } return core.DefaultPolicy() } diff --git a/policy.go b/policy.go index a333a84b0..1ac9ac1fb 100644 --- a/policy.go +++ b/policy.go @@ -13,40 +13,17 @@ type TimeoutPolicy struct { Handshake time.Duration // Timeout for connection being idle, i.e., there is no egress or ingress traffic in this connection. ConnectionIdle time.Duration - // Timeout for an uplink only connection, i.e., the downlink of the connection has ben closed. + // Timeout for an uplink only connection, i.e., the downlink of the connection has been closed. UplinkOnly time.Duration - // Timeout for an downlink only connection, i.e., the uplink of the connection has ben closed. + // Timeout for an downlink only connection, i.e., the uplink of the connection has been closed. DownlinkOnly time.Duration } -// OverrideWith overrides the current TimeoutPolicy with another one. All timeouts with zero value will be overridden with the new value. -func (p TimeoutPolicy) OverrideWith(another TimeoutPolicy) TimeoutPolicy { - if p.Handshake == 0 { - p.Handshake = another.Handshake - } - if p.ConnectionIdle == 0 { - p.ConnectionIdle = another.ConnectionIdle - } - if p.UplinkOnly == 0 { - p.UplinkOnly = another.UplinkOnly - } - if p.DownlinkOnly == 0 { - p.DownlinkOnly = another.DownlinkOnly - } - return p -} - // Policy is session based settings for controlling V2Ray requests. It contains various settings (or limits) that may differ for different users in the context. type Policy struct { Timeouts TimeoutPolicy // Timeout settings } -// OverrideWith overrides the current Policy with another one. All values with default value will be overridden. -func (p Policy) OverrideWith(another Policy) Policy { - p.Timeouts = p.Timeouts.OverrideWith(another.Timeouts) - return p -} - // PolicyManager is a feature that provides Policy for the given user by its id or level. type PolicyManager interface { Feature diff --git a/testing/scenarios/policy_test.go b/testing/scenarios/policy_test.go new file mode 100644 index 000000000..b3187cc64 --- /dev/null +++ b/testing/scenarios/policy_test.go @@ -0,0 +1,168 @@ +package scenarios + +import ( + "io" + "testing" + "time" + + "v2ray.com/core" + "v2ray.com/core/app/policy" + "v2ray.com/core/app/proxyman" + "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/serial" + "v2ray.com/core/common/uuid" + "v2ray.com/core/proxy/dokodemo" + "v2ray.com/core/proxy/freedom" + "v2ray.com/core/proxy/vmess" + "v2ray.com/core/proxy/vmess/inbound" + "v2ray.com/core/proxy/vmess/outbound" + . "v2ray.com/ext/assert" +) + +func startQuickClosingTCPServer() (net.Listener, error) { + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + return nil, err + } + go func() { + for { + conn, err := listener.Accept() + if err != nil { + break + } + b := make([]byte, 1024) + conn.Read(b) + conn.Close() + } + }() + return listener, nil +} + +func TestVMessClosing(t *testing.T) { + assert := With(t) + + tcpServer, err := startQuickClosingTCPServer() + assert(err, IsNil) + defer tcpServer.Close() + + dest := net.DestinationFromAddr(tcpServer.Addr()) + + userID := protocol.NewID(uuid.New()) + serverPort := pickPort() + serverConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&policy.Config{ + Level: map[uint32]*policy.Policy{ + 0: &policy.Policy{ + Timeout: &policy.Policy_Timeout{ + UplinkOnly: &policy.Second{Value: 0}, + DownlinkOnly: &policy.Second{Value: 0}, + }, + }, + }, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(serverPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + }), + }, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + clientPort := pickPort() + clientConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&policy.Config{ + Level: map[uint32]*policy.Policy{ + 0: &policy.Policy{ + Timeout: &policy.Policy_Timeout{ + UplinkOnly: &policy.Second{Value: 0}, + DownlinkOnly: &policy.Second{Value: 0}, + }, + }, + }, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(clientPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }), + }, + }, + }, + }, + }), + }, + }, + } + + servers, err := InitializeServerConfigs(serverConfig, clientConfig) + assert(err, IsNil) + + defer CloseAllServers(servers) + + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert(err, IsNil) + + conn.SetDeadline(time.Now().Add(time.Second * 2)) + + nBytes, err := conn.Write([]byte("test payload")) + assert(nBytes, GreaterThan, 0) + assert(err, IsNil) + + resp := make([]byte, 1024) + nBytes, err = conn.Read(resp) + assert(err, Equals, io.EOF) + assert(nBytes, Equals, 0) + + CloseAllServers(servers) +} From f0849543ed276e876b49d4b89b9c7688218d34c6 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 20 Feb 2018 21:19:09 +0100 Subject: [PATCH 037/183] remove commander from core feature --- app/commander/commander.go | 6 +++++- commander.go | 44 -------------------------------------- v2ray.go | 28 ++++++++++++------------ 3 files changed, 19 insertions(+), 59 deletions(-) delete mode 100644 commander.go diff --git a/app/commander/commander.go b/app/commander/commander.go index 67a0b89b7..e1e408a97 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -31,12 +31,16 @@ func NewCommander(ctx context.Context, config *Config) (*Commander, error) { ohm: v.OutboundHandlerManager(), v: v, } - if err := v.RegisterFeature((*core.Commander)(nil), c); err != nil { + if err := v.RegisterFeature((*Commander)(nil), c); err != nil { return nil, err } return c, nil } +func (c *Commander) Type() interface{} { + return (*Commander)(nil) +} + func (c *Commander) Start() error { c.Lock() c.server = grpc.NewServer() diff --git a/commander.go b/commander.go deleted file mode 100644 index eb2d3750d..000000000 --- a/commander.go +++ /dev/null @@ -1,44 +0,0 @@ -package core - -import ( - "sync" -) - -// Commander is a feature that accepts commands from external source. -type Commander interface { - Feature -} - -type syncCommander struct { - sync.RWMutex - Commander -} - -func (c *syncCommander) Start() error { - c.RLock() - defer c.RUnlock() - - if c.Commander == nil { - return nil - } - - return c.Commander.Start() -} - -func (c *syncCommander) Close() error { - c.RLock() - defer c.RUnlock() - - if c.Commander == nil { - return nil - } - - return c.Commander.Close() -} - -func (c *syncCommander) Set(commander Commander) { - c.Lock() - defer c.Unlock() - - c.Commander = commander -} diff --git a/v2ray.go b/v2ray.go index 5538da7ee..aab56fe8d 100644 --- a/v2ray.go +++ b/v2ray.go @@ -28,7 +28,6 @@ type Instance struct { router syncRouter ihm syncInboundHandlerManager ohm syncOutboundHandlerManager - cmd syncCommander access sync.Mutex features []Feature @@ -105,7 +104,7 @@ func (s *Instance) Close() error { defer s.access.Unlock() s.running = false - for _, f := range s.features { + for _, f := range s.allFeatures() { f.Close() } @@ -119,7 +118,7 @@ func (s *Instance) Start() error { defer s.access.Unlock() s.running = true - for _, f := range s.features { + for _, f := range s.allFeatures() { if err := f.Start(); err != nil { return err } @@ -134,6 +133,8 @@ func (s *Instance) Start() error { // If feature is one of the following types, the corressponding feature in this Instance // will be replaced: DNSClient, PolicyManager, Router, Dispatcher, InboundHandlerManager, OutboundHandlerManager. func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error { + running := false + switch feature.(type) { case DNSClient, *DNSClient: s.dnsClient.Set(instance.(DNSClient)) @@ -147,19 +148,23 @@ func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error s.ihm.Set(instance.(InboundHandlerManager)) case OutboundHandlerManager, *OutboundHandlerManager: s.ohm.Set(instance.(OutboundHandlerManager)) - case Commander, *Commander: - s.cmd.Set(instance.(Commander)) + default: + s.access.Lock() + s.features = append(s.features, instance) + running = s.running + s.access.Unlock() } - s.access.Lock() - defer s.access.Unlock() - s.features = append(s.features, instance) - if s.running { + if running { return instance.Start() } return nil } +func (s *Instance) allFeatures() []Feature { + return append([]Feature{s.DNSClient(), s.PolicyManager(), s.Dispatcher(), s.Router(), s.InboundHandlerManager(), s.OutboundHandlerManager()}, s.features...) +} + // GetFeature returns a feature that was registered in this Instance. Nil if not found. // The returned Feature must implement common.HasType and whose type equals the given feature type. func (s *Instance) GetFeature(featureType interface{}) Feature { @@ -202,8 +207,3 @@ func (s *Instance) InboundHandlerManager() InboundHandlerManager { func (s *Instance) OutboundHandlerManager() OutboundHandlerManager { return &(s.ohm) } - -// Commander returns the Commander used by this Instance. The returned Commander is always functional. -func (s *Instance) Commander() Commander { - return &(s.cmd) -} From c335789e40a96fd29c41ee6d9dedab2eff9c9a5d Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 20 Feb 2018 21:22:41 +0100 Subject: [PATCH 038/183] close inner features before resetting --- dns.go | 2 ++ network.go | 11 +++++++++++ policy.go | 2 ++ router.go | 10 ++++++++++ 4 files changed, 25 insertions(+) diff --git a/dns.go b/dns.go index 5e94d1c2a..9a9b0c07d 100644 --- a/dns.go +++ b/dns.go @@ -52,6 +52,8 @@ func (d *syncDNSClient) Set(client DNSClient) { return } + d.Close() + d.Lock() defer d.Unlock() diff --git a/network.go b/network.go index d526a91f1..3b1b721a5 100644 --- a/network.go +++ b/network.go @@ -84,6 +84,12 @@ func (m *syncInboundHandlerManager) Close() error { } func (m *syncInboundHandlerManager) Set(manager InboundHandlerManager) { + if manager == nil { + return + } + + m.Close() + m.Lock() defer m.Unlock() @@ -161,6 +167,11 @@ func (m *syncOutboundHandlerManager) Close() error { } func (m *syncOutboundHandlerManager) Set(manager OutboundHandlerManager) { + if manager == nil { + return + } + + m.Close() m.Lock() defer m.Unlock() diff --git a/policy.go b/policy.go index 1ac9ac1fb..4cb9b3979 100644 --- a/policy.go +++ b/policy.go @@ -87,6 +87,8 @@ func (m *syncPolicyManager) Set(manager PolicyManager) { return } + m.Close() + m.Lock() defer m.Unlock() diff --git a/router.go b/router.go index d3a8031f0..bd1e2d814 100644 --- a/router.go +++ b/router.go @@ -54,6 +54,11 @@ func (d *syncDispatcher) Close() error { } func (d *syncDispatcher) Set(disp Dispatcher) { + if disp == nil { + return + } + + d.Close() d.Lock() defer d.Unlock() @@ -108,6 +113,11 @@ func (r *syncRouter) Close() error { } func (r *syncRouter) Set(router Router) { + if router == nil { + return + } + + r.Close() r.Lock() defer r.Unlock() From 3734195156631f861c4948941edc6a06f86f30a2 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 20 Feb 2018 22:07:50 +0100 Subject: [PATCH 039/183] fix log test --- app/log/command/command_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/log/command/command_test.go b/app/log/command/command_test.go index 44ebb15f0..03debdec8 100644 --- a/app/log/command/command_test.go +++ b/app/log/command/command_test.go @@ -5,8 +5,12 @@ import ( "testing" "v2ray.com/core" + "v2ray.com/core/app/dispatcher" "v2ray.com/core/app/log" . "v2ray.com/core/app/log/command" + "v2ray.com/core/app/proxyman" + _ "v2ray.com/core/app/proxyman/inbound" + _ "v2ray.com/core/app/proxyman/outbound" "v2ray.com/core/common/serial" . "v2ray.com/ext/assert" ) @@ -17,6 +21,9 @@ func TestLoggerRestart(t *testing.T) { v, err := core.New(&core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{}), + serial.ToTypedMessage(&dispatcher.Config{}), + serial.ToTypedMessage(&proxyman.InboundConfig{}), + serial.ToTypedMessage(&proxyman.OutboundConfig{}), }, }) From 955444adc9e67839fafadd298aa372608388d862 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 20 Feb 2018 22:07:58 +0100 Subject: [PATCH 040/183] fix setter methods --- dns.go | 3 +-- network.go | 5 ++--- policy.go | 3 +-- router.go | 4 ++-- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/dns.go b/dns.go index 9a9b0c07d..5546f1156 100644 --- a/dns.go +++ b/dns.go @@ -52,10 +52,9 @@ func (d *syncDNSClient) Set(client DNSClient) { return } - d.Close() - d.Lock() defer d.Unlock() + common.Close(d.DNSClient) d.DNSClient = client } diff --git a/network.go b/network.go index 3b1b721a5..3e36624b0 100644 --- a/network.go +++ b/network.go @@ -88,11 +88,10 @@ func (m *syncInboundHandlerManager) Set(manager InboundHandlerManager) { return } - m.Close() - m.Lock() defer m.Unlock() + common.Close(m.InboundHandlerManager) m.InboundHandlerManager = manager } @@ -171,9 +170,9 @@ func (m *syncOutboundHandlerManager) Set(manager OutboundHandlerManager) { return } - m.Close() m.Lock() defer m.Unlock() + common.Close(m.OutboundHandlerManager) m.OutboundHandlerManager = manager } diff --git a/policy.go b/policy.go index 4cb9b3979..52d84a8a7 100644 --- a/policy.go +++ b/policy.go @@ -87,10 +87,9 @@ func (m *syncPolicyManager) Set(manager PolicyManager) { return } - m.Close() - m.Lock() defer m.Unlock() + common.Close(m.PolicyManager) m.PolicyManager = manager } diff --git a/router.go b/router.go index bd1e2d814..6cbb3e792 100644 --- a/router.go +++ b/router.go @@ -58,10 +58,10 @@ func (d *syncDispatcher) Set(disp Dispatcher) { return } - d.Close() d.Lock() defer d.Unlock() + common.Close(d.Dispatcher) d.Dispatcher = disp } @@ -117,9 +117,9 @@ func (r *syncRouter) Set(router Router) { return } - r.Close() r.Lock() defer r.Unlock() + common.Close(r.Router) r.Router = router } From 655fb40b3fe0740659c0ab37f4897aee09e08020 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 20 Feb 2018 23:03:29 +0100 Subject: [PATCH 041/183] remove json tag --- testing/scenarios/common_coverage.go | 2 +- testing/scenarios/common_regular.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/scenarios/common_coverage.go b/testing/scenarios/common_coverage.go index d0c794410..6b14a3682 100644 --- a/testing/scenarios/common_coverage.go +++ b/testing/scenarios/common_coverage.go @@ -17,7 +17,7 @@ func BuildV2Ray() error { return nil } - cmd := exec.Command("go", "test", "-tags", "json coverage coveragemain", "-coverpkg", "v2ray.com/core/...", "-c", "-o", testBinaryPath, GetSourcePath()) + cmd := exec.Command("go", "test", "-tags", "coverage coveragemain", "-coverpkg", "v2ray.com/core/...", "-c", "-o", testBinaryPath, GetSourcePath()) return cmd.Run() } diff --git a/testing/scenarios/common_regular.go b/testing/scenarios/common_regular.go index 6c5662d82..fc3d29970 100644 --- a/testing/scenarios/common_regular.go +++ b/testing/scenarios/common_regular.go @@ -16,7 +16,7 @@ func BuildV2Ray() error { } fmt.Printf("Building V2Ray into path (%s)\n", testBinaryPath) - cmd := exec.Command("go", "build", "-tags=json", "-o="+testBinaryPath, GetSourcePath()) + cmd := exec.Command("go", "build", "-o="+testBinaryPath, GetSourcePath()) return cmd.Run() } From 87ef3b0a8f507e316d003301d70eae0da8aa06b6 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 21 Feb 2018 00:18:53 +0100 Subject: [PATCH 042/183] test case for dial --- testing/scenarios/feature_test.go | 103 ++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/testing/scenarios/feature_test.go b/testing/scenarios/feature_test.go index 22c3ff727..6549417b7 100644 --- a/testing/scenarios/feature_test.go +++ b/testing/scenarios/feature_test.go @@ -1,6 +1,7 @@ package scenarios import ( + "context" "io/ioutil" "net/http" "net/url" @@ -9,8 +10,11 @@ import ( xproxy "golang.org/x/net/proxy" "v2ray.com/core" + "v2ray.com/core/app/dispatcher" "v2ray.com/core/app/log" "v2ray.com/core/app/proxyman" + _ "v2ray.com/core/app/proxyman/inbound" + _ "v2ray.com/core/app/proxyman/outbound" "v2ray.com/core/app/router" clog "v2ray.com/core/common/log" "v2ray.com/core/common/net" @@ -748,3 +752,102 @@ func TestDomainSniffing(t *testing.T) { CloseAllServers(servers) } + +func TestDialV2Ray(t *testing.T) { + assert := With(t) + + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + assert(err, IsNil) + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + serverPort := pickPort() + serverConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: clog.Severity_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(serverPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + }), + }, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + clientConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&dispatcher.Config{}), + serial.ToTypedMessage(&proxyman.InboundConfig{}), + serial.ToTypedMessage(&proxyman.OutboundConfig{}), + }, + Inbound: []*core.InboundHandlerConfig{}, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }), + }, + }, + }, + }, + }), + }, + }, + } + + servers, err := InitializeServerConfigs(serverConfig) + assert(err, IsNil) + + client, err := core.New(clientConfig) + assert(err, IsNil) + + conn, err := core.Dial(context.Background(), client, dest) + assert(err, IsNil) + + payload := "commander request." + nBytes, err := conn.Write([]byte(payload)) + assert(err, IsNil) + assert(nBytes, Equals, len(payload)) + + response := make([]byte, 1024) + nBytes, err = conn.Read(response) + assert(err, IsNil) + assert(response[:nBytes], Equals, xor([]byte(payload))) + assert(conn.Close(), IsNil) + + CloseAllServers(servers) +} From af967c2b72aee4dc8c5673c66df64d66bc76e38c Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 21 Feb 2018 10:36:41 +0100 Subject: [PATCH 043/183] use golang 1.10 for release --- release/install.sh | 4 ++-- release/release-ci.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/release/install.sh b/release/install.sh index 7922373ef..764e764c7 100755 --- a/release/install.sh +++ b/release/install.sh @@ -1,7 +1,7 @@ #!/bin/bash -GO_AMD64=https://storage.googleapis.com/golang/go1.9.4.linux-amd64.tar.gz -GO_X86=https://storage.googleapis.com/golang/go1.9.4.linux-386.tar.gz +GO_AMD64=https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz +GO_X86=https://storage.googleapis.com/golang/go1.10.linux-386.tar.gz ARCH=$(uname -m) GO_CUR=${GO_AMD64} diff --git a/release/release-ci.sh b/release/release-ci.sh index 51b752211..fc1925578 100755 --- a/release/release-ci.sh +++ b/release/release-ci.sh @@ -25,7 +25,7 @@ echo ${SIGN_KEY_PASS} | gpg --passphrase-fd 0 --batch --import /v2ray/build/sign curl -L -o /v2ray/build/releases https://api.github.com/repos/v2ray/v2ray-core/releases GO_INSTALL=golang.tar.gz -curl -L -o ${GO_INSTALL} https://storage.googleapis.com/golang/go1.9.4.linux-amd64.tar.gz +curl -L -o ${GO_INSTALL} https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz tar -C /usr/local -xzf ${GO_INSTALL} export PATH=$PATH:/usr/local/go/bin From 88b25d38cbd9dd40bbb47dde863d1a4ed7118889 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 21 Feb 2018 17:05:29 +0100 Subject: [PATCH 044/183] simplify context retrieval --- app/commander/commander.go | 5 +---- app/dispatcher/default.go | 6 +----- app/dns/server.go | 6 +----- app/log/command/command.go | 5 +---- app/proxyman/command/command.go | 5 +---- app/proxyman/inbound/dynamic.go | 5 +---- app/proxyman/inbound/inbound.go | 5 +---- app/proxyman/mux/mux.go | 2 +- app/proxyman/outbound/handler.go | 5 +---- app/proxyman/outbound/outbound.go | 5 +---- app/router/router.go | 6 +----- context.go | 11 ++++++++++- proxy/dokodemo/dokodemo.go | 6 +----- proxy/freedom/freedom.go | 6 +----- proxy/http/server.go | 5 +---- proxy/shadowsocks/client.go | 6 +----- proxy/shadowsocks/server.go | 6 +----- proxy/socks/client.go | 6 +----- proxy/socks/server.go | 5 +---- proxy/vmess/inbound/inbound.go | 6 +----- proxy/vmess/outbound/outbound.go | 6 +----- 21 files changed, 30 insertions(+), 88 deletions(-) diff --git a/app/commander/commander.go b/app/commander/commander.go index e1e408a97..19991fce4 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -22,10 +22,7 @@ type Commander struct { } func NewCommander(ctx context.Context, config *Config) (*Commander, error) { - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context.") - } + v := core.MustFromContext(ctx) c := &Commander{ config: *config, ohm: v.OutboundHandlerManager(), diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 6be84fcf3..1aba8be66 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -27,11 +27,7 @@ type DefaultDispatcher struct { // NewDefaultDispatcher create a new DefaultDispatcher. func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatcher, error) { - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context.") - } - + v := core.MustFromContext(ctx) d := &DefaultDispatcher{ ohm: v.OutboundHandlerManager(), router: v.Router(), diff --git a/app/dns/server.go b/app/dns/server.go index 2972ff5cf..866c5ff0f 100644 --- a/app/dns/server.go +++ b/app/dns/server.go @@ -54,11 +54,7 @@ func New(ctx context.Context, config *Config) (*Server, error) { return nil }, } - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context.") - } - + v := core.MustFromContext(ctx) if err := v.RegisterFeature((*core.DNSClient)(nil), server); err != nil { return nil, newError("unable to register DNSClient.").Base(err) } diff --git a/app/log/command/command.go b/app/log/command/command.go index 84d3078c9..7e7804996 100644 --- a/app/log/command/command.go +++ b/app/log/command/command.go @@ -41,10 +41,7 @@ func (s *service) Register(server *grpc.Server) { func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { - s := core.FromContext(ctx) - if s == nil { - return nil, newError("V is not in context.") - } + s := core.MustFromContext(ctx) return &service{v: s}, nil })) } diff --git a/app/proxyman/command/command.go b/app/proxyman/command/command.go index c0357579d..023b83c5e 100644 --- a/app/proxyman/command/command.go +++ b/app/proxyman/command/command.go @@ -139,10 +139,7 @@ func (s *service) Register(server *grpc.Server) { func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { - s := core.FromContext(ctx) - if s == nil { - return nil, newError("V is not in context.") - } + s := core.MustFromContext(ctx) return &service{v: s}, nil })) } diff --git a/app/proxyman/inbound/dynamic.go b/app/proxyman/inbound/dynamic.go index e6f77972b..ca2cc9440 100644 --- a/app/proxyman/inbound/dynamic.go +++ b/app/proxyman/inbound/dynamic.go @@ -29,10 +29,7 @@ type DynamicInboundHandler struct { } func NewDynamicInboundHandler(ctx context.Context, tag string, receiverConfig *proxyman.ReceiverConfig, proxyConfig interface{}) (*DynamicInboundHandler, error) { - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context.") - } + v := core.MustFromContext(ctx) h := &DynamicInboundHandler{ tag: tag, proxyConfig: proxyConfig, diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index e7a190bb4..c752216f9 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -24,10 +24,7 @@ func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) m := &Manager{ taggedHandlers: make(map[string]core.InboundHandler), } - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context") - } + v := core.MustFromContext(ctx) if err := v.RegisterFeature((*core.InboundHandlerManager)(nil), m); err != nil { return nil, newError("unable to register InboundHandlerManager").Base(err) } diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index b921dbd69..85a1ae5ac 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -261,7 +261,7 @@ type Server struct { // NewServer creates a new mux.Server. func NewServer(ctx context.Context) *Server { s := &Server{ - dispatcher: core.FromContext(ctx).Dispatcher(), + dispatcher: core.MustFromContext(ctx).Dispatcher(), } return s } diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index 63a4ec55b..c2cf8ebdf 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -22,10 +22,7 @@ type Handler struct { } func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (*Handler, error) { - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context") - } + v := core.MustFromContext(ctx) h := &Handler{ config: config, outboundManager: v.OutboundHandlerManager(), diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index 52848f324..91bc2c441 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -25,10 +25,7 @@ func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) m := &Manager{ taggedHandler: make(map[string]core.OutboundHandler), } - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context") - } + v := core.MustFromContext(ctx) if err := v.RegisterFeature((*core.OutboundHandlerManager)(nil), m); err != nil { return nil, newError("unable to register OutboundHandlerManager").Base(err) } diff --git a/app/router/router.go b/app/router/router.go index 5e0109d5b..4267064c5 100644 --- a/app/router/router.go +++ b/app/router/router.go @@ -18,11 +18,7 @@ type Router struct { } func NewRouter(ctx context.Context, config *Config) (*Router, error) { - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context") - } - + v := core.MustFromContext(ctx) r := &Router{ domainStrategy: config.DomainStrategy, rules: make([]Rule, len(config.Rule)), diff --git a/context.go b/context.go index c92f0bacc..2651f3fe2 100644 --- a/context.go +++ b/context.go @@ -8,10 +8,19 @@ type key int const v2rayKey key = 1 -// FromContext returns a Instance from the given context, or nil if the context doesn't contain one. +// FromContext returns an Instance from the given context, or nil if the context doesn't contain one. func FromContext(ctx context.Context) *Instance { if s, ok := ctx.Value(v2rayKey).(*Instance); ok { return s } return nil } + +// MustFromContext returns an Instance from the given context, or panics if not present. +func MustFromContext(ctx context.Context) *Instance { + v := FromContext(ctx) + if v == nil { + panic("V is not in context.") + } + return v +} diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 8f834b9a2..78be47c12 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -28,11 +28,7 @@ func New(ctx context.Context, config *Config) (*DokodemoDoor, error) { if config.NetworkList == nil || config.NetworkList.Size() == 0 { return nil, newError("no network specified") } - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context.") - } - + v := core.MustFromContext(ctx) d := &DokodemoDoor{ config: config, address: config.GetPredefinedAddress(), diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 80292fb60..a45f0a70d 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -27,11 +27,7 @@ type Handler struct { // New creates a new Freedom handler. func New(ctx context.Context, config *Config) (*Handler, error) { - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not found in context.") - } - + v := core.MustFromContext(ctx) f := &Handler{ config: *config, policyManager: v.PolicyManager(), diff --git a/proxy/http/server.go b/proxy/http/server.go index 9179de132..2acc426b8 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -31,10 +31,7 @@ type Server struct { func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { s := &Server{ config: config, - v: core.FromContext(ctx), - } - if s.v == nil { - return nil, newError("V is not in context.") + v: core.MustFromContext(ctx), } return s, nil diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index 58e659db3..317a6588d 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -32,12 +32,8 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { } client := &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), - v: core.FromContext(ctx), + v: core.MustFromContext(ctx), } - if client.v == nil { - return nil, newError("V is not in context.") - } - return client, nil } diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 31b345a07..96fb65107 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -39,11 +39,7 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { config: config, user: config.GetUser(), account: account, - v: core.FromContext(ctx), - } - - if s.v == nil { - return nil, newError("V is not in context.") + v: core.MustFromContext(ctx), } return s, nil diff --git a/proxy/socks/client.go b/proxy/socks/client.go index 78191e1e6..61d6495fd 100644 --- a/proxy/socks/client.go +++ b/proxy/socks/client.go @@ -32,11 +32,7 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { return nil, newError("0 target server") } - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context") - } - + v := core.MustFromContext(ctx) return &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), policyManager: v.PolicyManager(), diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 262ef4ad5..5b0524edd 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -27,10 +27,7 @@ type Server struct { func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { s := &Server{ config: config, - v: core.FromContext(ctx), - } - if s.v == nil { - return nil, newError("V is not in context.") + v: core.MustFromContext(ctx), } return s, nil } diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 145a60ba2..792bd3438 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -105,11 +105,7 @@ type Handler struct { // New creates a new VMess inbound handler. func New(ctx context.Context, config *Config) (*Handler, error) { - v := core.FromContext(ctx) - if v == nil { - return nil, newError("V is not in context.") - } - + v := core.MustFromContext(ctx) handler := &Handler{ policyManager: v.PolicyManager(), inboundHandlerManager: v.InboundHandlerManager(), diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index 3225cb7ac..e4cb0faa2 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -35,11 +35,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) { handler := &Handler{ serverList: serverList, serverPicker: protocol.NewRoundRobinServerPicker(serverList), - v: core.FromContext(ctx), - } - - if handler.v == nil { - return nil, newError("V is not in context.") + v: core.MustFromContext(ctx), } return handler, nil From 098244530bc8221473600648c5d5537389fa5cd6 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 21 Feb 2018 21:42:33 +0100 Subject: [PATCH 045/183] update Must2 --- common/common.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/common/common.go b/common/common.go index b26ba7704..5ab6a7704 100644 --- a/common/common.go +++ b/common/common.go @@ -11,9 +11,8 @@ func Must(err error) { } } -// Must2 panics if the second parameter is not nil. -func Must2(v interface{}, err error) { - if err != nil { - panic(err) - } +// Must2 panics if the second parameter is not nil, otherwise returns the first parameter. +func Must2(v interface{}, err error) interface{} { + Must(err) + return v } From 1077e33d628df4942620dd2d8d81e73bc1841319 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 22 Feb 2018 10:31:08 +0100 Subject: [PATCH 046/183] unify address reading in socks and shadowsocks --- proxy/shadowsocks/protocol.go | 40 ++++++-------------- proxy/socks/protocol.go | 70 ++++++++++++++++++++--------------- proxy/socks/protocol_test.go | 55 +++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 57 deletions(-) diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index 62fe14989..48fbd486a 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -8,6 +8,7 @@ import ( "v2ray.com/core/common" "v2ray.com/core/common/bitmask" "v2ray.com/core/common/buf" + "v2ray.com/core/common/dice" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/proxy/socks" @@ -76,36 +77,18 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea } addrType := (buffer.Byte(0) & 0x0F) - switch addrType { - case AddrTypeIPv4: - if err := buffer.AppendSupplier(buf.ReadFullFrom(br, 4)); err != nil { - return nil, nil, newError("failed to read IPv4 address").Base(err) - } - request.Address = net.IPAddress(buffer.BytesFrom(-4)) - case AddrTypeIPv6: - if err := buffer.AppendSupplier(buf.ReadFullFrom(br, 16)); err != nil { - return nil, nil, newError("failed to read IPv6 address").Base(err) - } - request.Address = net.IPAddress(buffer.BytesFrom(-16)) - case AddrTypeDomain: - if err := buffer.AppendSupplier(buf.ReadFullFrom(br, 1)); err != nil { - return nil, nil, newError("failed to read domain lenth.").Base(err) - } - domainLength := int(buffer.BytesFrom(-1)[0]) - err = buffer.AppendSupplier(buf.ReadFullFrom(br, domainLength)) - if err != nil { - return nil, nil, newError("failed to read domain").Base(err) - } - request.Address = net.DomainAddress(string(buffer.BytesFrom(-domainLength))) - default: - // Check address validity after OTA verification. + + addr, port, err := socks.ReadAddress(buffer, addrType, br) + if err != nil { + // Invalid address. Continue to read some bytes to confuse client. + nBytes := dice.Roll(32) + buffer.Clear() + buffer.AppendSupplier(buf.ReadFullFrom(br, nBytes)) + return nil, nil, newError("failed to read address").Base(err) } - err = buffer.AppendSupplier(buf.ReadFullFrom(br, 2)) - if err != nil { - return nil, nil, newError("failed to read port").Base(err) - } - request.Port = net.PortFromBytes(buffer.BytesFrom(-2)) + request.Address = addr + request.Port = port if request.Option.Has(RequestOptionOneTimeAuth) { actualAuth := make([]byte, AuthSize) @@ -320,6 +303,7 @@ func DecodeUDPPacket(user *protocol.User, payload *buf.Buffer) (*protocol.Reques addrType := (payload.Byte(0) & 0x0F) payload.SliceFrom(1) + switch addrType { case AddrTypeIPv4: request.Address = net.IPAddress(payload.BytesTo(4)) diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index b9920630d..d9dbffb47 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -279,7 +279,7 @@ func writeSocks5Response(writer io.Writer, errCode byte, address net.Address, po func writeSocks4Response(writer io.Writer, errCode byte, address net.Address, port net.Port) error { buffer := buf.NewLocal(32) buffer.AppendBytes(0x00, errCode) - buffer.AppendSupplier(serial.WriteUint16(port.Value())) + common.Must(buffer.AppendSupplier(serial.WriteUint16(port.Value()))) buffer.Append(address.IP()) _, err := writer.Write(buffer.Bytes()) return err @@ -392,6 +392,40 @@ func (w *UDPWriter) Write(b []byte) (int, error) { return len(b), nil } +func ReadAddress(b *buf.Buffer, addrType byte, reader io.Reader) (net.Address, net.Port, error) { + var address net.Address + switch addrType { + case addrTypeIPv4: + if err := b.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { + return nil, 0, err + } + address = net.IPAddress(b.BytesFrom(-4)) + case addrTypeIPv6: + if err := b.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil { + return nil, 0, err + } + address = net.IPAddress(b.BytesFrom(-16)) + case addrTypeDomain: + if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { + return nil, 0, err + } + domainLength := int(b.Byte(b.Len() - 1)) + if err := b.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { + return nil, 0, err + } + address = net.DomainAddress(string(b.BytesFrom(-domainLength))) + default: + return nil, 0, newError("unknown address type: ", addrType) + } + + if err := b.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { + return nil, 0, err + } + port := net.PortFromBytes(b.BytesFrom(-2)) + + return address, port, nil +} + func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { authByte := byte(authNotRequired) if request.User != nil { @@ -444,7 +478,10 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i command = byte(cmdUDPPort) } b.AppendBytes(socks5Version, command, 0x00 /* reserved */) - AppendAddress(b, request.Address, request.Port) + if err := AppendAddress(b, request.Address, request.Port); err != nil { + return nil, err + } + if _, err := writer.Write(b.Bytes()); err != nil { return nil, err } @@ -463,35 +500,10 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i b.Clear() - var address net.Address - switch addrType { - case addrTypeIPv4: - if err := b.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { - return nil, err - } - address = net.IPAddress(b.Bytes()) - case addrTypeIPv6: - if err := b.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil { - return nil, err - } - address = net.IPAddress(b.Bytes()) - case addrTypeDomain: - if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { - return nil, err - } - domainLength := int(b.Byte(0)) - if err := b.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { - return nil, err - } - address = net.DomainAddress(string(b.BytesFrom(-domainLength))) - default: - return nil, newError("unknown address type: ", addrType) - } - - if err := b.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { + address, port, err := ReadAddress(b, addrType, reader) + if err != nil { return nil, err } - port := net.PortFromBytes(b.BytesFrom(-2)) if request.Command == protocol.RequestCommandUDP { udpRequest := &protocol.RequestHeader{ diff --git a/proxy/socks/protocol_test.go b/proxy/socks/protocol_test.go index a5cb18cfb..bd14dfe5f 100644 --- a/proxy/socks/protocol_test.go +++ b/proxy/socks/protocol_test.go @@ -1,10 +1,12 @@ package socks_test import ( + "bytes" "testing" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" + _ "v2ray.com/core/common/net/testing" "v2ray.com/core/common/protocol" . "v2ray.com/core/proxy/socks" . "v2ray.com/ext/assert" @@ -32,3 +34,56 @@ func TestUDPEncoding(t *testing.T) { assert(err, IsNil) assert(decodedPayload[0].Bytes(), Equals, content) } + +func TestReadAddress(t *testing.T) { + assert := With(t) + + data := []struct { + AddrType byte + Input []byte + Address net.Address + Port net.Port + Error bool + }{ + { + AddrType: 0, + Input: []byte{0, 0, 0, 0}, + Error: true, + }, + { + AddrType: 1, + Input: []byte{0, 0, 0, 0, 0, 53}, + Address: net.IPAddress([]byte{0, 0, 0, 0}), + Port: net.Port(53), + }, + { + AddrType: 4, + Input: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 80}, + Address: net.IPAddress([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}), + Port: net.Port(80), + }, + { + AddrType: 3, + Input: []byte{9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 80}, + Address: net.DomainAddress("v2ray.com"), + Port: net.Port(80), + }, + { + AddrType: 3, + Input: []byte{9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0}, + Error: true, + }, + } + + for _, tc := range data { + b := buf.New() + addr, port, err := ReadAddress(b, tc.AddrType, bytes.NewBuffer(tc.Input)) + b.Release() + if tc.Error { + assert(err, IsNotNil) + } else { + assert(addr, Equals, tc.Address) + assert(port, Equals, tc.Port) + } + } +} From 7e6b45ace4d9ee7d1f29c0ef169adf0cecd6b088 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 22 Feb 2018 10:34:14 +0100 Subject: [PATCH 047/183] format --- app/policy/manager_test.go | 2 +- testing/scenarios/command_test.go | 4 ++-- testing/scenarios/policy_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/policy/manager_test.go b/app/policy/manager_test.go index d56afb86f..dde4e2acc 100644 --- a/app/policy/manager_test.go +++ b/app/policy/manager_test.go @@ -15,7 +15,7 @@ func TestPolicy(t *testing.T) { manager, err := New(context.Background(), &Config{ Level: map[uint32]*Policy{ - 0: &Policy{ + 0: { Timeout: &Policy_Timeout{ Handshake: &Second{ Value: 2, diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go index f27b49238..b15431f64 100644 --- a/testing/scenarios/command_test.go +++ b/testing/scenarios/command_test.go @@ -170,7 +170,7 @@ func TestCommanderAddRemoveUser(t *testing.T) { }), serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ - 0: &policy.Policy{ + 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, @@ -224,7 +224,7 @@ func TestCommanderAddRemoveUser(t *testing.T) { App: []*serial.TypedMessage{ serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ - 0: &policy.Policy{ + 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, diff --git a/testing/scenarios/policy_test.go b/testing/scenarios/policy_test.go index b3187cc64..9e8b7ce7f 100644 --- a/testing/scenarios/policy_test.go +++ b/testing/scenarios/policy_test.go @@ -54,7 +54,7 @@ func TestVMessClosing(t *testing.T) { App: []*serial.TypedMessage{ serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ - 0: &policy.Policy{ + 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, @@ -93,7 +93,7 @@ func TestVMessClosing(t *testing.T) { App: []*serial.TypedMessage{ serial.ToTypedMessage(&policy.Config{ Level: map[uint32]*policy.Policy{ - 0: &policy.Policy{ + 0: { Timeout: &policy.Policy_Timeout{ UplinkOnly: &policy.Second{Value: 0}, DownlinkOnly: &policy.Second{Value: 0}, From 0920af171bba6d1801930429fce5f29b1df800d7 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 22 Feb 2018 10:49:06 +0100 Subject: [PATCH 048/183] remove redundent code --- proxy/socks/protocol.go | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index d9dbffb47..356891369 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -139,34 +139,13 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol buffer.Clear() request.Version = socks5Version - switch addrType { - case addrTypeIPv4: - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { - return nil, err - } - request.Address = net.IPAddress(buffer.Bytes()) - case addrTypeIPv6: - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil { - return nil, err - } - request.Address = net.IPAddress(buffer.Bytes()) - case addrTypeDomain: - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { - return nil, err - } - domainLength := int(buffer.Byte(0)) - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { - return nil, err - } - request.Address = net.ParseAddress(string(buffer.BytesFrom(-domainLength))) - default: - return nil, newError("Unknown address type: ", addrType) - } - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { - return nil, err + addr, port, err := ReadAddress(buffer, addrType, reader) + if err != nil { + return nil, newError("failed to read address").Base(err) } - request.Port = net.PortFromBytes(buffer.BytesFrom(-2)) + request.Address = addr + request.Port = port responseAddress := net.AnyIP responsePort := net.Port(1717) From cdfbd0317f0826bc09adbaff2d123312862865e9 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 22 Feb 2018 11:58:46 +0100 Subject: [PATCH 049/183] remove unused function --- proxy/errors.generated.go | 5 ----- proxy/proxy.go | 2 -- 2 files changed, 7 deletions(-) delete mode 100644 proxy/errors.generated.go diff --git a/proxy/errors.generated.go b/proxy/errors.generated.go deleted file mode 100644 index f144f5d19..000000000 --- a/proxy/errors.generated.go +++ /dev/null @@ -1,5 +0,0 @@ -package proxy - -import "v2ray.com/core/common/errors" - -func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("Proxy") } diff --git a/proxy/proxy.go b/proxy/proxy.go index 7174c2453..0278d1185 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -5,8 +5,6 @@ // 2. Register a config creator through common.RegisterConfig. package proxy -//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg proxy -path Proxy - import ( "context" From 80a1e73361cf20c3dec57d83084c0508aa5559d4 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 22 Feb 2018 15:25:26 +0100 Subject: [PATCH 050/183] fix error propagation in vmess server --- proxy/vmess/encoding/server.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index bd691db2d..2c52287ca 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -195,7 +195,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request // 1 bytes reserved request.Command = protocol.RequestCommand(buffer.Byte(37)) - invalidRequest := false + var invalidRequestErr error switch request.Command { case protocol.RequestCommandMux: request.Address = net.DomainAddress("v1.mux.cool") @@ -205,18 +205,17 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request request.Address = addr request.Port = port } else { - invalidRequest = true - newError("failed to read address").Base(err).WriteToLog() + invalidRequestErr = newError("invalid address").Base(err) } default: - invalidRequest = true + invalidRequestErr = newError("invalid request command: ", request.Command) } - if invalidRequest { + if invalidRequestErr != nil { randomLen := dice.Roll(32) + 1 // Read random number of bytes for prevent detection. buffer.AppendSupplier(buf.ReadFullFrom(decryptor, randomLen)) - return nil, newError("invalid request") + return nil, invalidRequestErr } if padingLen > 0 { From 6b872c266c5f870ede15a7546eeb8b0bb66c591f Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 22 Feb 2018 15:26:00 +0100 Subject: [PATCH 051/183] session id --- app/dispatcher/default.go | 8 ++-- app/proxyman/inbound/worker.go | 14 +++++-- app/proxyman/mux/mux.go | 12 +++--- app/proxyman/outbound/handler.go | 8 ++-- common/errors/errors.go | 51 +++++++++++++++++++++++++- common/session/session.go | 34 +++++++++++++++++ proxy/dokodemo/dokodemo.go | 2 +- proxy/freedom/freedom.go | 6 +-- proxy/http/server.go | 4 +- proxy/shadowsocks/server.go | 12 +++--- proxy/socks/client.go | 4 +- proxy/socks/server.go | 12 +++--- proxy/vmess/encoding/client.go | 9 ++--- proxy/vmess/inbound/inbound.go | 8 ++-- proxy/vmess/outbound/outbound.go | 2 +- transport/internet/tcp/dialer.go | 2 +- transport/internet/tcp/hub.go | 2 +- transport/internet/udp/dispatcher.go | 6 +-- transport/internet/websocket/dialer.go | 2 +- 19 files changed, 143 insertions(+), 55 deletions(-) create mode 100644 common/session/session.go diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 1aba8be66..16079bf6b 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -62,7 +62,7 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin go func() { domain, err := snifer(ctx, sniferList, outbound) if err == nil { - newError("sniffed domain: ", domain).WriteToLog() + newError("sniffed domain: ", domain).WithContext(ctx).WriteToLog() destination.Address = net.ParseAddress(domain) ctx = proxy.ContextWithTarget(ctx, destination) } @@ -107,13 +107,13 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, outbound ray.Out if d.router != nil { if tag, err := d.router.PickRoute(ctx); err == nil { if handler := d.ohm.GetHandler(tag); handler != nil { - newError("taking detour [", tag, "] for [", destination, "]").WriteToLog() + newError("taking detour [", tag, "] for [", destination, "]").WithContext(ctx).WriteToLog() dispatcher = handler } else { - newError("nonexisting tag: ", tag).AtWarning().WriteToLog() + newError("nonexisting tag: ", tag).AtWarning().WithContext(ctx).WriteToLog() } } else { - newError("default route for ", destination).WriteToLog() + newError("default route for ", destination).WithContext(ctx).WriteToLog() } } dispatcher.Dispatch(ctx, outbound) diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index 1c63be3d2..afa09aa49 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -7,6 +7,8 @@ import ( "sync/atomic" "time" + "v2ray.com/core/common/session" + "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/common" @@ -41,10 +43,13 @@ type tcpWorker struct { func (w *tcpWorker) callback(conn internet.Connection) { ctx, cancel := context.WithCancel(context.Background()) + sid := session.NewID() + ctx = session.ContextWithID(ctx, sid) + if w.recvOrigDest { dest, err := tcp.GetOriginalDestination(conn) if err != nil { - newError("failed to get original destination").Base(err).WriteToLog() + newError("failed to get original destination").WithContext(ctx).Base(err).WriteToLog() } if dest.IsValid() { ctx = proxy.ContextWithOriginalTarget(ctx, dest) @@ -59,11 +64,11 @@ func (w *tcpWorker) callback(conn internet.Connection) { ctx = proxyman.ContextWithProtocolSniffers(ctx, w.sniffers) } if err := w.proxy.Process(ctx, net.Network_TCP, conn, w.dispatcher); err != nil { - newError("connection ends").Base(err).WriteToLog() + newError("connection ends").Base(err).WithContext(ctx).WriteToLog() } cancel() if err := conn.Close(); err != nil { - newError("failed to close connection").Base(err).WriteToLog() + newError("failed to close connection").Base(err).WithContext(ctx).WriteToLog() } } @@ -220,6 +225,9 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest if !existing { go func() { ctx := context.Background() + sid := session.NewID() + ctx = session.ContextWithID(ctx, sid) + if originalDest.IsValid() { ctx = proxy.ContextWithOriginalTarget(ctx, originalDest) } diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index 85a1ae5ac..2119dcebc 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -149,14 +149,14 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) { defer writer.Close() defer s.Close() - newError("dispatching request to ", dest).WriteToLog() + newError("dispatching request to ", dest).WithContext(ctx).WriteToLog() data, _ := s.input.ReadTimeout(time.Millisecond * 500) if err := writer.WriteMultiBuffer(data); err != nil { - newError("failed to write first payload").Base(err).WriteToLog() + newError("failed to write first payload").Base(err).WithContext(ctx).WriteToLog() return } if err := buf.Copy(s.input, writer); err != nil { - newError("failed to fetch all input").Base(err).WriteToLog() + newError("failed to fetch all input").Base(err).WithContext(ctx).WriteToLog() } } @@ -298,7 +298,7 @@ type ServerWorker struct { func handle(ctx context.Context, s *Session, output buf.Writer) { writer := NewResponseWriter(s.ID, output, s.transferType) if err := buf.Copy(s.input, writer); err != nil { - newError("session ", s.ID, " ends.").Base(err).WriteToLog() + newError("session ", s.ID, " ends.").Base(err).WithContext(ctx).WriteToLog() } writer.Close() s.Close() @@ -312,7 +312,7 @@ func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.Bu } func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error { - newError("received request for ", meta.Target).WriteToLog() + newError("received request for ", meta.Target).WithContext(ctx).WriteToLog() inboundRay, err := w.dispatcher.Dispatch(ctx, meta.Target) if err != nil { if meta.Option.Has(OptionData) { @@ -397,7 +397,7 @@ func (w *ServerWorker) run(ctx context.Context) { err := w.handleFrame(ctx, reader) if err != nil { if errors.Cause(err) != io.EOF { - newError("unexpected EOF").Base(err).WriteToLog() + newError("unexpected EOF").Base(err).WithContext(ctx).WriteToLog() input.CloseError() } return diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index c2cf8ebdf..a70513078 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -78,14 +78,14 @@ func (h *Handler) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) { if h.mux != nil { err := h.mux.Dispatch(ctx, outboundRay) if err != nil { - newError("failed to process outbound traffic").Base(err).WriteToLog() + newError("failed to process outbound traffic").Base(err).WithContext(ctx).WriteToLog() outboundRay.OutboundOutput().CloseError() } } else { err := h.proxy.Process(ctx, outboundRay, h) // Ensure outbound ray is properly closed. if err != nil { - newError("failed to process outbound traffic").Base(err).WriteToLog() + newError("failed to process outbound traffic").Base(err).WithContext(ctx).WriteToLog() outboundRay.OutboundOutput().CloseError() } else { outboundRay.OutboundOutput().Close() @@ -101,14 +101,14 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn tag := h.senderSettings.ProxySettings.Tag handler := h.outboundManager.GetHandler(tag) if handler != nil { - newError("proxying to ", tag, " for dest ", dest).AtDebug().WriteToLog() + newError("proxying to ", tag, " for dest ", dest).AtDebug().WithContext(ctx).WriteToLog() ctx = proxy.ContextWithTarget(ctx, dest) stream := ray.NewRay(ctx) go handler.Dispatch(ctx, stream) return ray.NewConnection(stream.InboundOutput(), stream.InboundInput()), nil } - newError("failed to get outbound handler with tag: ", tag).AtWarning().WriteToLog() + newError("failed to get outbound handler with tag: ", tag).AtWarning().WithContext(ctx).WriteToLog() } if h.senderSettings.Via != nil { diff --git a/common/errors/errors.go b/common/errors/errors.go index 3d9996ab1..a3c2758d9 100644 --- a/common/errors/errors.go +++ b/common/errors/errors.go @@ -2,10 +2,12 @@ package errors import ( + "context" "strings" "v2ray.com/core/common/log" "v2ray.com/core/common/serial" + "v2ray.com/core/common/session" ) type hasInnerError interface { @@ -17,12 +19,17 @@ type hasSeverity interface { Severity() log.Severity } +type hasContext interface { + Context() context.Context +} + // Error is an error object with underlying error. type Error struct { message []interface{} inner error severity log.Severity path []string + ctx context.Context } // Error implements error.Error(). @@ -50,6 +57,27 @@ func (v *Error) Base(err error) *Error { return v } +func (v *Error) WithContext(ctx context.Context) *Error { + v.ctx = ctx + return v +} + +func (v *Error) Context() context.Context { + if v.ctx != nil { + return v.ctx + } + + if v.inner == nil { + return nil + } + + if c, ok := v.inner.(hasContext); ok { + return c.Context() + } + + return nil +} + func (v *Error) atSeverity(s log.Severity) *Error { v.severity = s return v @@ -103,9 +131,21 @@ func (v *Error) String() string { // WriteToLog writes current error into log. func (v *Error) WriteToLog() { + ctx := v.Context() + var sid session.ID + if ctx != nil { + sid = session.IDFromContext(ctx) + } + var c interface{} = v + if sid > 0 { + c = sessionLog{ + id: sid, + content: v, + } + } log.Record(&log.GeneralMessage{ Severity: GetSeverity(v), - Content: v, + Content: c, }) } @@ -139,3 +179,12 @@ func GetSeverity(err error) log.Severity { } return log.Severity_Info } + +type sessionLog struct { + id session.ID + content interface{} +} + +func (s sessionLog) String() string { + return serial.Concat("[", s.id, "] ", s.content) +} diff --git a/common/session/session.go b/common/session/session.go new file mode 100644 index 000000000..af7aa6558 --- /dev/null +++ b/common/session/session.go @@ -0,0 +1,34 @@ +package session + +import ( + "context" + "math/rand" +) + +type ID uint32 + +func NewID() ID { + for { + id := ID(rand.Uint32()) + if id != 0 { + return id + } + } +} + +type sessionKey int + +const ( + idSessionKey sessionKey = iota +) + +func ContextWithID(ctx context.Context, id ID) context.Context { + return context.WithValue(ctx, idSessionKey, id) +} + +func IDFromContext(ctx context.Context) ID { + if id, ok := ctx.Value(idSessionKey).(ID); ok { + return id + } + return 0 +} diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 78be47c12..1445f864f 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -53,7 +53,7 @@ func (d *DokodemoDoor) policy() core.Policy { } func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher core.Dispatcher) error { - newError("processing connection from: ", conn.RemoteAddr()).AtDebug().WriteToLog() + newError("processing connection from: ", conn.RemoteAddr()).AtDebug().WithContext(ctx).WriteToLog() dest := net.Destination{ Network: network, Address: d.address, diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index a45f0a70d..7868c3c96 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -56,7 +56,7 @@ func (h *Handler) resolveIP(ctx context.Context, domain string) net.Address { ips, err := h.dns.LookupIP(domain) if err != nil { - newError("failed to get IP address for domain ", domain).Base(err).WriteToLog() + newError("failed to get IP address for domain ", domain).Base(err).WithContext(ctx).WriteToLog() } if len(ips) == 0 { return nil @@ -75,7 +75,7 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial Port: net.Port(server.Port), } } - newError("opening connection to ", destination).WriteToLog() + newError("opening connection to ", destination).WithContext(ctx).WriteToLog() input := outboundRay.OutboundInput() output := outboundRay.OutboundOutput() @@ -88,7 +88,7 @@ func (h *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial Address: ip, Port: destination.Port, } - newError("changing destination to ", destination).WriteToLog() + newError("changing destination to ", destination).WithContext(ctx).WriteToLog() } } diff --git a/proxy/http/server.go b/proxy/http/server.go index 2acc426b8..6668c03f8 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -121,7 +121,7 @@ Start: } } - newError("request to Method [", request.Method, "] Host [", request.Host, "] with URL [", request.URL, "]").WriteToLog() + newError("request to Method [", request.Method, "] Host [", request.Host, "] with URL [", request.URL, "]").WithContext(ctx).WriteToLog() conn.SetReadDeadline(time.Time{}) defaultPort := net.Port(80) @@ -276,7 +276,7 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri result = nil } } else { - newError("failed to read response from ", request.Host).Base(err).AtWarning().WriteToLog() + newError("failed to read response from ", request.Host).Base(err).AtWarning().WithContext(ctx).WriteToLog() response = &http.Response{ Status: "Service Unavailable", StatusCode: 503, diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 96fb65107..63e3f2a04 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -80,7 +80,7 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection request, data, err := DecodeUDPPacket(s.user, payload) if err != nil { if source, ok := proxy.SourceFromContext(ctx); ok { - newError("dropping invalid UDP packet from: ", source).Base(err).WriteToLog() + newError("dropping invalid UDP packet from: ", source).Base(err).WithContext(ctx).WriteToLog() log.Record(&log.AccessMessage{ From: source, To: "", @@ -93,13 +93,13 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection } if request.Option.Has(RequestOptionOneTimeAuth) && s.account.OneTimeAuth == Account_Disabled { - newError("client payload enables OTA but server doesn't allow it").WriteToLog() + newError("client payload enables OTA but server doesn't allow it").WithContext(ctx).WriteToLog() payload.Release() continue } if !request.Option.Has(RequestOptionOneTimeAuth) && s.account.OneTimeAuth == Account_Enabled { - newError("client payload disables OTA but server forces it").WriteToLog() + newError("client payload disables OTA but server forces it").WithContext(ctx).WriteToLog() payload.Release() continue } @@ -113,7 +113,7 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection Reason: "", }) } - newError("tunnelling request to ", dest).WriteToLog() + newError("tunnelling request to ", dest).WithContext(ctx).WriteToLog() ctx = protocol.ContextWithUser(ctx, request.User) udpServer.Dispatch(ctx, dest, data, func(payload *buf.Buffer) { @@ -121,7 +121,7 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection data, err := EncodeUDPPacket(request, payload.Bytes()) if err != nil { - newError("failed to encode UDP packet").Base(err).AtWarning().WriteToLog() + newError("failed to encode UDP packet").Base(err).AtWarning().WithContext(ctx).WriteToLog() return } defer data.Release() @@ -159,7 +159,7 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, Status: log.AccessAccepted, Reason: "", }) - newError("tunnelling request to ", dest).WriteToLog() + newError("tunnelling request to ", dest).WithContext(ctx).WriteToLog() ctx = protocol.ContextWithUser(ctx, request.User) diff --git a/proxy/socks/client.go b/proxy/socks/client.go index 61d6495fd..8f450fe63 100644 --- a/proxy/socks/client.go +++ b/proxy/socks/client.go @@ -86,7 +86,7 @@ func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy. } if err := conn.SetDeadline(time.Now().Add(p.Timeouts.Handshake)); err != nil { - newError("failed to set deadline for handshake").Base(err).WriteToLog() + newError("failed to set deadline for handshake").Base(err).WithContext(ctx).WriteToLog() } udpRequest, err := ClientHandshake(request, conn, conn) if err != nil { @@ -94,7 +94,7 @@ func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy. } if err := conn.SetDeadline(time.Time{}); err != nil { - newError("failed to clear deadline after handshake").Base(err).WriteToLog() + newError("failed to clear deadline after handshake").Base(err).WithContext(ctx).WriteToLog() } ctx, cancel := context.WithCancel(ctx) diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 5b0524edd..181e47449 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -91,7 +91,7 @@ func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispa if request.Command == protocol.RequestCommandTCP { dest := request.Destination() - newError("TCP Connect request to ", dest).WriteToLog() + newError("TCP Connect request to ", dest).WithContext(ctx).WriteToLog() if source, ok := proxy.SourceFromContext(ctx); ok { log.Record(&log.AccessMessage{ From: source, @@ -163,7 +163,7 @@ func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, udpServer := udp.NewDispatcher(dispatcher) if source, ok := proxy.SourceFromContext(ctx); ok { - newError("client UDP connection from ", source).WriteToLog() + newError("client UDP connection from ", source).WithContext(ctx).WriteToLog() } reader := buf.NewReader(conn) @@ -177,7 +177,7 @@ func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, request, data, err := DecodeUDPPacket(payload.Bytes()) if err != nil { - newError("failed to parse UDP request").Base(err).WriteToLog() + newError("failed to parse UDP request").Base(err).WithContext(ctx).WriteToLog() continue } @@ -185,7 +185,7 @@ func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, continue } - newError("send packet to ", request.Destination(), " with ", len(data), " bytes").AtDebug().WriteToLog() + newError("send packet to ", request.Destination(), " with ", len(data), " bytes").AtDebug().WithContext(ctx).WriteToLog() if source, ok := proxy.SourceFromContext(ctx); ok { log.Record(&log.AccessMessage{ From: source, @@ -200,12 +200,12 @@ func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, udpServer.Dispatch(ctx, request.Destination(), dataBuf, func(payload *buf.Buffer) { defer payload.Release() - newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WriteToLog() + newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WithContext(ctx).WriteToLog() udpMessage, err := EncodeUDPPacket(request, payload.Bytes()) defer udpMessage.Release() if err != nil { - newError("failed to write UDP response").AtWarning().Base(err).WriteToLog() + newError("failed to write UDP response").AtWarning().Base(err).WithContext(ctx).WriteToLog() } conn.Write(udpMessage.Bytes()) diff --git a/proxy/vmess/encoding/client.go b/proxy/vmess/encoding/client.go index cc930dc51..5b47eb59c 100644 --- a/proxy/vmess/encoding/client.go +++ b/proxy/vmess/encoding/client.go @@ -63,8 +63,7 @@ func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)() account, err := header.User.GetTypedAccount() if err != nil { - newError("failed to get user account: ", err).AtError().WriteToLog() - return nil + return newError("failed to get user account: ", err).AtError() } idHash := c.idHash(account.(*vmess.InternalAccount).AnyValidID().Bytes()) common.Must2(idHash.Write(timestamp.Bytes(nil))) @@ -200,8 +199,7 @@ func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon defer buffer.Release() if err := buffer.AppendSupplier(buf.ReadFullFrom(c.responseReader, 4)); err != nil { - newError("failed to read response header").Base(err).WriteToLog() - return nil, err + return nil, newError("failed to read response header").Base(err) } if buffer.Byte(0) != c.responseHeader { @@ -217,8 +215,7 @@ func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon dataLen := int(buffer.Byte(3)) if err := buffer.Reset(buf.ReadFullFrom(c.responseReader, dataLen)); err != nil { - newError("failed to read response command").Base(err).WriteToLog() - return nil, err + return nil, newError("failed to read response command").Base(err) } command, err := UnmarshalCommand(cmdID, buffer.Bytes()) if err == nil { diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 792bd3438..4fb3e45b6 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -242,10 +242,10 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i Reason: "", }) - newError("received request for ", request.Destination()).WriteToLog() + newError("received request for ", request.Destination()).WithContext(ctx).WriteToLog() if err := connection.SetReadDeadline(time.Time{}); err != nil { - newError("unable to set back read deadline").Base(err).WriteToLog() + newError("unable to set back read deadline").Base(err).WithContext(ctx).WriteToLog() } sessionPolicy = h.policyManager.ForLevel(request.User.Level) @@ -292,7 +292,7 @@ func (h *Handler) generateCommand(ctx context.Context, request *protocol.Request if h.inboundHandlerManager != nil { handler, err := h.inboundHandlerManager.GetHandler(ctx, tag) if err != nil { - newError("failed to get detour handler: ", tag).Base(err).AtWarning().WriteToLog() + newError("failed to get detour handler: ", tag).Base(err).AtWarning().WithContext(ctx).WriteToLog() return nil } proxyHandler, port, availableMin := handler.GetRandomInboundProxy() @@ -302,7 +302,7 @@ func (h *Handler) generateCommand(ctx context.Context, request *protocol.Request availableMin = 255 } - newError("pick detour handler for port ", port, " for ", availableMin, " minutes.").AtDebug().WriteToLog() + newError("pick detour handler for port ", port, " for ", availableMin, " minutes.").AtDebug().WithContext(ctx).WriteToLog() user := inboundHandler.GetUser(request.User.Email) if user == nil { return nil diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index e4cb0faa2..e189f25e5 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -65,7 +65,7 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial if !ok { return newError("target not specified").AtError() } - newError("tunneling request to ", target, " via ", rec.Destination()).WriteToLog() + newError("tunneling request to ", target, " via ", rec.Destination()).WithContext(ctx).WriteToLog() command := protocol.RequestCommandTCP if target.Network == net.Network_UDP { diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index b31b431ea..1b5f544d2 100644 --- a/transport/internet/tcp/dialer.go +++ b/transport/internet/tcp/dialer.go @@ -19,7 +19,7 @@ func getTCPSettingsFromContext(ctx context.Context) *Config { // Dial dials a new TCP connection to the given destination. func Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) { - newError("dialing TCP to ", dest).WriteToLog() + newError("dialing TCP to ", dest).WithContext(ctx).WriteToLog() src := internet.DialerSourceFromContext(ctx) conn, err := internet.DialSystem(ctx, src, dest) diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index 05c7e4eb3..4f535a08b 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -29,7 +29,7 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, handler if err != nil { return nil, err } - newError("listening TCP on ", address, ":", port).WriteToLog() + newError("listening TCP on ", address, ":", port).WithContext(ctx).WriteToLog() networkSettings := internet.TransportSettingsFromContext(ctx) tcpSettings := networkSettings.(*Config) diff --git a/transport/internet/udp/dispatcher.go b/transport/internet/udp/dispatcher.go index 0db6ae54c..5fd3bf824 100644 --- a/transport/internet/udp/dispatcher.go +++ b/transport/internet/udp/dispatcher.go @@ -72,13 +72,13 @@ func (v *Dispatcher) getInboundRay(dest net.Destination, callback ResponseCallba func (v *Dispatcher) Dispatch(ctx context.Context, destination net.Destination, payload *buf.Buffer, callback ResponseCallback) { // TODO: Add user to destString - newError("dispatch request to: ", destination).AtDebug().WriteToLog() + newError("dispatch request to: ", destination).AtDebug().WithContext(ctx).WriteToLog() conn := v.getInboundRay(destination, callback) outputStream := conn.inbound.InboundInput() if outputStream != nil { if err := outputStream.WriteMultiBuffer(buf.NewMultiBufferValue(payload)); err != nil { - newError("failed to write first UDP payload").Base(err).WriteToLog() + newError("failed to write first UDP payload").Base(err).WithContext(ctx).WriteToLog() conn.cancel() return } @@ -98,7 +98,7 @@ func handleInput(ctx context.Context, conn *connEntry, callback ResponseCallback mb, err := input.ReadMultiBuffer() if err != nil { - newError("failed to handle UDP input").Base(err).WriteToLog() + newError("failed to handle UDP input").Base(err).WithContext(ctx).WriteToLog() conn.cancel() return } diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index fa9718123..9d6cf9bf9 100644 --- a/transport/internet/websocket/dialer.go +++ b/transport/internet/websocket/dialer.go @@ -13,7 +13,7 @@ import ( // Dial dials a WebSocket connection to the given destination. func Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) { - newError("creating connection to ", dest).WriteToLog() + newError("creating connection to ", dest).WithContext(ctx).WriteToLog() conn, err := dialWebsocket(ctx, dest) if err != nil { From bc1979400e28fd31974fb62c595938deda5d638d Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 22 Feb 2018 15:27:51 +0100 Subject: [PATCH 052/183] simplify log --- common/log/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/log/log.go b/common/log/log.go index c7cdfadf8..e739a1f0c 100644 --- a/common/log/log.go +++ b/common/log/log.go @@ -24,7 +24,7 @@ type GeneralMessage struct { // String implements Message. func (m *GeneralMessage) String() string { - return serial.Concat("[", m.Severity, "]: ", m.Content) + return serial.Concat("[", m.Severity, "] ", m.Content) } // Record writes a message into log stream. From df34931ab31f1c35f6f4bf91a4e9c63dcc32dab9 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 22 Feb 2018 15:28:21 +0100 Subject: [PATCH 053/183] fix log test --- common/log/log_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/log/log_test.go b/common/log/log_test.go index f4ee096c8..3555e5cc5 100644 --- a/common/log/log_test.go +++ b/common/log/log_test.go @@ -28,5 +28,5 @@ func TestLogRecord(t *testing.T) { Content: net.ParseAddress(ip), }) - assert(logger.value, Equals, "[Error]: "+ip) + assert(logger.value, Equals, "[Error] "+ip) } From 0a3b3d0b6da4a9fbca6e8eac346131e57a831632 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 22 Feb 2018 17:29:21 +0100 Subject: [PATCH 054/183] refactor UDPNameServer clean up task --- app/dns/nameserver.go | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index a689e785c..f3e19e040 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -10,14 +10,10 @@ import ( "v2ray.com/core/common/buf" "v2ray.com/core/common/dice" "v2ray.com/core/common/net" + "v2ray.com/core/common/signal" "v2ray.com/core/transport/internet/udp" ) -const ( - CleanupInterval = time.Second * 120 - CleanupThreshold = 512 -) - var ( multiQuestionDNS = map[net.Address]bool{ net.IPAddress([]byte{8, 8, 8, 8}): true, @@ -42,10 +38,10 @@ type PendingRequest struct { type UDPNameServer struct { sync.Mutex - address net.Destination - requests map[uint16]*PendingRequest - udpServer *udp.Dispatcher - nextCleanup time.Time + address net.Destination + requests map[uint16]*PendingRequest + udpServer *udp.Dispatcher + cleanup *signal.PeriodicTask } func NewUDPNameServer(address net.Destination, dispatcher core.Dispatcher) *UDPNameServer { @@ -54,36 +50,35 @@ func NewUDPNameServer(address net.Destination, dispatcher core.Dispatcher) *UDPN requests: make(map[uint16]*PendingRequest), udpServer: udp.NewDispatcher(dispatcher), } + s.cleanup = &signal.PeriodicTask{ + Interval: time.Minute, + Execute: s.Cleanup, + } + s.cleanup.Start() return s } -func (s *UDPNameServer) Cleanup() { - expiredRequests := make([]uint16, 0, 16) +func (s *UDPNameServer) Cleanup() error { now := time.Now() s.Lock() for id, r := range s.requests { if r.expire.Before(now) { - expiredRequests = append(expiredRequests, id) close(r.response) + delete(s.requests, id) } } - for _, id := range expiredRequests { - delete(s.requests, id) - } s.Unlock() + return nil } func (s *UDPNameServer) AssignUnusedID(response chan<- *ARecord) uint16 { var id uint16 s.Lock() - if len(s.requests) > CleanupThreshold && s.nextCleanup.Before(time.Now()) { - s.nextCleanup = time.Now().Add(CleanupInterval) - go s.Cleanup() - } for { id = dice.RollUint16() if _, found := s.requests[id]; found { + time.Sleep(time.Millisecond * 500) continue } newError("add pending request id ", id).AtDebug().WriteToLog() From 3c1a7ad7378975f04f5d563f602c74d1d1fa4d95 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 22 Feb 2018 21:12:10 +0100 Subject: [PATCH 055/183] Update version --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index c0c4c364a..4c6d09853 100644 --- a/core.go +++ b/core.go @@ -16,7 +16,7 @@ import ( ) var ( - version = "3.10" + version = "3.11" build = "Custom" codename = "die Commanderin" intro = "An unified platform for anti-censorship." From 546c2fb226272a4510783a421d46014cd265f521 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 23 Feb 2018 12:13:02 +0100 Subject: [PATCH 056/183] settings for forcing secure encryption in vmess server --- common/protocol/headers.go | 23 +++-------- proxy/vmess/account.go | 4 +- proxy/vmess/encoding/client.go | 38 +++++++----------- proxy/vmess/encoding/server.go | 67 +++++++++++++++++--------------- proxy/vmess/inbound/config.pb.go | 56 +++++++++++++++----------- proxy/vmess/inbound/config.proto | 3 +- proxy/vmess/inbound/inbound.go | 16 ++++++++ proxy/vmess/outbound/outbound.go | 2 +- 8 files changed, 109 insertions(+), 100 deletions(-) diff --git a/common/protocol/headers.go b/common/protocol/headers.go index b8fc58853..f3c6e1538 100644 --- a/common/protocol/headers.go +++ b/common/protocol/headers.go @@ -38,24 +38,11 @@ const ( RequestOptionChunkMasking bitmask.Byte = 0x04 ) -type Security byte - -func (s Security) Is(t SecurityType) bool { - return s == Security(t) -} - -func NormSecurity(s Security) Security { - if s.Is(SecurityType_UNKNOWN) { - return Security(SecurityType_LEGACY) - } - return s -} - type RequestHeader struct { Version byte Command RequestCommand Option bitmask.Byte - Security Security + Security SecurityType Port net.Port Address net.Address User *User @@ -88,14 +75,14 @@ type CommandSwitchAccount struct { ValidMin byte } -func (sc *SecurityConfig) AsSecurity() Security { +func (sc *SecurityConfig) GetSecurityType() SecurityType { if sc == nil || sc.Type == SecurityType_AUTO { if runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x" { - return Security(SecurityType_AES128_GCM) + return SecurityType_AES128_GCM } - return Security(SecurityType_CHACHA20_POLY1305) + return SecurityType_CHACHA20_POLY1305 } - return NormSecurity(Security(sc.Type)) + return sc.Type } func IsDomainTooLong(domain string) bool { diff --git a/proxy/vmess/account.go b/proxy/vmess/account.go index 57cc1c5a3..1efc7edaf 100644 --- a/proxy/vmess/account.go +++ b/proxy/vmess/account.go @@ -9,7 +9,7 @@ import ( type InternalAccount struct { ID *protocol.ID AlterIDs []*protocol.ID - Security protocol.Security + Security protocol.SecurityType } func (a *InternalAccount) AnyValidID() *protocol.ID { @@ -37,6 +37,6 @@ func (a *Account) AsAccount() (protocol.Account, error) { return &InternalAccount{ ID: protoID, AlterIDs: protocol.NewAlterIDs(protoID, uint16(a.AlterId)), - Security: a.SecuritySettings.AsSecurity(), + Security: a.SecuritySettings.GetSecurityType(), }, nil } diff --git a/proxy/vmess/encoding/client.go b/proxy/vmess/encoding/client.go index 5b47eb59c..6fe187418 100644 --- a/proxy/vmess/encoding/client.go +++ b/proxy/vmess/encoding/client.go @@ -128,7 +128,8 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write if request.Option.Has(protocol.RequestOptionChunkMasking) { sizeParser = NewShakeSizeParser(c.requestBodyIV) } - if request.Security.Is(protocol.SecurityType_NONE) { + switch request.Security { + case protocol.SecurityType_NONE: if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamWriter(sizeParser, writer) @@ -142,9 +143,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write } return buf.NewWriter(writer) - } - - if request.Security.Is(protocol.SecurityType_LEGACY) { + case protocol.SecurityType_LEGACY: aesStream := crypto.NewAesEncryptionStream(c.requestBodyKey, c.requestBodyIV) cryptionWriter := crypto.NewCryptionWriter(aesStream, writer) if request.Option.Has(protocol.RequestOptionChunkStream) { @@ -157,9 +156,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write } return buf.NewWriter(cryptionWriter) - } - - if request.Security.Is(protocol.SecurityType_AES128_GCM) { + case protocol.SecurityType_AES128_GCM: block, _ := aes.NewCipher(c.requestBodyKey) aead, _ := cipher.NewGCM(block) @@ -172,9 +169,7 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType()) - } - - if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) { + case protocol.SecurityType_CHACHA20_POLY1305: aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.requestBodyKey)) auth := &crypto.AEADAuthenticator{ @@ -186,9 +181,9 @@ func (c *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, write AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType()) + default: + panic("Unknown security type.") } - - panic("Unknown security type.") } func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.ResponseHeader, error) { @@ -231,7 +226,8 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read if request.Option.Has(protocol.RequestOptionChunkMasking) { sizeParser = NewShakeSizeParser(c.responseBodyIV) } - if request.Security.Is(protocol.SecurityType_NONE) { + switch request.Security { + case protocol.SecurityType_NONE: if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamReader(sizeParser, reader) @@ -247,9 +243,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read } return buf.NewReader(reader) - } - - if request.Security.Is(protocol.SecurityType_LEGACY) { + case protocol.SecurityType_LEGACY: if request.Option.Has(protocol.RequestOptionChunkStream) { auth := &crypto.AEADAuthenticator{ AEAD: new(FnvAuthenticator), @@ -260,9 +254,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read } return buf.NewReader(c.responseReader) - } - - if request.Security.Is(protocol.SecurityType_AES128_GCM) { + case protocol.SecurityType_AES128_GCM: block, _ := aes.NewCipher(c.responseBodyKey) aead, _ := cipher.NewGCM(block) @@ -275,9 +267,7 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, } return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType()) - } - - if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) { + case protocol.SecurityType_CHACHA20_POLY1305: aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(c.responseBodyKey)) auth := &crypto.AEADAuthenticator{ @@ -289,9 +279,9 @@ func (c *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, read AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, } return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType()) + default: + panic("Unknown security type.") } - - panic("Unknown security type.") } type ChunkNonceGenerator struct { diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index 2c52287ca..10d0e0a38 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -143,6 +143,13 @@ func readAddress(buffer *buf.Buffer, reader io.Reader) (net.Address, net.Port, e return address, port, nil } +func parseSecurityType(b byte) protocol.SecurityType { + if _, f := protocol.SecurityType_name[int32(b)]; f { + return protocol.SecurityType(b) + } + return protocol.SecurityType_UNKNOWN +} + func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.RequestHeader, error) { buffer := buf.New() defer buffer.Release() @@ -191,11 +198,24 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request s.responseHeader = buffer.Byte(33) // 1 byte request.Option = bitmask.Byte(buffer.Byte(34)) // 1 byte padingLen := int(buffer.Byte(35) >> 4) - request.Security = protocol.NormSecurity(protocol.Security(buffer.Byte(35) & 0x0F)) + request.Security = parseSecurityType(buffer.Byte(35) & 0x0F) // 1 bytes reserved request.Command = protocol.RequestCommand(buffer.Byte(37)) var invalidRequestErr error + defer func() { + if invalidRequestErr != nil { + randomLen := dice.Roll(64) + 1 + // Read random number of bytes for prevent detection. + buffer.AppendSupplier(buf.ReadFullFrom(decryptor, randomLen)) + } + }() + + if request.Security == protocol.SecurityType_UNKNOWN || request.Security == protocol.SecurityType_AUTO { + invalidRequestErr = newError("unknown security type") + return nil, invalidRequestErr + } + switch request.Command { case protocol.RequestCommandMux: request.Address = net.DomainAddress("v1.mux.cool") @@ -206,15 +226,10 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request request.Port = port } else { invalidRequestErr = newError("invalid address").Base(err) + return nil, invalidRequestErr } default: invalidRequestErr = newError("invalid request command: ", request.Command) - } - - if invalidRequestErr != nil { - randomLen := dice.Roll(32) + 1 - // Read random number of bytes for prevent detection. - buffer.AppendSupplier(buf.ReadFullFrom(decryptor, randomLen)) return nil, invalidRequestErr } @@ -249,7 +264,8 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade if request.Option.Has(protocol.RequestOptionChunkMasking) { sizeParser = NewShakeSizeParser(s.requestBodyIV) } - if request.Security.Is(protocol.SecurityType_NONE) { + switch request.Security { + case protocol.SecurityType_NONE: if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamReader(sizeParser, reader) @@ -264,9 +280,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade } return buf.NewReader(reader) - } - - if request.Security.Is(protocol.SecurityType_LEGACY) { + case protocol.SecurityType_LEGACY: aesStream := crypto.NewAesDecryptionStream(s.requestBodyKey, s.requestBodyIV) cryptionReader := crypto.NewCryptionReader(aesStream, reader) if request.Option.Has(protocol.RequestOptionChunkStream) { @@ -279,9 +293,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade } return buf.NewReader(cryptionReader) - } - - if request.Security.Is(protocol.SecurityType_AES128_GCM) { + case protocol.SecurityType_AES128_GCM: block, _ := aes.NewCipher(s.requestBodyKey) aead, _ := cipher.NewGCM(block) @@ -294,9 +306,7 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, } return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType()) - } - - if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) { + case protocol.SecurityType_CHACHA20_POLY1305: aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.requestBodyKey)) auth := &crypto.AEADAuthenticator{ @@ -308,9 +318,9 @@ func (s *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reade AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, } return crypto.NewAuthenticationReader(auth, sizeParser, reader, request.Command.TransferType()) + default: + panic("Unknown security type.") } - - panic("Unknown security type.") } func (s *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) { @@ -335,7 +345,8 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ if request.Option.Has(protocol.RequestOptionChunkMasking) { sizeParser = NewShakeSizeParser(s.responseBodyIV) } - if request.Security.Is(protocol.SecurityType_NONE) { + switch request.Security { + case protocol.SecurityType_NONE: if request.Option.Has(protocol.RequestOptionChunkStream) { if request.Command.TransferType() == protocol.TransferTypeStream { return crypto.NewChunkStreamWriter(sizeParser, writer) @@ -350,9 +361,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ } return buf.NewWriter(writer) - } - - if request.Security.Is(protocol.SecurityType_LEGACY) { + case protocol.SecurityType_LEGACY: if request.Option.Has(protocol.RequestOptionChunkStream) { auth := &crypto.AEADAuthenticator{ AEAD: new(FnvAuthenticator), @@ -363,9 +372,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ } return buf.NewWriter(s.responseWriter) - } - - if request.Security.Is(protocol.SecurityType_AES128_GCM) { + case protocol.SecurityType_AES128_GCM: block, _ := aes.NewCipher(s.responseBodyKey) aead, _ := cipher.NewGCM(block) @@ -378,9 +385,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType()) - } - - if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) { + case protocol.SecurityType_CHACHA20_POLY1305: aead, _ := chacha20poly1305.New(GenerateChacha20Poly1305Key(s.responseBodyKey)) auth := &crypto.AEADAuthenticator{ @@ -392,7 +397,7 @@ func (s *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writ AdditionalDataGenerator: crypto.NoOpBytesGenerator{}, } return crypto.NewAuthenticationWriter(auth, sizeParser, writer, request.Command.TransferType()) + default: + panic("Unknown security type.") } - - panic("Unknown security type.") } diff --git a/proxy/vmess/inbound/config.pb.go b/proxy/vmess/inbound/config.pb.go index e89c65eda..467653d97 100644 --- a/proxy/vmess/inbound/config.pb.go +++ b/proxy/vmess/inbound/config.pb.go @@ -57,9 +57,10 @@ func (m *DefaultConfig) GetLevel() uint32 { } type Config struct { - User []*v2ray_core_common_protocol.User `protobuf:"bytes,1,rep,name=user" json:"user,omitempty"` - Default *DefaultConfig `protobuf:"bytes,2,opt,name=default" json:"default,omitempty"` - Detour *DetourConfig `protobuf:"bytes,3,opt,name=detour" json:"detour,omitempty"` + User []*v2ray_core_common_protocol.User `protobuf:"bytes,1,rep,name=user" json:"user,omitempty"` + Default *DefaultConfig `protobuf:"bytes,2,opt,name=default" json:"default,omitempty"` + Detour *DetourConfig `protobuf:"bytes,3,opt,name=detour" json:"detour,omitempty"` + SecureEncryptionOnly bool `protobuf:"varint,4,opt,name=secure_encryption_only,json=secureEncryptionOnly" json:"secure_encryption_only,omitempty"` } func (m *Config) Reset() { *m = Config{} } @@ -88,6 +89,13 @@ func (m *Config) GetDetour() *DetourConfig { return nil } +func (m *Config) GetSecureEncryptionOnly() bool { + if m != nil { + return m.SecureEncryptionOnly + } + return false +} + func init() { proto.RegisterType((*DetourConfig)(nil), "v2ray.core.proxy.vmess.inbound.DetourConfig") proto.RegisterType((*DefaultConfig)(nil), "v2ray.core.proxy.vmess.inbound.DefaultConfig") @@ -97,24 +105,26 @@ func init() { func init() { proto.RegisterFile("v2ray.com/core/proxy/vmess/inbound/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 297 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0xcf, 0x4e, 0x83, 0x40, - 0x10, 0xc6, 0x03, 0xd5, 0xa2, 0x5b, 0xeb, 0x81, 0x78, 0x40, 0x0f, 0x84, 0x70, 0xaa, 0x89, 0xce, - 0x26, 0xe8, 0x03, 0x18, 0x4b, 0x62, 0x7a, 0x23, 0x9b, 0xd8, 0x83, 0x17, 0x43, 0x97, 0xad, 0x21, - 0x01, 0xa6, 0x59, 0x16, 0x22, 0xaf, 0xe4, 0xbb, 0xf8, 0x4e, 0xa6, 0x03, 0xc4, 0x3f, 0x07, 0x7b, - 0xdb, 0xd9, 0xfc, 0xbe, 0x6f, 0xbe, 0x6f, 0x18, 0x6f, 0x23, 0x9d, 0x76, 0x20, 0xb1, 0xe4, 0x12, - 0xb5, 0xe2, 0x3b, 0x8d, 0xef, 0x1d, 0x6f, 0x4b, 0x55, 0xd7, 0x3c, 0xaf, 0x36, 0xd8, 0x54, 0x19, - 0x97, 0x58, 0x6d, 0xf3, 0x37, 0xd8, 0x69, 0x34, 0xe8, 0xfa, 0xa3, 0x40, 0x2b, 0x20, 0x18, 0x08, - 0x86, 0x01, 0xbe, 0xba, 0xfe, 0x63, 0x28, 0xb1, 0x2c, 0xb1, 0xe2, 0x24, 0x96, 0x58, 0xf0, 0xa6, - 0x56, 0xba, 0xb7, 0x0a, 0x7d, 0x76, 0x16, 0x2b, 0x83, 0x8d, 0x5e, 0xd2, 0x02, 0xf7, 0x9c, 0xd9, - 0x06, 0x3d, 0x2b, 0xb0, 0x16, 0xa7, 0xc2, 0x36, 0x18, 0x3e, 0xb0, 0x79, 0xac, 0xb6, 0x69, 0x53, - 0x98, 0x01, 0xb8, 0x64, 0x27, 0x69, 0x61, 0x94, 0x7e, 0xcd, 0x33, 0xc2, 0xe6, 0xc2, 0xa1, 0x79, - 0x95, 0xb9, 0x17, 0xec, 0xb8, 0x50, 0xad, 0x2a, 0x3c, 0x9b, 0xfe, 0xfb, 0x21, 0xfc, 0xb4, 0xd8, - 0x74, 0xd0, 0xde, 0xb3, 0xa3, 0xfd, 0x6a, 0xcf, 0x0a, 0x26, 0x8b, 0x59, 0x14, 0xc0, 0x8f, 0x1a, - 0x7d, 0x44, 0x18, 0x23, 0xc2, 0x73, 0xad, 0xb4, 0x20, 0xda, 0x7d, 0x62, 0x4e, 0xd6, 0x47, 0x20, - 0xe3, 0x59, 0x74, 0x0b, 0xff, 0xf7, 0x87, 0x5f, 0x89, 0xc5, 0xa8, 0x76, 0x63, 0x36, 0xcd, 0xa8, - 0xab, 0x37, 0x21, 0x9f, 0x9b, 0xc3, 0x3e, 0xdf, 0x97, 0x11, 0x83, 0xf6, 0x31, 0x61, 0xa1, 0xc4, - 0xf2, 0x80, 0x34, 0xb1, 0x5e, 0x9c, 0xe1, 0xf9, 0x61, 0xfb, 0xeb, 0x48, 0xa4, 0x1d, 0x2c, 0xf7, - 0x6c, 0x42, 0xec, 0x9a, 0xd8, 0x55, 0x0f, 0x6c, 0xa6, 0xd4, 0xfa, 0xee, 0x2b, 0x00, 0x00, 0xff, - 0xff, 0x8d, 0x37, 0x9e, 0x90, 0x08, 0x02, 0x00, 0x00, + // 333 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x90, 0x4f, 0x4f, 0xf2, 0x40, + 0x10, 0xc6, 0xd3, 0xc2, 0x0b, 0xbc, 0x8b, 0x78, 0x68, 0x88, 0xa9, 0x1e, 0x48, 0xd3, 0x13, 0x26, + 0xba, 0x9b, 0x54, 0x3e, 0x80, 0x11, 0x8c, 0xe1, 0x24, 0x69, 0x22, 0x07, 0x2f, 0xa4, 0x6c, 0x07, + 0xd3, 0x64, 0xbb, 0x43, 0xb6, 0x5b, 0x62, 0xcf, 0x7e, 0x1b, 0x3f, 0xa5, 0x61, 0x5a, 0xfc, 0x77, + 0x90, 0xdb, 0xce, 0xce, 0xef, 0x79, 0xe6, 0x99, 0x61, 0x62, 0x17, 0x99, 0xa4, 0xe2, 0x12, 0x73, + 0x21, 0xd1, 0x80, 0xd8, 0x1a, 0x7c, 0xad, 0xc4, 0x2e, 0x87, 0xa2, 0x10, 0x99, 0x5e, 0x63, 0xa9, + 0x53, 0x21, 0x51, 0x6f, 0xb2, 0x17, 0xbe, 0x35, 0x68, 0xd1, 0x1b, 0x1d, 0x04, 0x06, 0x38, 0xc1, + 0x9c, 0x60, 0xde, 0xc0, 0x17, 0x97, 0xbf, 0x0c, 0x25, 0xe6, 0x39, 0x6a, 0x41, 0x62, 0x89, 0x4a, + 0x94, 0x05, 0x98, 0xda, 0x2a, 0x1c, 0xb1, 0x93, 0x19, 0x58, 0x2c, 0xcd, 0x94, 0x06, 0x78, 0xa7, + 0xcc, 0xb5, 0xe8, 0x3b, 0x81, 0x33, 0xfe, 0x1f, 0xbb, 0x16, 0xc3, 0x5b, 0x36, 0x98, 0xc1, 0x26, + 0x29, 0x95, 0x6d, 0x80, 0x73, 0xd6, 0x4b, 0x94, 0x05, 0xb3, 0xca, 0x52, 0xc2, 0x06, 0x71, 0x97, + 0xea, 0x79, 0xea, 0x0d, 0xd9, 0x3f, 0x05, 0x3b, 0x50, 0xbe, 0x4b, 0xff, 0x75, 0x11, 0xbe, 0xb9, + 0xac, 0xd3, 0x68, 0x27, 0xac, 0xbd, 0x1f, 0xed, 0x3b, 0x41, 0x6b, 0xdc, 0x8f, 0x02, 0xfe, 0x6d, + 0x8d, 0x3a, 0x22, 0x3f, 0x44, 0xe4, 0x4f, 0x05, 0x98, 0x98, 0x68, 0xef, 0x81, 0x75, 0xd3, 0x3a, + 0x02, 0x19, 0xf7, 0xa3, 0x6b, 0xfe, 0xf7, 0xfe, 0xfc, 0x47, 0xe2, 0xf8, 0xa0, 0xf6, 0x66, 0xac, + 0x93, 0xd2, 0xae, 0x7e, 0x8b, 0x7c, 0xae, 0x8e, 0xfb, 0x7c, 0x5d, 0x26, 0x6e, 0xb4, 0xde, 0x84, + 0x9d, 0x15, 0x20, 0x4b, 0x03, 0x2b, 0xd0, 0xd2, 0x54, 0x5b, 0x9b, 0xa1, 0x5e, 0xa1, 0x56, 0x95, + 0xdf, 0x0e, 0x9c, 0x71, 0x2f, 0x1e, 0xd6, 0xdd, 0xfb, 0xcf, 0xe6, 0xa3, 0x56, 0xd5, 0xdd, 0x82, + 0x85, 0x12, 0xf3, 0x23, 0x03, 0x17, 0xce, 0x73, 0xb7, 0x79, 0xbe, 0xbb, 0xa3, 0x65, 0x14, 0x27, + 0x15, 0x9f, 0xee, 0xd9, 0x05, 0xb1, 0x4b, 0x62, 0xe7, 0x35, 0xb0, 0xee, 0xd0, 0xad, 0x6e, 0x3e, + 0x02, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x49, 0x84, 0xe1, 0x3e, 0x02, 0x00, 0x00, } diff --git a/proxy/vmess/inbound/config.proto b/proxy/vmess/inbound/config.proto index fa69a2033..63caeff81 100644 --- a/proxy/vmess/inbound/config.proto +++ b/proxy/vmess/inbound/config.proto @@ -21,4 +21,5 @@ message Config { repeated v2ray.core.common.protocol.User user = 1; DefaultConfig default = 2; DetourConfig detour = 3; -} \ No newline at end of file + bool secure_encryption_only = 4; +} diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 4fb3e45b6..df85770e7 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -101,6 +101,7 @@ type Handler struct { usersByEmail *userByEmail detours *DetourConfig sessionHistory *encoding.SessionHistory + secure bool } // New creates a new VMess inbound handler. @@ -113,6 +114,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) { detours: config.Detour, usersByEmail: newUserByEmail(config.GetDefaultValue()), sessionHistory: encoding.NewSessionHistory(), + secure: config.SecureEncryptionOnly, } for _, user := range config.User { @@ -210,6 +212,10 @@ func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSess return nil } +func isInecureEncryption(s protocol.SecurityType) bool { + return s == protocol.SecurityType_NONE || s == protocol.SecurityType_LEGACY || s == protocol.SecurityType_UNKNOWN +} + // Process implements proxy.Inbound.Process(). func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher core.Dispatcher) error { sessionPolicy := h.policyManager.ForLevel(0) @@ -235,6 +241,16 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i return err } + if h.secure && isInecureEncryption(request.Security) { + log.Record(&log.AccessMessage{ + From: connection.RemoteAddr(), + To: "", + Status: log.AccessRejected, + Reason: "Insecure encryption", + }) + return newError("client is using insecure encryption: ", request.Security) + } + log.Record(&log.AccessMessage{ From: connection.RemoteAddr(), To: request.Destination(), diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index e189f25e5..a6b937c12 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -91,7 +91,7 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial account := rawAccount.(*vmess.InternalAccount) request.Security = account.Security - if request.Security.Is(protocol.SecurityType_AES128_GCM) || request.Security.Is(protocol.SecurityType_NONE) || request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) { + if request.Security == protocol.SecurityType_AES128_GCM || request.Security == protocol.SecurityType_NONE || request.Security == protocol.SecurityType_CHACHA20_POLY1305 { request.Option.Set(protocol.RequestOptionChunkMasking) } From 16c6a5f0784f56547ddfaf0b2a7cfe5b818eee63 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 23 Feb 2018 12:36:33 +0100 Subject: [PATCH 057/183] fix test break --- proxy/vmess/encoding/encoding_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proxy/vmess/encoding/encoding_test.go b/proxy/vmess/encoding/encoding_test.go index 2777f8cf3..d372189a3 100644 --- a/proxy/vmess/encoding/encoding_test.go +++ b/proxy/vmess/encoding/encoding_test.go @@ -34,7 +34,7 @@ func TestRequestSerialization(t *testing.T) { Command: protocol.RequestCommandTCP, Address: net.DomainAddress("www.v2ray.com"), Port: net.Port(443), - Security: protocol.Security(protocol.SecurityType_AES128_GCM), + Security: protocol.SecurityType_AES128_GCM, } buffer := buf.New() @@ -87,7 +87,7 @@ func TestInvalidRequest(t *testing.T) { Command: protocol.RequestCommand(100), Address: net.DomainAddress("www.v2ray.com"), Port: net.Port(443), - Security: protocol.Security(protocol.SecurityType_AES128_GCM), + Security: protocol.SecurityType_AES128_GCM, } buffer := buf.New() @@ -127,7 +127,7 @@ func TestMuxRequest(t *testing.T) { Version: 1, User: user, Command: protocol.RequestCommandMux, - Security: protocol.Security(protocol.SecurityType_AES128_GCM), + Security: protocol.SecurityType_AES128_GCM, } buffer := buf.New() From 768e30c37b8c997584cc822d8b6c9c963cfc73e1 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 23 Feb 2018 13:55:56 +0100 Subject: [PATCH 058/183] disable socks 4 when auth is required. --- proxy/socks/protocol.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index 356891369..8ad4235cc 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -49,6 +49,11 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol version := buffer.Byte(0) if version == socks4Version { + if s.config.AuthType == AuthType_PASSWORD { + writeSocks4Response(writer, socks4RequestRejected, net.AnyIP, net.Port(0)) + return nil, newError("socks 4 is not allowed when auth is required.") + } + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 6)); err != nil { return nil, newError("insufficient header").Base(err) } From 798212b311aa23526908af32c251440351a9c1ab Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 23 Feb 2018 17:07:45 +0100 Subject: [PATCH 059/183] fix timer settings --- proxy/http/server.go | 4 +++- proxy/shadowsocks/server.go | 7 ++++--- proxy/socks/server.go | 7 +++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/proxy/http/server.go b/proxy/http/server.go index 6668c03f8..48e0520f9 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -192,11 +192,13 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade }) responseDone := signal.ExecuteAsync(func() error { + defer timer.SetTimeout(s.policy().Timeouts.UplinkOnly) + v2writer := buf.NewWriter(conn) if err := buf.Copy(ray.InboundOutput(), v2writer, buf.UpdateActivity(timer)); err != nil { return err } - timer.SetTimeout(s.policy().Timeouts.UplinkOnly) + return nil }) diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 63e3f2a04..8aacd2eb3 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -171,6 +171,8 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, } responseDone := signal.ExecuteAsync(func() error { + defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) + bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) responseWriter, err := WriteTCPResponse(request, bufferedWriter) if err != nil { @@ -194,18 +196,17 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, return newError("failed to transport all TCP response").Base(err) } - timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) - return nil }) requestDone := signal.ExecuteAsync(func() error { + defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) defer ray.InboundInput().Close() if err := buf.Copy(bodyReader, ray.InboundInput(), buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP request").Base(err) } - timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) + return nil }) diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 181e47449..6f61df942 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -131,22 +131,25 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ output := ray.InboundOutput() requestDone := signal.ExecuteAsync(func() error { + defer timer.SetTimeout(v.policy().Timeouts.DownlinkOnly) defer input.Close() v2reader := buf.NewReader(reader) if err := buf.Copy(v2reader, input, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP request").Base(err) } - timer.SetTimeout(v.policy().Timeouts.DownlinkOnly) + return nil }) responseDone := signal.ExecuteAsync(func() error { + defer timer.SetTimeout(v.policy().Timeouts.UplinkOnly) + v2writer := buf.NewWriter(writer) if err := buf.Copy(output, v2writer, buf.UpdateActivity(timer)); err != nil { return newError("failed to transport all TCP response").Base(err) } - timer.SetTimeout(v.policy().Timeouts.UplinkOnly) + return nil }) From a059ee2c001436dc24bb8629587b2fe5cbb1efe1 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 23 Feb 2018 17:14:20 +0100 Subject: [PATCH 060/183] fix lint errors --- proxy/socks/client.go | 12 +++++++----- proxy/socks/server.go | 26 +++++++++++++++++--------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/proxy/socks/client.go b/proxy/socks/client.go index 8f450fe63..3958ae9de 100644 --- a/proxy/socks/client.go +++ b/proxy/socks/client.go @@ -49,7 +49,7 @@ func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy. var server *protocol.ServerSpec var conn internet.Connection - err := retry.ExponentialBackoff(5, 100).On(func() error { + if err := retry.ExponentialBackoff(5, 100).On(func() error { server = c.serverPicker.PickServer() dest := server.Destination() rawConn, err := dialer.Dial(ctx, dest) @@ -59,13 +59,15 @@ func (c *Client) Process(ctx context.Context, ray ray.OutboundRay, dialer proxy. conn = rawConn return nil - }) - - if err != nil { + }); err != nil { return newError("failed to find an available destination").Base(err) } - defer conn.Close() + defer func() { + if err := conn.Close(); err != nil { + newError("failed to closed connection").Base(err).WithContext(ctx).WriteToLog() + } + }() p := c.policyManager.ForLevel(0) diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 6f61df942..cae3208ef 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -41,6 +41,7 @@ func (s *Server) policy() core.Policy { return p } +// Network implements proxy.Inbound. func (s *Server) Network() net.NetworkList { list := net.NetworkList{ Network: []net.Network{net.Network_TCP}, @@ -51,6 +52,7 @@ func (s *Server) Network() net.NetworkList { return list } +// Process implements proxy.Inbound. func (s *Server) Process(ctx context.Context, network net.Network, conn internet.Connection, dispatcher core.Dispatcher) error { switch network { case net.Network_TCP: @@ -63,7 +65,10 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn internet } func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error { - conn.SetReadDeadline(time.Now().Add(s.policy().Timeouts.Handshake)) + if err := conn.SetReadDeadline(time.Now().Add(s.policy().Timeouts.Handshake)); err != nil { + newError("failed to set deadline").Base(err).WithContext(ctx).WriteToLog() + } + reader := buf.NewBufferedReader(buf.NewReader(conn)) inboundDest, ok := proxy.InboundEntryPointFromContext(ctx) @@ -87,7 +92,10 @@ func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispa } return newError("failed to read request").Base(err) } - conn.SetReadDeadline(time.Time{}) + + if err := conn.SetReadDeadline(time.Time{}); err != nil { + newError("failed to clear deadline").Base(err).WithContext(ctx).WriteToLog() + } if request.Command == protocol.RequestCommandTCP { dest := request.Destination() @@ -111,16 +119,16 @@ func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispa return nil } -func (*Server) handleUDP(c net.Conn) error { +func (*Server) handleUDP(c io.Reader) error { // The TCP connection closes after this method returns. We need to wait until // the client closes it. _, err := io.Copy(buf.DiscardBytes, c) return err } -func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher core.Dispatcher) error { +func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writer, dest net.Destination, dispatcher core.Dispatcher) error { ctx, cancel := context.WithCancel(ctx) - timer := signal.CancelAfterInactivity(ctx, cancel, v.policy().Timeouts.ConnectionIdle) + timer := signal.CancelAfterInactivity(ctx, cancel, s.policy().Timeouts.ConnectionIdle) ray, err := dispatcher.Dispatch(ctx, dest) if err != nil { @@ -131,8 +139,8 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ output := ray.InboundOutput() requestDone := signal.ExecuteAsync(func() error { - defer timer.SetTimeout(v.policy().Timeouts.DownlinkOnly) - defer input.Close() + defer timer.SetTimeout(s.policy().Timeouts.DownlinkOnly) + defer common.Must(input.Close()) v2reader := buf.NewReader(reader) if err := buf.Copy(v2reader, input, buf.UpdateActivity(timer)); err != nil { @@ -143,7 +151,7 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ }) responseDone := signal.ExecuteAsync(func() error { - defer timer.SetTimeout(v.policy().Timeouts.UplinkOnly) + defer timer.SetTimeout(s.policy().Timeouts.UplinkOnly) v2writer := buf.NewWriter(writer) if err := buf.Copy(output, v2writer, buf.UpdateActivity(timer)); err != nil { @@ -162,7 +170,7 @@ func (v *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ return nil } -func (v *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error { +func (s *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, dispatcher core.Dispatcher) error { udpServer := udp.NewDispatcher(dispatcher) if source, ok := proxy.SourceFromContext(ctx); ok { From af1abf687c33d3f81b62af9ceb513d801569b298 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 23 Feb 2018 23:42:01 +0100 Subject: [PATCH 061/183] unify all address reading and writing --- common/protocol/address.go | 183 +++++++++++++++++++++++++++++++ common/protocol/address_test.go | 70 ++++++++++++ proxy/shadowsocks/protocol.go | 68 +++++------- proxy/socks/protocol.go | 132 +++++----------------- proxy/socks/protocol_test.go | 54 --------- proxy/socks/server.go | 12 +- proxy/vmess/encoding/client.go | 20 +--- proxy/vmess/encoding/const.go | 5 - proxy/vmess/encoding/encoding.go | 16 +++ proxy/vmess/encoding/server.go | 40 +------ 10 files changed, 333 insertions(+), 267 deletions(-) create mode 100644 common/protocol/address.go create mode 100644 common/protocol/address_test.go delete mode 100644 proxy/vmess/encoding/const.go diff --git a/common/protocol/address.go b/common/protocol/address.go new file mode 100644 index 000000000..44c1abb9b --- /dev/null +++ b/common/protocol/address.go @@ -0,0 +1,183 @@ +package protocol + +import ( + "io" + + "v2ray.com/core/common/buf" + "v2ray.com/core/common/net" +) + +type AddressOption func(*AddressParser) + +func PortThenAddress() AddressOption { + return func(p *AddressParser) { + p.portFirst = true + } +} + +func AddressFamilyByte(b byte, f net.AddressFamily) AddressOption { + return func(p *AddressParser) { + p.addrTypeMap[b] = f + p.addrByteMap[f] = b + } +} + +type AddressTypeParser func(byte) byte + +func WithAddressTypeParser(atp AddressTypeParser) AddressOption { + return func(p *AddressParser) { + p.typeParser = atp + } +} + +type AddressParser struct { + addrTypeMap map[byte]net.AddressFamily + addrByteMap map[net.AddressFamily]byte + portFirst bool + typeParser AddressTypeParser +} + +func NewAddressParser(options ...AddressOption) *AddressParser { + p := &AddressParser{ + addrTypeMap: make(map[byte]net.AddressFamily, 8), + addrByteMap: make(map[net.AddressFamily]byte, 8), + } + for _, opt := range options { + opt(p) + } + return p +} + +func (p *AddressParser) readPort(b *buf.Buffer, reader io.Reader) (net.Port, error) { + if err := b.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { + return 0, err + } + return net.PortFromBytes(b.BytesFrom(-2)), nil +} + +func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Address, error) { + if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { + return nil, err + } + + addrType := b.Byte(b.Len() - 1) + if p.typeParser != nil { + addrType = p.typeParser(addrType) + } + + addrFamily, valid := p.addrTypeMap[addrType] + if !valid { + return nil, newError("unknown address type: ", addrType) + } + + switch addrFamily { + case net.AddressFamilyIPv4: + if err := b.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { + return nil, err + } + return net.IPAddress(b.BytesFrom(-4)), nil + case net.AddressFamilyIPv6: + if err := b.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil { + return nil, err + } + return net.IPAddress(b.BytesFrom(-16)), nil + case net.AddressFamilyDomain: + if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { + return nil, err + } + domainLength := int(b.Byte(b.Len() - 1)) + if err := b.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { + return nil, err + } + return net.DomainAddress(string(b.BytesFrom(-domainLength))), nil + default: + panic("impossible case") + } +} + +func (p *AddressParser) ReadAddressPort(buffer *buf.Buffer, input io.Reader) (net.Address, net.Port, error) { + if buffer == nil { + buffer = buf.New() + defer buffer.Release() + } + + if p.portFirst { + port, err := p.readPort(buffer, input) + if err != nil { + return nil, 0, err + } + addr, err := p.readAddress(buffer, input) + if err != nil { + return nil, 0, err + } + return addr, port, nil + } + + addr, err := p.readAddress(buffer, input) + if err != nil { + return nil, 0, err + } + + port, err := p.readPort(buffer, input) + if err != nil { + return nil, 0, err + } + + return addr, port, nil +} + +func (p *AddressParser) writePort(writer io.Writer, port net.Port) error { + if _, err := writer.Write(port.Bytes(nil)); err != nil { + return err + } + return nil +} + +func (p *AddressParser) writeAddress(writer io.Writer, address net.Address) error { + tb, valid := p.addrByteMap[address.Family()] + if !valid { + return newError("unknown address family", address.Family()) + } + + switch address.Family() { + case net.AddressFamilyIPv4, net.AddressFamilyIPv6: + if _, err := writer.Write([]byte{tb}); err != nil { + return err + } + if _, err := writer.Write(address.IP()); err != nil { + return err + } + case net.AddressFamilyDomain: + domain := address.Domain() + if IsDomainTooLong(domain) { + return newError("Super long domain is not supported: ", domain) + } + if _, err := writer.Write([]byte{tb, byte(len(domain))}); err != nil { + return err + } + if _, err := writer.Write([]byte(domain)); err != nil { + return err + } + } + return nil +} + +func (p *AddressParser) WriteAddressPort(writer io.Writer, addr net.Address, port net.Port) error { + if p.portFirst { + if err := p.writePort(writer, port); err != nil { + return err + } + if err := p.writeAddress(writer, addr); err != nil { + return err + } + return nil + } + + if err := p.writeAddress(writer, addr); err != nil { + return err + } + if err := p.writePort(writer, port); err != nil { + return err + } + return nil +} diff --git a/common/protocol/address_test.go b/common/protocol/address_test.go new file mode 100644 index 000000000..5e724d511 --- /dev/null +++ b/common/protocol/address_test.go @@ -0,0 +1,70 @@ +package protocol_test + +import ( + "bytes" + "testing" + + "v2ray.com/core/common/buf" + "v2ray.com/core/common/net" + . "v2ray.com/core/common/protocol" + . "v2ray.com/ext/assert" +) + +func TestAddressParser(t *testing.T) { + assert := With(t) + + data := []struct { + Options []AddressOption + Input []byte + Address net.Address + Port net.Port + Error bool + }{ + { + Options: []AddressOption{}, + Input: []byte{0, 0, 0, 0, 0}, + Error: true, + }, + { + Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)}, + Input: []byte{1, 0, 0, 0, 0, 0, 53}, + Address: net.IPAddress([]byte{0, 0, 0, 0}), + Port: net.Port(53), + }, + { + Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)}, + Input: []byte{1, 0, 0, 0, 0}, + Error: true, + }, + { + Options: []AddressOption{AddressFamilyByte(0x04, net.AddressFamilyIPv6)}, + Input: []byte{4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 80}, + Address: net.IPAddress([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}), + Port: net.Port(80), + }, + { + Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)}, + Input: []byte{3, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 80}, + Address: net.DomainAddress("v2ray.com"), + Port: net.Port(80), + }, + { + Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)}, + Input: []byte{3, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0}, + Error: true, + }, + } + + for _, tc := range data { + b := buf.New() + parser := NewAddressParser(tc.Options...) + addr, port, err := parser.ReadAddressPort(b, bytes.NewReader(tc.Input)) + b.Release() + if tc.Error { + assert(err, IsNotNil) + } else { + assert(addr, Equals, tc.Address) + assert(port, Equals, tc.Port) + } + } +} diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index 48fbd486a..fb3f22b91 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -11,16 +11,20 @@ import ( "v2ray.com/core/common/dice" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" - "v2ray.com/core/proxy/socks" ) const ( Version = 1 RequestOptionOneTimeAuth bitmask.Byte = 0x01 +) - AddrTypeIPv4 = 1 - AddrTypeIPv6 = 4 - AddrTypeDomain = 3 +var addrParser = protocol.NewAddressParser( + protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4), + protocol.AddressFamilyByte(0x04, net.AddressFamilyIPv6), + protocol.AddressFamilyByte(0x03, net.AddressFamilyDomain), + protocol.WithAddressTypeParser(func(b byte) byte { + return b & 0x0F + }), ) // ReadTCPSession reads a Shadowsocks TCP session from the given reader, returns its header and remaining parts. @@ -58,10 +62,21 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea Command: protocol.RequestCommandTCP, } - if err := buffer.Reset(buf.ReadFullFrom(br, 1)); err != nil { - return nil, nil, newError("failed to read address type").Base(err) + buffer.Clear() + + addr, port, err := addrParser.ReadAddressPort(buffer, br) + + if err != nil { + // Invalid address. Continue to read some bytes to confuse client. + nBytes := dice.Roll(32) + buffer.Clear() + buffer.AppendSupplier(buf.ReadFullFrom(br, nBytes)) + return nil, nil, newError("failed to read address").Base(err) } + request.Address = addr + request.Port = port + if !account.Cipher.IsAEAD() { if (buffer.Byte(0) & 0x10) == 0x10 { request.Option.Set(RequestOptionOneTimeAuth) @@ -76,20 +91,6 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea } } - addrType := (buffer.Byte(0) & 0x0F) - - addr, port, err := socks.ReadAddress(buffer, addrType, br) - if err != nil { - // Invalid address. Continue to read some bytes to confuse client. - nBytes := dice.Roll(32) - buffer.Clear() - buffer.AppendSupplier(buf.ReadFullFrom(br, nBytes)) - return nil, nil, newError("failed to read address").Base(err) - } - - request.Address = addr - request.Port = port - if request.Option.Has(RequestOptionOneTimeAuth) { actualAuth := make([]byte, AuthSize) authenticator.Authenticate(buffer.Bytes())(actualAuth) @@ -150,7 +151,7 @@ func WriteTCPRequest(request *protocol.RequestHeader, writer io.Writer) (buf.Wri header := buf.NewLocal(512) - if err := socks.AppendAddress(header, request.Address, request.Port); err != nil { + if err := addrParser.WriteAddressPort(header, request.Address, request.Port); err != nil { return nil, newError("failed to write address").Base(err) } @@ -230,7 +231,7 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff } iv := buffer.Bytes() - if err := socks.AppendAddress(buffer, request.Address, request.Port); err != nil { + if err := addrParser.WriteAddressPort(buffer, request.Address, request.Port); err != nil { return nil, newError("failed to write address").Base(err) } @@ -301,26 +302,15 @@ func DecodeUDPPacket(user *protocol.User, payload *buf.Buffer) (*protocol.Reques } } - addrType := (payload.Byte(0) & 0x0F) - payload.SliceFrom(1) + payload.SetByte(0, payload.Byte(0)&0x0F) - switch addrType { - case AddrTypeIPv4: - request.Address = net.IPAddress(payload.BytesTo(4)) - payload.SliceFrom(4) - case AddrTypeIPv6: - request.Address = net.IPAddress(payload.BytesTo(16)) - payload.SliceFrom(16) - case AddrTypeDomain: - domainLength := int(payload.Byte(0)) - request.Address = net.DomainAddress(string(payload.BytesRange(1, 1+domainLength))) - payload.SliceFrom(1 + domainLength) - default: - return nil, nil, newError("unknown address type: ", addrType).AtError() + addr, port, err := addrParser.ReadAddressPort(nil, payload) + if err != nil { + return nil, nil, newError("failed to parse address").Base(err) } - request.Port = net.PortFromBytes(payload.BytesTo(2)) - payload.SliceFrom(2) + request.Address = addr + request.Port = port return request, payload, nil } diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index 8ad4235cc..2090296a9 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -34,6 +34,12 @@ const ( statusCmdNotSupport = 0x07 ) +var addrParser = protocol.NewAddressParser( + protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4), + protocol.AddressFamilyByte(0x04, net.AddressFamilyIPv6), + protocol.AddressFamilyByte(0x03, net.AddressFamilyDomain), +) + type ServerSession struct { config *ServerConfig port net.Port @@ -122,7 +128,7 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol return nil, newError("failed to write auth response").Base(err) } } - if err := buffer.Reset(buf.ReadFullFrom(reader, 4)); err != nil { + if err := buffer.Reset(buf.ReadFullFrom(reader, 3)); err != nil { return nil, newError("failed to read request").Base(err) } @@ -139,13 +145,11 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol request.Command = protocol.RequestCommandUDP } - addrType := buffer.Byte(3) - buffer.Clear() request.Version = socks5Version - addr, port, err := ReadAddress(buffer, addrType, reader) + addr, port, err := addrParser.ReadAddressPort(buffer, reader) if err != nil { return nil, newError("failed to read address").Base(err) } @@ -229,30 +233,10 @@ func writeSocks5AuthenticationResponse(writer io.Writer, version byte, auth byte return err } -// AppendAddress appends Socks address into the given buffer. -func AppendAddress(buffer *buf.Buffer, address net.Address, port net.Port) error { - switch address.Family() { - case net.AddressFamilyIPv4: - buffer.AppendBytes(addrTypeIPv4) - buffer.Append(address.IP()) - case net.AddressFamilyIPv6: - buffer.AppendBytes(addrTypeIPv6) - buffer.Append(address.IP()) - case net.AddressFamilyDomain: - if protocol.IsDomainTooLong(address.Domain()) { - return newError("Super long domain is not supported in Socks protocol: ", address.Domain()) - } - buffer.AppendBytes(addrTypeDomain, byte(len(address.Domain()))) - common.Must(buffer.AppendSupplier(serial.WriteString(address.Domain()))) - } - common.Must(buffer.AppendSupplier(serial.WriteUint16(port.Value()))) - return nil -} - func writeSocks5Response(writer io.Writer, errCode byte, address net.Address, port net.Port) error { buffer := buf.NewLocal(64) buffer.AppendBytes(socks5Version, errCode, 0x00 /* reserved */) - if err := AppendAddress(buffer, address, port); err != nil { + if err := addrParser.WriteAddressPort(buffer, address, port); err != nil { return err } @@ -269,9 +253,9 @@ func writeSocks4Response(writer io.Writer, errCode byte, address net.Address, po return err } -func DecodeUDPPacket(packet []byte) (*protocol.RequestHeader, []byte, error) { - if len(packet) < 5 { - return nil, nil, newError("insufficient length of packet.") +func DecodeUDPPacket(packet *buf.Buffer) (*protocol.RequestHeader, error) { + if packet.Len() < 5 { + return nil, newError("insufficient length of packet.") } request := &protocol.RequestHeader{ Version: socks5Version, @@ -279,50 +263,25 @@ func DecodeUDPPacket(packet []byte) (*protocol.RequestHeader, []byte, error) { } // packet[0] and packet[1] are reserved - if packet[2] != 0 /* fragments */ { - return nil, nil, newError("discarding fragmented payload.") + if packet.Byte(2) != 0 /* fragments */ { + return nil, newError("discarding fragmented payload.") } - addrType := packet[3] - var dataBegin int + packet.SliceFrom(3) - switch addrType { - case addrTypeIPv4: - if len(packet) < 10 { - return nil, nil, newError("insufficient length of packet") - } - ip := packet[4:8] - request.Port = net.PortFromBytes(packet[8:10]) - request.Address = net.IPAddress(ip) - dataBegin = 10 - case addrTypeIPv6: - if len(packet) < 22 { - return nil, nil, newError("insufficient length of packet") - } - ip := packet[4:20] - request.Port = net.PortFromBytes(packet[20:22]) - request.Address = net.IPAddress(ip) - dataBegin = 22 - case addrTypeDomain: - domainLength := int(packet[4]) - if len(packet) < 5+domainLength+2 { - return nil, nil, newError("insufficient length of packet") - } - domain := string(packet[5 : 5+domainLength]) - request.Port = net.PortFromBytes(packet[5+domainLength : 5+domainLength+2]) - request.Address = net.ParseAddress(domain) - dataBegin = 5 + domainLength + 2 - default: - return nil, nil, newError("unknown address type ", addrType) + addr, port, err := addrParser.ReadAddressPort(nil, packet) + if err != nil { + return nil, newError("failed to read UDP header").Base(err) } - - return request, packet[dataBegin:], nil + request.Address = addr + request.Port = port + return request, nil } func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) (*buf.Buffer, error) { b := buf.New() b.AppendBytes(0, 0, 0 /* Fragment */) - if err := AppendAddress(b, request.Address, request.Port); err != nil { + if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil { return nil, err } b.Append(data) @@ -342,12 +301,9 @@ func (r *UDPReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if err := b.AppendSupplier(buf.ReadFrom(r.reader)); err != nil { return nil, err } - _, data, err := DecodeUDPPacket(b.Bytes()) - if err != nil { + if _, err := DecodeUDPPacket(b); err != nil { return nil, err } - b.Clear() - b.Append(data) return buf.NewMultiBufferValue(b), nil } @@ -376,40 +332,6 @@ func (w *UDPWriter) Write(b []byte) (int, error) { return len(b), nil } -func ReadAddress(b *buf.Buffer, addrType byte, reader io.Reader) (net.Address, net.Port, error) { - var address net.Address - switch addrType { - case addrTypeIPv4: - if err := b.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { - return nil, 0, err - } - address = net.IPAddress(b.BytesFrom(-4)) - case addrTypeIPv6: - if err := b.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil { - return nil, 0, err - } - address = net.IPAddress(b.BytesFrom(-16)) - case addrTypeDomain: - if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { - return nil, 0, err - } - domainLength := int(b.Byte(b.Len() - 1)) - if err := b.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { - return nil, 0, err - } - address = net.DomainAddress(string(b.BytesFrom(-domainLength))) - default: - return nil, 0, newError("unknown address type: ", addrType) - } - - if err := b.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { - return nil, 0, err - } - port := net.PortFromBytes(b.BytesFrom(-2)) - - return address, port, nil -} - func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { authByte := byte(authNotRequired) if request.User != nil { @@ -462,7 +384,7 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i command = byte(cmdUDPPort) } b.AppendBytes(socks5Version, command, 0x00 /* reserved */) - if err := AppendAddress(b, request.Address, request.Port); err != nil { + if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil { return nil, err } @@ -471,7 +393,7 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i } b.Clear() - if err := b.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { + if err := b.AppendSupplier(buf.ReadFullFrom(reader, 3)); err != nil { return nil, err } @@ -480,11 +402,9 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i return nil, newError("server rejects request: ", resp) } - addrType := b.Byte(3) - b.Clear() - address, port, err := ReadAddress(b, addrType, reader) + address, port, err := addrParser.ReadAddressPort(b, reader) if err != nil { return nil, err } diff --git a/proxy/socks/protocol_test.go b/proxy/socks/protocol_test.go index bd14dfe5f..5857471bc 100644 --- a/proxy/socks/protocol_test.go +++ b/proxy/socks/protocol_test.go @@ -1,7 +1,6 @@ package socks_test import ( - "bytes" "testing" "v2ray.com/core/common/buf" @@ -34,56 +33,3 @@ func TestUDPEncoding(t *testing.T) { assert(err, IsNil) assert(decodedPayload[0].Bytes(), Equals, content) } - -func TestReadAddress(t *testing.T) { - assert := With(t) - - data := []struct { - AddrType byte - Input []byte - Address net.Address - Port net.Port - Error bool - }{ - { - AddrType: 0, - Input: []byte{0, 0, 0, 0}, - Error: true, - }, - { - AddrType: 1, - Input: []byte{0, 0, 0, 0, 0, 53}, - Address: net.IPAddress([]byte{0, 0, 0, 0}), - Port: net.Port(53), - }, - { - AddrType: 4, - Input: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 0, 80}, - Address: net.IPAddress([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}), - Port: net.Port(80), - }, - { - AddrType: 3, - Input: []byte{9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0, 80}, - Address: net.DomainAddress("v2ray.com"), - Port: net.Port(80), - }, - { - AddrType: 3, - Input: []byte{9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0}, - Error: true, - }, - } - - for _, tc := range data { - b := buf.New() - addr, port, err := ReadAddress(b, tc.AddrType, bytes.NewBuffer(tc.Input)) - b.Release() - if tc.Error { - assert(err, IsNotNil) - } else { - assert(addr, Equals, tc.Address) - assert(port, Equals, tc.Port) - } - } -} diff --git a/proxy/socks/server.go b/proxy/socks/server.go index cae3208ef..e80efbd11 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -185,18 +185,20 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, } for _, payload := range mpayload { - request, data, err := DecodeUDPPacket(payload.Bytes()) + request, err := DecodeUDPPacket(payload) if err != nil { newError("failed to parse UDP request").Base(err).WithContext(ctx).WriteToLog() + payload.Release() continue } - if len(data) == 0 { + if payload.IsEmpty() { + payload.Release() continue } - newError("send packet to ", request.Destination(), " with ", len(data), " bytes").AtDebug().WithContext(ctx).WriteToLog() + newError("send packet to ", request.Destination(), " with ", payload.Len(), " bytes").AtDebug().WithContext(ctx).WriteToLog() if source, ok := proxy.SourceFromContext(ctx); ok { log.Record(&log.AccessMessage{ From: source, @@ -206,9 +208,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, }) } - dataBuf := buf.New() - dataBuf.Append(data) - udpServer.Dispatch(ctx, request.Destination(), dataBuf, func(payload *buf.Buffer) { + udpServer.Dispatch(ctx, request.Destination(), payload, func(payload *buf.Buffer) { defer payload.Release() newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WithContext(ctx).WriteToLog() diff --git a/proxy/vmess/encoding/client.go b/proxy/vmess/encoding/client.go index 6fe187418..688e0e241 100644 --- a/proxy/vmess/encoding/client.go +++ b/proxy/vmess/encoding/client.go @@ -15,7 +15,6 @@ import ( "v2ray.com/core/common/buf" "v2ray.com/core/common/crypto" "v2ray.com/core/common/dice" - "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/serial" "v2ray.com/core/proxy/vmess" @@ -82,23 +81,8 @@ func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ buffer.AppendBytes(security, byte(0), byte(header.Command)) if header.Command != protocol.RequestCommandMux { - common.Must(buffer.AppendSupplier(serial.WriteUint16(header.Port.Value()))) - - switch header.Address.Family() { - case net.AddressFamilyIPv4: - buffer.AppendBytes(byte(protocol.AddressTypeIPv4)) - buffer.Append(header.Address.IP()) - case net.AddressFamilyIPv6: - buffer.AppendBytes(byte(protocol.AddressTypeIPv6)) - buffer.Append(header.Address.IP()) - case net.AddressFamilyDomain: - domain := header.Address.Domain() - if protocol.IsDomainTooLong(domain) { - return newError("long domain not supported: ", domain) - } - nDomain := len(domain) - buffer.AppendBytes(byte(protocol.AddressTypeDomain), byte(nDomain)) - common.Must(buffer.AppendSupplier(serial.WriteString(domain))) + if err := addrParser.WriteAddressPort(buffer, header.Address, header.Port); err != nil { + return newError("failed to writer address and port").Base(err) } } diff --git a/proxy/vmess/encoding/const.go b/proxy/vmess/encoding/const.go deleted file mode 100644 index d9d858787..000000000 --- a/proxy/vmess/encoding/const.go +++ /dev/null @@ -1,5 +0,0 @@ -package encoding - -const ( - Version = byte(1) -) diff --git a/proxy/vmess/encoding/encoding.go b/proxy/vmess/encoding/encoding.go index 3772a39d3..b9e36c7fe 100644 --- a/proxy/vmess/encoding/encoding.go +++ b/proxy/vmess/encoding/encoding.go @@ -1,3 +1,19 @@ package encoding +import ( + "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" +) + //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg encoding -path Proxy,VMess,Encoding + +const ( + Version = byte(1) +) + +var addrParser = protocol.NewAddressParser( + protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4), + protocol.AddressFamilyByte(0x02, net.AddressFamilyDomain), + protocol.AddressFamilyByte(0x03, net.AddressFamilyIPv6), + protocol.PortThenAddress(), +) diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index 10d0e0a38..3d778cc85 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -105,44 +105,6 @@ func NewServerSession(validator protocol.UserValidator, sessionHistory *SessionH } } -func readAddress(buffer *buf.Buffer, reader io.Reader) (net.Address, net.Port, error) { - var address net.Address - var port net.Port - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 3)); err != nil { - return address, port, newError("failed to read port and address type").Base(err) - } - port = net.PortFromBytes(buffer.BytesRange(-3, -1)) - - addressType := protocol.AddressType(buffer.Byte(buffer.Len() - 1)) - switch addressType { - case protocol.AddressTypeIPv4: - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil { - return address, port, newError("failed to read IPv4 address").Base(err) - } - address = net.IPAddress(buffer.BytesFrom(-4)) - case protocol.AddressTypeIPv6: - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil { - return address, port, newError("failed to read IPv6 address").Base(err) - } - address = net.IPAddress(buffer.BytesFrom(-16)) - case protocol.AddressTypeDomain: - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { - return address, port, newError("failed to read domain address").Base(err) - } - domainLength := int(buffer.Byte(buffer.Len() - 1)) - if domainLength == 0 { - return address, port, newError("zero length domain") - } - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { - return address, port, newError("failed to read domain address").Base(err) - } - address = net.DomainAddress(string(buffer.BytesFrom(-domainLength))) - default: - return address, port, newError("invalid address type", addressType) - } - return address, port, nil -} - func parseSecurityType(b byte) protocol.SecurityType { if _, f := protocol.SecurityType_name[int32(b)]; f { return protocol.SecurityType(b) @@ -221,7 +183,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request request.Address = net.DomainAddress("v1.mux.cool") request.Port = 0 case protocol.RequestCommandTCP, protocol.RequestCommandUDP: - if addr, port, err := readAddress(buffer, decryptor); err == nil { + if addr, port, err := addrParser.ReadAddressPort(buffer, decryptor); err == nil { request.Address = addr request.Port = port } else { From 61c258f2bea8c41239d4bc60953ad141bdb9efca Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 23 Feb 2018 23:42:33 +0100 Subject: [PATCH 062/183] input channel was being closed too quickly --- proxy/socks/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/socks/server.go b/proxy/socks/server.go index e80efbd11..77803ad2a 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -140,7 +140,7 @@ func (s *Server) transport(ctx context.Context, reader io.Reader, writer io.Writ requestDone := signal.ExecuteAsync(func() error { defer timer.SetTimeout(s.policy().Timeouts.DownlinkOnly) - defer common.Must(input.Close()) + defer input.Close() v2reader := buf.NewReader(reader) if err := buf.Copy(v2reader, input, buf.UpdateActivity(timer)); err != nil { From c43a5e7d8539812f0fca5ef4a9300cf3a8c133c6 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 24 Feb 2018 00:57:54 +0100 Subject: [PATCH 063/183] use AddressParser in mux --- app/proxyman/mux/frame.go | 106 ++++++++++++------------------- app/proxyman/mux/reader.go | 2 +- app/proxyman/mux/writer.go | 6 +- proxy/vmess/encoding/encoding.go | 6 +- 4 files changed, 47 insertions(+), 73 deletions(-) diff --git a/app/proxyman/mux/frame.go b/app/proxyman/mux/frame.go index 462dd0da4..3c56d6044 100644 --- a/app/proxyman/mux/frame.go +++ b/app/proxyman/mux/frame.go @@ -28,6 +28,13 @@ const ( TargetNetworkUDP TargetNetwork = 0x02 ) +var addrParser = protocol.NewAddressParser( + protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), + protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), + protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6), + protocol.PortThenAddress(), +) + /* Frame format 2 bytes - length @@ -48,88 +55,55 @@ type FrameMetadata struct { SessionStatus SessionStatus } -func (f FrameMetadata) AsSupplier() buf.Supplier { - return func(b []byte) (int, error) { - lengthBytes := b - b = serial.Uint16ToBytes(uint16(0), b[:0]) // place holder for length +func (f FrameMetadata) WriteTo(b *buf.Buffer) error { + lenBytes := b.Bytes() + b.AppendBytes(0x00, 0x00) - b = serial.Uint16ToBytes(f.SessionID, b) - b = append(b, byte(f.SessionStatus), byte(f.Option)) - length := 4 + len0 := b.Len() + if err := b.AppendSupplier(serial.WriteUint16(f.SessionID)); err != nil { + return err + } - if f.SessionStatus == SessionStatusNew { - switch f.Target.Network { - case net.Network_TCP: - b = append(b, byte(TargetNetworkTCP)) - case net.Network_UDP: - b = append(b, byte(TargetNetworkUDP)) - } - length++ + b.AppendBytes(byte(f.SessionStatus), byte(f.Option)) - b = serial.Uint16ToBytes(f.Target.Port.Value(), b) - length += 2 - - addr := f.Target.Address - switch addr.Family() { - case net.AddressFamilyIPv4: - b = append(b, byte(protocol.AddressTypeIPv4)) - b = append(b, addr.IP()...) - length += 5 - case net.AddressFamilyIPv6: - b = append(b, byte(protocol.AddressTypeIPv6)) - b = append(b, addr.IP()...) - length += 17 - case net.AddressFamilyDomain: - domain := addr.Domain() - if protocol.IsDomainTooLong(domain) { - return 0, newError("domain name too long: ", domain) - } - nDomain := len(domain) - b = append(b, byte(protocol.AddressTypeDomain), byte(nDomain)) - b = append(b, domain...) - length += nDomain + 2 - } + if f.SessionStatus == SessionStatusNew { + switch f.Target.Network { + case net.Network_TCP: + b.AppendBytes(byte(TargetNetworkTCP)) + case net.Network_UDP: + b.AppendBytes(byte(TargetNetworkUDP)) } - serial.Uint16ToBytes(uint16(length), lengthBytes[:0]) - return length + 2, nil + if err := addrParser.WriteAddressPort(b, f.Target.Address, f.Target.Port); err != nil { + return err + } } + + len1 := b.Len() + serial.Uint16ToBytes(uint16(len1-len0), lenBytes) + return nil } -func ReadFrameFrom(b []byte) (*FrameMetadata, error) { - if len(b) < 4 { - return nil, newError("insufficient buffer: ", len(b)) +func ReadFrameFrom(b *buf.Buffer) (*FrameMetadata, error) { + if b.Len() < 4 { + return nil, newError("insufficient buffer: ", b.Len()) } f := &FrameMetadata{ - SessionID: serial.BytesToUint16(b[:2]), - SessionStatus: SessionStatus(b[2]), - Option: bitmask.Byte(b[3]), + SessionID: serial.BytesToUint16(b.BytesTo(2)), + SessionStatus: SessionStatus(b.Byte(2)), + Option: bitmask.Byte(b.Byte(3)), } - b = b[4:] - if f.SessionStatus == SessionStatusNew { - network := TargetNetwork(b[0]) - port := net.PortFromBytes(b[1:3]) - addrType := protocol.AddressType(b[3]) - b = b[4:] + network := TargetNetwork(b.Byte(4)) + b.SliceFrom(5) - var addr net.Address - switch addrType { - case protocol.AddressTypeIPv4: - addr = net.IPAddress(b[0:4]) - b = b[4:] - case protocol.AddressTypeIPv6: - addr = net.IPAddress(b[0:16]) - b = b[16:] - case protocol.AddressTypeDomain: - nDomain := int(b[0]) - addr = net.DomainAddress(string(b[1 : 1+nDomain])) - b = b[nDomain+1:] - default: - return nil, newError("unknown address type: ", addrType) + addr, port, err := addrParser.ReadAddressPort(nil, b) + if err != nil { + return nil, newError("failed to parse address and port").Base(err) } + switch network { case TargetNetworkTCP: f.Target = net.TCPDestination(addr, port) diff --git a/app/proxyman/mux/reader.go b/app/proxyman/mux/reader.go index 39e6d8ba7..98b6e4400 100644 --- a/app/proxyman/mux/reader.go +++ b/app/proxyman/mux/reader.go @@ -23,7 +23,7 @@ func ReadMetadata(reader io.Reader) (*FrameMetadata, error) { if err := b.Reset(buf.ReadFullFrom(reader, int(metaLen))); err != nil { return nil, err } - return ReadFrameFrom(b.Bytes()) + return ReadFrameFrom(b) } // PacketReader is an io.Reader that reads whole chunk of Mux frames every time. diff --git a/app/proxyman/mux/writer.go b/app/proxyman/mux/writer.go index f8cbc21f2..b7ecfa4e0 100644 --- a/app/proxyman/mux/writer.go +++ b/app/proxyman/mux/writer.go @@ -53,7 +53,7 @@ func (w *Writer) getNextFrameMeta() FrameMetadata { func (w *Writer) writeMetaOnly() error { meta := w.getNextFrameMeta() b := buf.New() - if err := b.Reset(meta.AsSupplier()); err != nil { + if err := meta.WriteTo(b); err != nil { return err } return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(b)) @@ -64,7 +64,7 @@ func (w *Writer) writeData(mb buf.MultiBuffer) error { meta.Option.Set(OptionData) frame := buf.New() - if err := frame.Reset(meta.AsSupplier()); err != nil { + if err := meta.WriteTo(frame); err != nil { return err } if err := frame.AppendSupplier(serial.WriteUint16(uint16(mb.Len()))); err != nil { @@ -107,7 +107,7 @@ func (w *Writer) Close() error { } frame := buf.New() - common.Must(frame.Reset(meta.AsSupplier())) + common.Must(meta.WriteTo(frame)) w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(frame)) return nil diff --git a/proxy/vmess/encoding/encoding.go b/proxy/vmess/encoding/encoding.go index b9e36c7fe..a016f15b4 100644 --- a/proxy/vmess/encoding/encoding.go +++ b/proxy/vmess/encoding/encoding.go @@ -12,8 +12,8 @@ const ( ) var addrParser = protocol.NewAddressParser( - protocol.AddressFamilyByte(0x01, net.AddressFamilyIPv4), - protocol.AddressFamilyByte(0x02, net.AddressFamilyDomain), - protocol.AddressFamilyByte(0x03, net.AddressFamilyIPv6), + protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), + protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), + protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6), protocol.PortThenAddress(), ) From a42b4b513e211463943bab4dc591afa735a8d7af Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 24 Feb 2018 02:13:40 +0100 Subject: [PATCH 064/183] test case for write address --- common/protocol/address_test.go | 39 ++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/common/protocol/address_test.go b/common/protocol/address_test.go index 5e724d511..584ae7479 100644 --- a/common/protocol/address_test.go +++ b/common/protocol/address_test.go @@ -10,7 +10,7 @@ import ( . "v2ray.com/ext/assert" ) -func TestAddressParser(t *testing.T) { +func TestAddressReading(t *testing.T) { assert := With(t) data := []struct { @@ -20,6 +20,11 @@ func TestAddressParser(t *testing.T) { Port net.Port Error bool }{ + { + Options: []AddressOption{}, + Input: []byte{}, + Error: true, + }, { Options: []AddressOption{}, Input: []byte{0, 0, 0, 0, 0}, @@ -65,6 +70,38 @@ func TestAddressParser(t *testing.T) { } else { assert(addr, Equals, tc.Address) assert(port, Equals, tc.Port) + assert(err, IsNil) + } + } +} + +func TestAddressWriting(t *testing.T) { + assert := With(t) + + data := []struct { + Options []AddressOption + Address net.Address + Port net.Port + Bytes []byte + Error bool + }{ + { + Options: []AddressOption{AddressFamilyByte(0x01, net.AddressFamilyIPv4)}, + Address: net.LocalHostIP, + Port: net.Port(80), + Bytes: []byte{1, 127, 0, 0, 1, 0, 80}, + }, + } + + for _, tc := range data { + parser := NewAddressParser(tc.Options...) + + b := buf.New() + err := parser.WriteAddressPort(b, tc.Address, tc.Port) + if tc.Error { + assert(err, IsNotNil) + } else { + assert(b.Bytes(), Equals, tc.Bytes) } } } From dfd4d397bbd54c0b5bd8c4f1a50a5d047b7e4cd5 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 24 Feb 2018 02:14:50 +0100 Subject: [PATCH 065/183] fix version format --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index 4c6d09853..fc709468e 100644 --- a/core.go +++ b/core.go @@ -31,7 +31,7 @@ func Version() string { // VersionStatement returns a list of strings representing the full version info. func VersionStatement() []string { return []string{ - serial.Concat("V2Ray ", Version(), "(", codename, ")", build), + serial.Concat("V2Ray ", Version(), " (", codename, ") ", build), intro, } } From 2b4104e491d2388b20c86267195095d5f8dc410b Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 24 Feb 2018 13:57:25 +0100 Subject: [PATCH 066/183] benchmark for dice --- common/dice/dice_test.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 common/dice/dice_test.go diff --git a/common/dice/dice_test.go b/common/dice/dice_test.go new file mode 100644 index 000000000..09b1dd4d3 --- /dev/null +++ b/common/dice/dice_test.go @@ -0,0 +1,32 @@ +package dice_test + +import ( + "math/rand" + "testing" + + . "v2ray.com/core/common/dice" +) + +func BenchmarkRoll1(b *testing.B) { + for i := 0; i < b.N; i++ { + Roll(1) + } +} + +func BenchmarkRoll20(b *testing.B) { + for i := 0; i < b.N; i++ { + Roll(20) + } +} + +func BenchmarkIntn1(b *testing.B) { + for i := 0; i < b.N; i++ { + rand.Intn(1) + } +} + +func BenchmarkIntn20(b *testing.B) { + for i := 0; i < b.N; i++ { + rand.Intn(20) + } +} From 1e0cad50b1a9afe3be83ee738f9d31153924f883 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 24 Feb 2018 23:23:10 +0100 Subject: [PATCH 067/183] buffered input in kcp client --- transport/internet/kcp/dialer.go | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/transport/internet/kcp/dialer.go b/transport/internet/kcp/dialer.go index 44f9fd479..1c33410a2 100644 --- a/transport/internet/kcp/dialer.go +++ b/transport/internet/kcp/dialer.go @@ -19,16 +19,26 @@ var ( ) func fetchInput(ctx context.Context, input io.Reader, reader PacketReader, conn *Connection) { - payload := buf.New() - defer payload.Release() - - for { - err := payload.Reset(buf.ReadFrom(input)) - if err != nil { - payload.Release() - return + cache := make(chan *buf.Buffer, 1024) + go func() { + for { + payload := buf.New() + if err := payload.Reset(buf.ReadFrom(input)); err != nil { + payload.Release() + close(cache) + return + } + select { + case cache <- payload: + default: + payload.Release() + } } + }() + + for payload := range cache { segments := reader.Read(payload.Bytes()) + payload.Release() if len(segments) > 0 { conn.Input(segments) } From caa52e9327d923466778bc5de562820fce08329e Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 25 Feb 2018 00:02:31 +0100 Subject: [PATCH 068/183] early release buffers for better reusing --- proxy/shadowsocks/server.go | 3 +-- proxy/socks/server.go | 4 ++-- transport/internet/kcp/listener.go | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 8aacd2eb3..ffb676920 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -117,9 +117,8 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection ctx = protocol.ContextWithUser(ctx, request.User) udpServer.Dispatch(ctx, dest, data, func(payload *buf.Buffer) { - defer payload.Release() - data, err := EncodeUDPPacket(request, payload.Bytes()) + payload.Release() if err != nil { newError("failed to encode UDP packet").Base(err).AtWarning().WithContext(ctx).WriteToLog() return diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 77803ad2a..314fd30ef 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -209,11 +209,11 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn internet.Connection, } udpServer.Dispatch(ctx, request.Destination(), payload, func(payload *buf.Buffer) { - defer payload.Release() - newError("writing back UDP response with ", payload.Len(), " bytes").AtDebug().WithContext(ctx).WriteToLog() udpMessage, err := EncodeUDPPacket(request, payload.Bytes()) + payload.Release() + defer udpMessage.Release() if err != nil { newError("failed to write UDP response").AtWarning().Base(err).WithContext(ctx).WriteToLog() diff --git a/transport/internet/kcp/listener.go b/transport/internet/kcp/listener.go index 12786cc02..00ea56f29 100644 --- a/transport/internet/kcp/listener.go +++ b/transport/internet/kcp/listener.go @@ -73,9 +73,9 @@ func NewListener(ctx context.Context, address net.Address, port net.Port, addCon } func (l *Listener) OnReceive(payload *buf.Buffer, src net.Destination, originalDest net.Destination) { - defer payload.Release() - segments := l.reader.Read(payload.Bytes()) + payload.Release() + if len(segments) == 0 { newError("discarding invalid payload from ", src).WriteToLog() return From 4783b8f2c649be5a2f6c90ab00a612a1c56105c1 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 25 Feb 2018 22:13:42 +0100 Subject: [PATCH 069/183] stop doing anything after being released. fixes #804 --- transport/internet/kcp/sending.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/transport/internet/kcp/sending.go b/transport/internet/kcp/sending.go index 0bc02c8d8..6cefe6dbb 100644 --- a/transport/internet/kcp/sending.go +++ b/transport/internet/kcp/sending.go @@ -191,11 +191,12 @@ type SendingWorker struct { conn *Connection window *SendingWindow firstUnacknowledged uint32 - firstUnacknowledgedUpdated bool nextNumber uint32 remoteNextNumber uint32 controlWindow uint32 fastResend uint32 + firstUnacknowledgedUpdated bool + closed bool } func NewSendingWorker(kcp *Connection) *SendingWorker { @@ -212,6 +213,7 @@ func NewSendingWorker(kcp *Connection) *SendingWorker { func (w *SendingWorker) Release() { w.Lock() w.window.Release() + w.closed = true w.Unlock() } @@ -258,6 +260,10 @@ func (w *SendingWorker) ProcessSegment(current uint32, seg *AckSegment, rto uint w.Lock() defer w.Unlock() + if w.closed { + return + } + if w.remoteNextNumber < seg.ReceivingWindow { w.remoteNextNumber = seg.ReceivingWindow } @@ -289,6 +295,10 @@ func (w *SendingWorker) Push(f buf.Supplier) bool { w.Lock() defer w.Unlock() + if w.closed { + return false + } + if w.window.IsFull() { return false } @@ -333,6 +343,11 @@ func (w *SendingWorker) OnPacketLoss(lossRate uint32) { func (w *SendingWorker) Flush(current uint32) { w.Lock() + if w.closed { + w.Unlock() + return + } + cwnd := w.firstUnacknowledged + w.conn.Config.GetSendingInFlightSize() if cwnd > w.remoteNextNumber { cwnd = w.remoteNextNumber From 58e79dbdc5e170271a1f8008f5da50a8394edc52 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 25 Feb 2018 23:00:04 +0100 Subject: [PATCH 070/183] refactor kcp.ReadSegment --- transport/internet/kcp/segment.go | 166 ++++++++++++++++-------------- 1 file changed, 91 insertions(+), 75 deletions(-) diff --git a/transport/internet/kcp/segment.go b/transport/internet/kcp/segment.go index 5acf25be1..9af1629dd 100644 --- a/transport/internet/kcp/segment.go +++ b/transport/internet/kcp/segment.go @@ -31,6 +31,7 @@ type Segment interface { Command() Command ByteSize() int Bytes() buf.Supplier + parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) } const ( @@ -53,6 +54,34 @@ func NewDataSegment() *DataSegment { return new(DataSegment) } +func (s *DataSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) { + s.Conv = conv + s.Option = opt + if len(buf) < 15 { + return false, nil + } + s.Timestamp = serial.BytesToUint32(buf) + buf = buf[4:] + + s.Number = serial.BytesToUint32(buf) + buf = buf[4:] + + s.SendingNext = serial.BytesToUint32(buf) + buf = buf[4:] + + dataLen := int(serial.BytesToUint16(buf)) + buf = buf[2:] + + if len(buf) < dataLen { + return false, nil + } + s.Data().Clear() + s.Data().Append(buf[:dataLen]) + buf = buf[dataLen:] + + return true, buf +} + func (s *DataSegment) Conversation() uint16 { return s.Conv } @@ -113,6 +142,36 @@ func NewAckSegment() *AckSegment { } } +func (s *AckSegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) { + s.Conv = conv + s.Option = opt + if len(buf) < 13 { + return false, nil + } + + s.ReceivingWindow = serial.BytesToUint32(buf) + buf = buf[4:] + + s.ReceivingNext = serial.BytesToUint32(buf) + buf = buf[4:] + + s.Timestamp = serial.BytesToUint32(buf) + buf = buf[4:] + + count := int(buf[0]) + buf = buf[1:] + + if len(buf) < count*4 { + return false, nil + } + for i := 0; i < count; i++ { + s.PutNumber(serial.BytesToUint32(buf)) + buf = buf[4:] + } + + return true, buf +} + func (s *AckSegment) Conversation() uint16 { return s.Conv } @@ -176,6 +235,27 @@ func NewCmdOnlySegment() *CmdOnlySegment { return new(CmdOnlySegment) } +func (s *CmdOnlySegment) parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) { + s.Conv = conv + s.Cmd = cmd + s.Option = opt + + if len(buf) < 12 { + return false, nil + } + + s.SendingNext = serial.BytesToUint32(buf) + buf = buf[4:] + + s.ReceivingNext = serial.BytesToUint32(buf) + buf = buf[4:] + + s.PeerRTO = serial.BytesToUint32(buf) + buf = buf[4:] + + return true, buf +} + func (s *CmdOnlySegment) Conversation() uint16 { return s.Conv } @@ -213,83 +293,19 @@ func ReadSegment(buf []byte) (Segment, []byte) { opt := SegmentOption(buf[1]) buf = buf[2:] - if cmd == CommandData { - seg := NewDataSegment() - seg.Conv = conv - seg.Option = opt - if len(buf) < 15 { - return nil, nil - } - seg.Timestamp = serial.BytesToUint32(buf) - buf = buf[4:] - - seg.Number = serial.BytesToUint32(buf) - buf = buf[4:] - - seg.SendingNext = serial.BytesToUint32(buf) - buf = buf[4:] - - dataLen := int(serial.BytesToUint16(buf)) - buf = buf[2:] - - if len(buf) < dataLen { - return nil, nil - } - seg.Data().Clear() - seg.Data().Append(buf[:dataLen]) - buf = buf[dataLen:] - - return seg, buf + var seg Segment + switch cmd { + case CommandData: + seg = NewDataSegment() + case CommandACK: + seg = NewAckSegment() + default: + seg = NewCmdOnlySegment() } - if cmd == CommandACK { - seg := NewAckSegment() - seg.Conv = conv - seg.Option = opt - if len(buf) < 13 { - return nil, nil - } - - seg.ReceivingWindow = serial.BytesToUint32(buf) - buf = buf[4:] - - seg.ReceivingNext = serial.BytesToUint32(buf) - buf = buf[4:] - - seg.Timestamp = serial.BytesToUint32(buf) - buf = buf[4:] - - count := int(buf[0]) - buf = buf[1:] - - if len(buf) < count*4 { - return nil, nil - } - for i := 0; i < count; i++ { - seg.PutNumber(serial.BytesToUint32(buf)) - buf = buf[4:] - } - - return seg, buf - } - - seg := NewCmdOnlySegment() - seg.Conv = conv - seg.Cmd = cmd - seg.Option = opt - - if len(buf) < 12 { + valid, extra := seg.parse(conv, cmd, opt, buf) + if !valid { return nil, nil } - - seg.SendingNext = serial.BytesToUint32(buf) - buf = buf[4:] - - seg.ReceivingNext = serial.BytesToUint32(buf) - buf = buf[4:] - - seg.PeerRTO = serial.BytesToUint32(buf) - buf = buf[4:] - - return seg, buf + return seg, extra } From 2c56f5fd9c3a469560b78bae8de4930d1c331fa6 Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Mon, 26 Feb 2018 08:57:43 +0800 Subject: [PATCH 071/183] Remove duplicate len function call. --- proxy/vmess/vmess.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/vmess/vmess.go b/proxy/vmess/vmess.go index 52ead21f2..6d5510ade 100644 --- a/proxy/vmess/vmess.go +++ b/proxy/vmess/vmess.go @@ -159,7 +159,7 @@ func (v *TimedUserValidator) Remove(email string) bool { return false } ulen := len(v.users) - if idx < len(v.users) { + if idx < ulen { v.users[idx] = v.users[ulen-1] v.users[ulen-1] = nil v.users = v.users[:ulen-1] From 861ff94e5ed3d1820c9589682ca96916865b7574 Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Mon, 26 Feb 2018 09:06:27 +0800 Subject: [PATCH 072/183] Fix sniff typo --- app/dispatcher/default.go | 10 +++++----- app/dispatcher/sniffer.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 16079bf6b..738fbb539 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -55,12 +55,12 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin ctx = proxy.ContextWithTarget(ctx, destination) outbound := ray.NewRay(ctx) - sniferList := proxyman.ProtocoSniffersFromContext(ctx) - if destination.Address.Family().IsDomain() || len(sniferList) == 0 { + snifferList := proxyman.ProtocoSniffersFromContext(ctx) + if destination.Address.Family().IsDomain() || len(snifferList) == 0 { go d.routedDispatch(ctx, outbound, destination) } else { go func() { - domain, err := snifer(ctx, sniferList, outbound) + domain, err := sniffer(ctx, snifferList, outbound) if err == nil { newError("sniffed domain: ", domain).WithContext(ctx).WriteToLog() destination.Address = net.ParseAddress(domain) @@ -72,11 +72,11 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin return outbound, nil } -func snifer(ctx context.Context, sniferList []proxyman.KnownProtocols, outbound ray.OutboundRay) (string, error) { +func sniffer(ctx context.Context, snifferList []proxyman.KnownProtocols, outbound ray.OutboundRay) (string, error) { payload := buf.New() defer payload.Release() - sniffer := NewSniffer(sniferList) + sniffer := NewSniffer(snifferList) totalAttempt := 0 for { select { diff --git a/app/dispatcher/sniffer.go b/app/dispatcher/sniffer.go index a02d81054..f81ba9a02 100644 --- a/app/dispatcher/sniffer.go +++ b/app/dispatcher/sniffer.go @@ -173,10 +173,10 @@ type Sniffer struct { err []error } -func NewSniffer(sniferList []proxyman.KnownProtocols) *Sniffer { +func NewSniffer(snifferList []proxyman.KnownProtocols) *Sniffer { s := new(Sniffer) - for _, protocol := range sniferList { + for _, protocol := range snifferList { var f func([]byte) (string, error) switch protocol { case proxyman.KnownProtocols_HTTP: From 6a3abf31473c582d9674806d6f8eccf996e3ee2c Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 26 Feb 2018 17:49:53 +0100 Subject: [PATCH 073/183] fix data race when caching session id --- proxy/vmess/encoding/server.go | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index 3d778cc85..0642adb4b 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -55,21 +55,16 @@ func (h *SessionHistory) Close() error { return h.task.Close() } -func (h *SessionHistory) add(session sessionId) { +func (h *SessionHistory) addIfNotExits(session sessionId) bool { h.Lock() defer h.Unlock() - h.cache[session] = time.Now().Add(time.Minute * 3) -} - -func (h *SessionHistory) has(session sessionId) bool { - h.RLock() - defer h.RUnlock() - - if expire, found := h.cache[session]; found { - return expire.After(time.Now()) + if expire, found := h.cache[session]; found && expire.After(time.Now()) { + return false } - return false + + h.cache[session] = time.Now().Add(time.Minute * 3) + return true } func (h *SessionHistory) removeExpiredEntries() { @@ -152,10 +147,9 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request copy(sid.user[:], vmessAccount.ID.Bytes()) copy(sid.key[:], s.requestBodyKey) copy(sid.nonce[:], s.requestBodyIV) - if s.sessionHistory.has(sid) { + if !s.sessionHistory.addIfNotExits(sid) { return nil, newError("duplicated session id, possibly under replay attack") } - s.sessionHistory.add(sid) s.responseHeader = buffer.Byte(33) // 1 byte request.Option = bitmask.Byte(buffer.Byte(34)) // 1 byte From b7d48fe7c5db81e2c435760f9fcfcc88ab8e5098 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 26 Feb 2018 17:50:14 +0100 Subject: [PATCH 074/183] prevent reading 0 bytes --- proxy/shadowsocks/protocol.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index fb3f22b91..4ad68a884 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -68,7 +68,7 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea if err != nil { // Invalid address. Continue to read some bytes to confuse client. - nBytes := dice.Roll(32) + nBytes := dice.Roll(32) + 1 buffer.Clear() buffer.AppendSupplier(buf.ReadFullFrom(br, nBytes)) return nil, nil, newError("failed to read address").Base(err) From bdab1af29a64d89a056a3297637dea0ba9676957 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 28 Feb 2018 15:15:22 +0100 Subject: [PATCH 075/183] update tls config generation --- transport/internet/kcp/dialer.go | 4 ++-- transport/internet/tcp/dialer.go | 4 ++-- transport/internet/tcp/hub.go | 4 ++-- transport/internet/tls/config.go | 28 ++++++++++++++------------ transport/internet/websocket/dialer.go | 4 ++-- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/transport/internet/kcp/dialer.go b/transport/internet/kcp/dialer.go index 1c33410a2..e44e6b1b4 100644 --- a/transport/internet/kcp/dialer.go +++ b/transport/internet/kcp/dialer.go @@ -86,8 +86,8 @@ func DialKCP(ctx context.Context, dest net.Destination) (internet.Connection, er var iConn internet.Connection = session - if config := v2tls.ConfigFromContext(ctx, v2tls.WithDestination(dest)); config != nil { - tlsConn := tls.Client(iConn, config.GetTLSConfig()) + if config := v2tls.ConfigFromContext(ctx); config != nil { + tlsConn := tls.Client(iConn, config.GetTLSConfig(v2tls.WithDestination(dest))) iConn = tlsConn } diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index 1b5f544d2..d90ed46e2 100644 --- a/transport/internet/tcp/dialer.go +++ b/transport/internet/tcp/dialer.go @@ -27,8 +27,8 @@ func Dial(ctx context.Context, dest net.Destination) (internet.Connection, error return nil, err } - if config := tls.ConfigFromContext(ctx, tls.WithDestination(dest), tls.WithNextProto("h2")); config != nil { - conn = tls.Client(conn, config.GetTLSConfig()) + if config := tls.ConfigFromContext(ctx); config != nil { + conn = tls.Client(conn, config.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("h2"))) } tcpSettings := getTCPSettingsFromContext(ctx) diff --git a/transport/internet/tcp/hub.go b/transport/internet/tcp/hub.go index 4f535a08b..e52cc5c00 100644 --- a/transport/internet/tcp/hub.go +++ b/transport/internet/tcp/hub.go @@ -39,8 +39,8 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, handler addConn: handler, } - if config := tls.ConfigFromContext(ctx, tls.WithNextProto("h2")); config != nil { - l.tlsConfig = config.GetTLSConfig() + if config := tls.ConfigFromContext(ctx); config != nil { + l.tlsConfig = config.GetTLSConfig(tls.WithNextProto("h2")) } if tcpSettings.HeaderSettings != nil { diff --git a/transport/internet/tls/config.go b/transport/internet/tls/config.go index 1faf1f7a5..7172df527 100644 --- a/transport/internet/tls/config.go +++ b/transport/internet/tls/config.go @@ -25,7 +25,7 @@ func (c *Config) BuildCertificates() []tls.Certificate { return certs } -func (c *Config) GetTLSConfig() *tls.Config { +func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { config := &tls.Config{ ClientSessionCache: globalSessionCache, NextProtos: []string{"http/1.1"}, @@ -34,6 +34,10 @@ func (c *Config) GetTLSConfig() *tls.Config { return config } + for _, opt := range opts { + opt(config) + } + config.InsecureSkipVerify = c.AllowInsecure config.Certificates = c.BuildCertificates() config.BuildNameToCertificate() @@ -47,10 +51,10 @@ func (c *Config) GetTLSConfig() *tls.Config { return config } -type Option func(*Config) +type Option func(*tls.Config) func WithDestination(dest net.Destination) Option { - return func(config *Config) { + return func(config *tls.Config) { if dest.Address.Family().IsDomain() && len(config.ServerName) == 0 { config.ServerName = dest.Address.Domain() } @@ -58,23 +62,21 @@ func WithDestination(dest net.Destination) Option { } func WithNextProto(protocol ...string) Option { - return func(config *Config) { - if len(config.NextProtocol) == 0 { - config.NextProtocol = protocol + return func(config *tls.Config) { + if len(config.NextProtos) == 0 { + config.NextProtos = protocol } } } -func ConfigFromContext(ctx context.Context, opts ...Option) *Config { +func ConfigFromContext(ctx context.Context) *Config { securitySettings := internet.SecuritySettingsFromContext(ctx) if securitySettings == nil { return nil } - if config, ok := securitySettings.(*Config); ok { - for _, opt := range opts { - opt(config) - } - return config + config, ok := securitySettings.(*Config) + if !ok { + return nil } - return nil + return config } diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index 9d6cf9bf9..8a1b0c713 100644 --- a/transport/internet/websocket/dialer.go +++ b/transport/internet/websocket/dialer.go @@ -41,9 +41,9 @@ func dialWebsocket(ctx context.Context, dest net.Destination) (net.Conn, error) protocol := "ws" - if config := tls.ConfigFromContext(ctx, tls.WithDestination(dest)); config != nil { + if config := tls.ConfigFromContext(ctx); config != nil { protocol = "wss" - dialer.TLSClientConfig = config.GetTLSConfig() + dialer.TLSClientConfig = config.GetTLSConfig(tls.WithDestination(dest)) } host := dest.NetAddr() From a7d467992df6165050c909abeff7bcdfb37e8022 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 11:32:55 +0100 Subject: [PATCH 076/183] try parse domain address as IP. fixes #894. --- common/protocol/address.go | 25 ++++++++++++++++++++++++- common/protocol/address_test.go | 11 +++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/common/protocol/address.go b/common/protocol/address.go index 44c1abb9b..a0ae63525 100644 --- a/common/protocol/address.go +++ b/common/protocol/address.go @@ -55,6 +55,19 @@ func (p *AddressParser) readPort(b *buf.Buffer, reader io.Reader) (net.Port, err return net.PortFromBytes(b.BytesFrom(-2)), nil } +func maybeIPPrefix(b byte) bool { + return b == '[' || (b >= '0' && b <= '9') +} + +func isValidDomain(d string) bool { + for _, c := range d { + if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '.') { + return false + } + } + return true +} + func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Address, error) { if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { return nil, err @@ -89,7 +102,17 @@ func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Addres if err := b.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { return nil, err } - return net.DomainAddress(string(b.BytesFrom(-domainLength))), nil + domain := string(b.BytesFrom(-domainLength)) + if maybeIPPrefix(domain[0]) { + addr := net.ParseAddress(domain) + if addr.Family().IsIPv4() || addrFamily.IsIPv6() { + return addr, nil + } + } + if !isValidDomain(domain) { + return nil, newError("invalid domain name: ", domain) + } + return net.DomainAddress(domain), nil default: panic("impossible case") } diff --git a/common/protocol/address_test.go b/common/protocol/address_test.go index 584ae7479..d66d119e5 100644 --- a/common/protocol/address_test.go +++ b/common/protocol/address_test.go @@ -58,6 +58,17 @@ func TestAddressReading(t *testing.T) { Input: []byte{3, 9, 118, 50, 114, 97, 121, 46, 99, 111, 109, 0}, Error: true, }, + { + Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)}, + Input: []byte{3, 7, 56, 46, 56, 46, 56, 46, 56, 0, 80}, + Address: net.ParseAddress("8.8.8.8"), + Port: net.Port(80), + }, + { + Options: []AddressOption{AddressFamilyByte(0x03, net.AddressFamilyDomain)}, + Input: []byte{3, 7, 10, 46, 56, 46, 56, 46, 56, 0, 80}, + Error: true, + }, } for _, tc := range data { From af29e4277159d3f446f11e08036fca50b4dd6e57 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 11:53:39 +0100 Subject: [PATCH 077/183] move tcp.PickPort to its own package --- testing/scenarios/command_test.go | 10 ++++---- testing/scenarios/common.go | 9 ------- testing/scenarios/dns_test.go | 2 +- testing/scenarios/dokodemo_test.go | 8 +++---- testing/scenarios/feature_test.go | 28 +++++++++++----------- testing/scenarios/http_test.go | 14 +++++------ testing/scenarios/policy_test.go | 5 ++-- testing/scenarios/shadowsocks_test.go | 26 ++++++++++---------- testing/scenarios/socks_test.go | 12 +++++----- testing/scenarios/tls_test.go | 10 ++++---- testing/scenarios/transport_test.go | 4 ++-- testing/scenarios/vmess_test.go | 34 +++++++++++++-------------- testing/servers/tcp/port.go | 16 +++++++++++++ testing/servers/udp/port.go | 1 + 14 files changed, 94 insertions(+), 85 deletions(-) create mode 100644 testing/servers/tcp/port.go diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go index b15431f64..c3d0844b2 100644 --- a/testing/scenarios/command_test.go +++ b/testing/scenarios/command_test.go @@ -37,8 +37,8 @@ func TestCommanderRemoveHandler(t *testing.T) { assert(err, IsNil) defer tcpServer.Close() - clientPort := pickPort() - cmdPort := pickPort() + clientPort := tcp.PickPort() + cmdPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&commander.Config{ @@ -150,8 +150,8 @@ func TestCommanderAddRemoveUser(t *testing.T) { u1 := protocol.NewID(uuid.New()) u2 := protocol.NewID(uuid.New()) - cmdPort := pickPort() - serverPort := pickPort() + cmdPort := tcp.PickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&commander.Config{ @@ -219,7 +219,7 @@ func TestCommanderAddRemoveUser(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&policy.Config{ diff --git a/testing/scenarios/common.go b/testing/scenarios/common.go index ac6155bb8..bfc8c03c2 100644 --- a/testing/scenarios/common.go +++ b/testing/scenarios/common.go @@ -22,15 +22,6 @@ import ( "v2ray.com/core/common/serial" ) -func pickPort() net.Port { - listener, err := net.Listen("tcp4", "127.0.0.1:0") - common.Must(err) - defer listener.Close() - - addr := listener.Addr().(*net.TCPAddr) - return net.Port(addr.Port) -} - func xor(b []byte) []byte { r := make([]byte, len(b)) for i, v := range b { diff --git a/testing/scenarios/dns_test.go b/testing/scenarios/dns_test.go index 5f168211d..bb9e92ac7 100644 --- a/testing/scenarios/dns_test.go +++ b/testing/scenarios/dns_test.go @@ -28,7 +28,7 @@ func TestResolveIP(t *testing.T) { assert(err, IsNil) defer tcpServer.Close() - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&dns.Config{ diff --git a/testing/scenarios/dokodemo_test.go b/testing/scenarios/dokodemo_test.go index 2a821f35b..5148280d5 100644 --- a/testing/scenarios/dokodemo_test.go +++ b/testing/scenarios/dokodemo_test.go @@ -32,7 +32,7 @@ func TestDokodemoTCP(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -64,7 +64,7 @@ func TestDokodemoTCP(t *testing.T) { }, } - clientPort := uint32(pickPort()) + clientPort := uint32(tcp.PickPort()) clientPortRange := uint32(5) clientConfig := &core.Config{ App: []*serial.TypedMessage{ @@ -145,7 +145,7 @@ func TestDokodemoUDP(t *testing.T) { defer udpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -171,7 +171,7 @@ func TestDokodemoUDP(t *testing.T) { }, } - clientPort := uint32(pickPort()) + clientPort := uint32(tcp.PickPort()) clientPortRange := uint32(5) clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ diff --git a/testing/scenarios/feature_test.go b/testing/scenarios/feature_test.go index 6549417b7..84aaaccd8 100644 --- a/testing/scenarios/feature_test.go +++ b/testing/scenarios/feature_test.go @@ -46,7 +46,7 @@ func TestPassiveConnection(t *testing.T) { assert(err, IsNil) defer tcpServer.Close() - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -117,7 +117,7 @@ func TestProxy(t *testing.T) { defer tcpServer.Close() serverUserID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -144,7 +144,7 @@ func TestProxy(t *testing.T) { } proxyUserID := protocol.NewID(uuid.New()) - proxyPort := pickPort() + proxyPort := tcp.PickPort() proxyConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -170,7 +170,7 @@ func TestProxy(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -265,7 +265,7 @@ func TestProxyOverKCP(t *testing.T) { defer tcpServer.Close() serverUserID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -295,7 +295,7 @@ func TestProxyOverKCP(t *testing.T) { } proxyUserID := protocol.NewID(uuid.New()) - proxyPort := pickPort() + proxyPort := tcp.PickPort() proxyConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -326,7 +326,7 @@ func TestProxyOverKCP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -430,8 +430,8 @@ func TestBlackhole(t *testing.T) { assert(err, IsNil) defer tcpServer2.Close() - serverPort := pickPort() - serverPort2 := pickPort() + serverPort := tcp.PickPort() + serverPort2 := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -521,7 +521,7 @@ func TestForward(t *testing.T) { assert(err, IsNil) defer tcpServer.Close() - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -587,7 +587,7 @@ func TestUDPConnection(t *testing.T) { assert(err, IsNil) defer udpServer.Close() - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -663,8 +663,8 @@ func TestUDPConnection(t *testing.T) { func TestDomainSniffing(t *testing.T) { assert := With(t) - sniffingPort := pickPort() - httpPort := pickPort() + sniffingPort := tcp.PickPort() + httpPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -764,7 +764,7 @@ func TestDialV2Ray(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ diff --git a/testing/scenarios/http_test.go b/testing/scenarios/http_test.go index 942718e33..e490c4a42 100644 --- a/testing/scenarios/http_test.go +++ b/testing/scenarios/http_test.go @@ -26,7 +26,7 @@ import ( func TestHttpConformance(t *testing.T) { assert := With(t) - httpServerPort := pickPort() + httpServerPort := tcp.PickPort() httpServer := &v2httptest.Server{ Port: httpServerPort, PathHandler: make(map[string]http.HandlerFunc), @@ -35,7 +35,7 @@ func TestHttpConformance(t *testing.T) { assert(err, IsNil) defer httpServer.Close() - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -90,7 +90,7 @@ func TestHttpConnectMethod(t *testing.T) { assert(err, IsNil) defer tcpServer.Close() - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -146,7 +146,7 @@ func TestHttpConnectMethod(t *testing.T) { func TestHttpPost(t *testing.T) { assert := With(t) - httpServerPort := pickPort() + httpServerPort := tcp.PickPort() httpServer := &v2httptest.Server{ Port: httpServerPort, PathHandler: map[string]http.HandlerFunc{ @@ -169,7 +169,7 @@ func TestHttpPost(t *testing.T) { assert(err, IsNil) defer httpServer.Close() - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -226,7 +226,7 @@ func setProxyBasicAuth(req *http.Request, user, pass string) { func TestHttpBasicAuth(t *testing.T) { assert := With(t) - httpServerPort := pickPort() + httpServerPort := tcp.PickPort() httpServer := &v2httptest.Server{ Port: httpServerPort, PathHandler: make(map[string]http.HandlerFunc), @@ -235,7 +235,7 @@ func TestHttpBasicAuth(t *testing.T) { assert(err, IsNil) defer httpServer.Close() - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { diff --git a/testing/scenarios/policy_test.go b/testing/scenarios/policy_test.go index 9e8b7ce7f..93e4de3e1 100644 --- a/testing/scenarios/policy_test.go +++ b/testing/scenarios/policy_test.go @@ -17,6 +17,7 @@ import ( "v2ray.com/core/proxy/vmess" "v2ray.com/core/proxy/vmess/inbound" "v2ray.com/core/proxy/vmess/outbound" + "v2ray.com/core/testing/servers/tcp" . "v2ray.com/ext/assert" ) @@ -49,7 +50,7 @@ func TestVMessClosing(t *testing.T) { dest := net.DestinationFromAddr(tcpServer.Addr()) userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&policy.Config{ @@ -88,7 +89,7 @@ func TestVMessClosing(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&policy.Config{ diff --git a/testing/scenarios/shadowsocks_test.go b/testing/scenarios/shadowsocks_test.go index 7f827bb9b..74ed835be 100644 --- a/testing/scenarios/shadowsocks_test.go +++ b/testing/scenarios/shadowsocks_test.go @@ -41,7 +41,7 @@ func TestShadowsocksAES256TCP(t *testing.T) { Ota: shadowsocks.Account_Enabled, }) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -70,7 +70,7 @@ func TestShadowsocksAES256TCP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -159,7 +159,7 @@ func TestShadowsocksAES128UDP(t *testing.T) { Ota: shadowsocks.Account_Enabled, }) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -189,7 +189,7 @@ func TestShadowsocksAES128UDP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -278,7 +278,7 @@ func TestShadowsocksChacha20TCP(t *testing.T) { Ota: shadowsocks.Account_Enabled, }) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -307,7 +307,7 @@ func TestShadowsocksChacha20TCP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -395,7 +395,7 @@ func TestShadowsocksAES256GCMTCP(t *testing.T) { CipherType: shadowsocks.CipherType_AES_256_GCM, }) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -424,7 +424,7 @@ func TestShadowsocksAES256GCMTCP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -512,7 +512,7 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) { CipherType: shadowsocks.CipherType_AES_128_GCM, }) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -542,7 +542,7 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -630,7 +630,7 @@ func TestShadowsocksAES256GCMConformance(t *testing.T) { CipherType: shadowsocks.CipherType_AES_256_GCM, }) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -708,7 +708,7 @@ func TestShadowsocksChacha20Poly1305UDPConformance(t *testing.T) { CipherType: shadowsocks.CipherType_CHACHA20_POLY1305, }) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -791,7 +791,7 @@ func TestShadowsocksChacha20Conformance(t *testing.T) { Ota: shadowsocks.Account_Disabled, }) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ diff --git a/testing/scenarios/socks_test.go b/testing/scenarios/socks_test.go index 04ebe9660..4bbaddb27 100644 --- a/testing/scenarios/socks_test.go +++ b/testing/scenarios/socks_test.go @@ -28,7 +28,7 @@ func TestSocksBridgeTCP(t *testing.T) { assert(err, IsNil) defer tcpServer.Close() - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -53,7 +53,7 @@ func TestSocksBridgeTCP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -125,7 +125,7 @@ func TestSocksBridageUDP(t *testing.T) { assert(err, IsNil) defer udpServer.Close() - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -150,7 +150,7 @@ func TestSocksBridageUDP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -222,8 +222,8 @@ func TestSocksConformance(t *testing.T) { assert(err, IsNil) defer tcpServer.Close() - authPort := pickPort() - noAuthPort := pickPort() + authPort := tcp.PickPort() + noAuthPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { diff --git a/testing/scenarios/tls_test.go b/testing/scenarios/tls_test.go index c6d5505d4..392dbcdb5 100644 --- a/testing/scenarios/tls_test.go +++ b/testing/scenarios/tls_test.go @@ -36,7 +36,7 @@ func TestSimpleTLSConnection(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -70,7 +70,7 @@ func TestSimpleTLSConnection(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -185,7 +185,7 @@ func TestTLSOverKCP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -266,7 +266,7 @@ func TestTLSOverWebSocket(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -301,7 +301,7 @@ func TestTLSOverWebSocket(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { diff --git a/testing/scenarios/transport_test.go b/testing/scenarios/transport_test.go index 253cd2806..0ce1462c3 100644 --- a/testing/scenarios/transport_test.go +++ b/testing/scenarios/transport_test.go @@ -33,7 +33,7 @@ func TestHttpConnectionHeader(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { @@ -69,7 +69,7 @@ func TestHttpConnectionHeader(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ Inbound: []*core.InboundHandlerConfig{ { diff --git a/testing/scenarios/vmess_test.go b/testing/scenarios/vmess_test.go index c392d91f2..9b746baa9 100644 --- a/testing/scenarios/vmess_test.go +++ b/testing/scenarios/vmess_test.go @@ -36,7 +36,7 @@ func TestVMessDynamicPort(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -91,7 +91,7 @@ func TestVMessDynamicPort(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -171,7 +171,7 @@ func TestVMessGCM(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -204,7 +204,7 @@ func TestVMessGCM(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -294,7 +294,7 @@ func TestVMessGCMUDP(t *testing.T) { defer udpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -327,7 +327,7 @@ func TestVMessGCMUDP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -427,7 +427,7 @@ func TestVMessChacha20(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -460,7 +460,7 @@ func TestVMessChacha20(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -550,7 +550,7 @@ func TestVMessNone(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -583,7 +583,7 @@ func TestVMessNone(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -711,7 +711,7 @@ func TestVMessKCP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -808,7 +808,7 @@ func TestVMessIPv6(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -841,7 +841,7 @@ func TestVMessIPv6(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -923,7 +923,7 @@ func TestVMessGCMMux(t *testing.T) { defer tcpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -956,7 +956,7 @@ func TestVMessGCMMux(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -1065,7 +1065,7 @@ func TestVMessGCMMuxUDP(t *testing.T) { defer udpServer.Close() userID := protocol.NewID(uuid.New()) - serverPort := pickPort() + serverPort := tcp.PickPort() serverConfig := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(&log.Config{ @@ -1098,7 +1098,7 @@ func TestVMessGCMMuxUDP(t *testing.T) { }, } - clientPort := pickPort() + clientPort := tcp.PickPort() clientUDPPort := udp.PickPort() clientConfig := &core.Config{ App: []*serial.TypedMessage{ diff --git a/testing/servers/tcp/port.go b/testing/servers/tcp/port.go new file mode 100644 index 000000000..cf4729d2c --- /dev/null +++ b/testing/servers/tcp/port.go @@ -0,0 +1,16 @@ +package tcp + +import ( + "v2ray.com/core/common" + "v2ray.com/core/common/net" +) + +// PickPort returns an unused TCP port in the system. The port returned is highly likely to be unused, but not guaranteed. +func PickPort() net.Port { + listener, err := net.Listen("tcp4", "127.0.0.1:0") + common.Must(err) + defer listener.Close() + + addr := listener.Addr().(*net.TCPAddr) + return net.Port(addr.Port) +} diff --git a/testing/servers/udp/port.go b/testing/servers/udp/port.go index 0c77c4cff..75da5df44 100644 --- a/testing/servers/udp/port.go +++ b/testing/servers/udp/port.go @@ -5,6 +5,7 @@ import ( "v2ray.com/core/common/net" ) +// PickPort returns an unused UDP port in the system. The port returned is highly likely to be unused, but not guaranteed. func PickPort() net.Port { conn, err := net.ListenUDP("udp4", &net.UDPAddr{ IP: net.LocalHostIP.IP(), From d207d953bdb63ecb61c63c015c0870db833917aa Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 13:16:52 +0100 Subject: [PATCH 078/183] h2 transport --- common/interfaces.go | 13 +++ testing/tls/tls.go | 4 +- transport/internet/config.pb.go | 53 ++++----- transport/internet/config.proto | 1 + transport/internet/http/config.go | 12 ++ transport/internet/http/config.pb.go | 63 +++++++++++ transport/internet/http/config.proto | 12 ++ transport/internet/http/connection.go | 49 +++++++++ transport/internet/http/dialer.go | 115 ++++++++++++++++++++ transport/internet/http/errors.generated.go | 5 + transport/internet/http/http.go | 3 + transport/internet/http/http_test.go | 75 +++++++++++++ transport/internet/http/hub.go | 86 +++++++++++++++ transport/internet/tls/config.go | 4 +- 14 files changed, 467 insertions(+), 28 deletions(-) create mode 100644 transport/internet/http/config.go create mode 100644 transport/internet/http/config.pb.go create mode 100644 transport/internet/http/config.proto create mode 100644 transport/internet/http/connection.go create mode 100644 transport/internet/http/dialer.go create mode 100644 transport/internet/http/errors.generated.go create mode 100644 transport/internet/http/http.go create mode 100644 transport/internet/http/http_test.go create mode 100644 transport/internet/http/hub.go diff --git a/common/interfaces.go b/common/interfaces.go index f08235de5..962141784 100644 --- a/common/interfaces.go +++ b/common/interfaces.go @@ -27,3 +27,16 @@ type HasType interface { // Type returns the type of the object. Type() interface{} } + +type ChainedClosable []Closable + +func NewChainedClosable(c ...Closable) ChainedClosable { + return ChainedClosable(c) +} + +func (cc ChainedClosable) Close() error { + for _, c := range cc { + c.Close() + } + return nil +} diff --git a/testing/tls/tls.go b/testing/tls/tls.go index 22f81df93..7858e7333 100644 --- a/testing/tls/tls.go +++ b/testing/tls/tls.go @@ -29,12 +29,12 @@ func GenerateCertificateForTest() *v2tls.Certificate { Subject: pkix.Name{ Organization: []string{"V2Ray Inc"}, }, - NotBefore: time.Now(), + NotBefore: time.Now().Add(time.Hour * -1), NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, - DNSNames: []string{"www.v2ray.com"}, + DNSNames: []string{"www.v2ray.com", "v2ray.com"}, } derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) diff --git a/transport/internet/config.pb.go b/transport/internet/config.pb.go index ff89b9c73..129ad2083 100644 --- a/transport/internet/config.pb.go +++ b/transport/internet/config.pb.go @@ -23,6 +23,7 @@ const ( TransportProtocol_UDP TransportProtocol = 1 TransportProtocol_MKCP TransportProtocol = 2 TransportProtocol_WebSocket TransportProtocol = 3 + TransportProtocol_HTTP TransportProtocol = 4 ) var TransportProtocol_name = map[int32]string{ @@ -30,12 +31,14 @@ var TransportProtocol_name = map[int32]string{ 1: "UDP", 2: "MKCP", 3: "WebSocket", + 4: "HTTP", } var TransportProtocol_value = map[string]int32{ "TCP": 0, "UDP": 1, "MKCP": 2, "WebSocket": 3, + "HTTP": 4, } func (x TransportProtocol) String() string { @@ -138,29 +141,29 @@ func init() { func init() { proto.RegisterFile("v2ray.com/core/transport/internet/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 374 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x91, 0x4d, 0x4b, 0xeb, 0x40, - 0x14, 0x86, 0x6f, 0x92, 0x72, 0x6f, 0x7a, 0xda, 0x5e, 0xd3, 0x59, 0x15, 0xa1, 0x58, 0x2b, 0x48, - 0x70, 0x31, 0x29, 0x71, 0xef, 0xa2, 0x71, 0x23, 0x5a, 0x0c, 0x49, 0x55, 0x10, 0xa4, 0xa4, 0xe3, - 0x18, 0x82, 0x4d, 0xa6, 0x4c, 0x46, 0x31, 0xbf, 0xc7, 0x9d, 0x7b, 0xff, 0x9f, 0xe4, 0x63, 0x86, - 0xa2, 0x50, 0xba, 0x71, 0x37, 0x64, 0xde, 0xf3, 0x9c, 0x27, 0xef, 0x00, 0x7e, 0x75, 0x79, 0x54, - 0x60, 0xc2, 0x52, 0x87, 0x30, 0x4e, 0x1d, 0xc1, 0xa3, 0x2c, 0x5f, 0x33, 0x2e, 0x9c, 0x24, 0x13, - 0x94, 0x67, 0x54, 0x38, 0x84, 0x65, 0x4f, 0x49, 0x8c, 0xd7, 0x9c, 0x09, 0x86, 0x86, 0x32, 0xcf, - 0x29, 0x56, 0x59, 0x2c, 0xb3, 0xfb, 0x93, 0x6f, 0x38, 0xc2, 0xd2, 0x94, 0x65, 0x4e, 0x4e, 0x79, - 0x12, 0xad, 0x1c, 0x51, 0xac, 0xe9, 0xe3, 0x22, 0xa5, 0x79, 0x1e, 0xc5, 0xb4, 0x06, 0x8e, 0xdf, - 0x35, 0xd8, 0x9b, 0x4b, 0x90, 0x57, 0xad, 0x42, 0x57, 0x60, 0x56, 0x97, 0x84, 0xad, 0x06, 0xda, - 0x48, 0xb3, 0xff, 0xbb, 0x13, 0xbc, 0x75, 0x2f, 0x56, 0x04, 0xbf, 0x99, 0x0b, 0x14, 0x01, 0x4d, - 0xc1, 0xcc, 0xa9, 0x10, 0x49, 0x16, 0xe7, 0x03, 0x7d, 0xa4, 0xd9, 0x1d, 0xf7, 0x78, 0x93, 0x56, - 0x2b, 0xe2, 0x5a, 0x11, 0xcf, 0x4b, 0xc5, 0x59, 0x6d, 0x18, 0xa8, 0xb9, 0xf1, 0xa7, 0x0e, 0xdd, - 0x50, 0x70, 0x1a, 0xa5, 0xbf, 0xa2, 0xf8, 0x00, 0x48, 0x4d, 0x2c, 0x36, 0x64, 0x0d, 0xbb, 0xe3, - 0xe2, 0x5d, 0xb9, 0xb5, 0x59, 0xd0, 0x57, 0x99, 0xb0, 0x01, 0xa1, 0x23, 0xe8, 0xe5, 0x94, 0xbc, - 0xf0, 0x44, 0x14, 0x8b, 0xf2, 0x0d, 0x06, 0xc6, 0x48, 0xb3, 0xdb, 0x41, 0x57, 0x7e, 0x2c, 0x7f, - 0x1a, 0x85, 0xd0, 0x57, 0x21, 0xa5, 0xd0, 0xaa, 0x14, 0x76, 0xed, 0xcb, 0x92, 0x00, 0xb9, 0x79, - 0x7c, 0x00, 0x1d, 0x9f, 0xb3, 0xb7, 0xa2, 0x69, 0xcd, 0x02, 0x43, 0x44, 0x71, 0x55, 0x58, 0x3b, - 0x28, 0x8f, 0x27, 0x67, 0xd0, 0xff, 0x51, 0x0c, 0xfa, 0x07, 0xc6, 0xdc, 0xf3, 0xad, 0x3f, 0xe5, - 0xe1, 0xe6, 0xdc, 0xb7, 0x34, 0x64, 0x42, 0x6b, 0x76, 0xe9, 0xf9, 0x96, 0x8e, 0x7a, 0xd0, 0xbe, - 0xa3, 0xcb, 0x90, 0x91, 0x67, 0x2a, 0x2c, 0x63, 0x7a, 0x0d, 0x87, 0x84, 0xa5, 0xdb, 0x2b, 0xf2, - 0xb5, 0x7b, 0x53, 0x9e, 0x3f, 0xf4, 0xe1, 0xad, 0x1b, 0x44, 0x05, 0xf6, 0xca, 0xac, 0x5a, 0x8d, - 0x2f, 0x9a, 0xfb, 0xe5, 0xdf, 0xea, 0x51, 0x4e, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x4a, - 0x68, 0x51, 0x19, 0x03, 0x00, 0x00, + // 379 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x91, 0x4f, 0x4b, 0xf3, 0x40, + 0x10, 0x87, 0xdf, 0x24, 0xe5, 0x35, 0x9d, 0xb6, 0x9a, 0xee, 0xa9, 0x08, 0xc5, 0x5a, 0x41, 0x82, + 0x87, 0x4d, 0x89, 0xdf, 0xa0, 0xf1, 0x50, 0xd1, 0x62, 0x48, 0xa2, 0x82, 0x20, 0x25, 0x5d, 0xd7, + 0x10, 0x6c, 0xb2, 0x65, 0xb3, 0x8a, 0xf9, 0x3c, 0xde, 0xbc, 0xfb, 0xfd, 0x24, 0xff, 0x96, 0xa2, + 0x50, 0x7a, 0xf1, 0x36, 0x64, 0x7e, 0xf3, 0xcc, 0x93, 0x59, 0xc0, 0x6f, 0x36, 0x0f, 0x73, 0x4c, + 0x58, 0x62, 0x11, 0xc6, 0xa9, 0x25, 0x78, 0x98, 0x66, 0x6b, 0xc6, 0x85, 0x15, 0xa7, 0x82, 0xf2, + 0x94, 0x0a, 0x8b, 0xb0, 0xf4, 0x39, 0x8e, 0xf0, 0x9a, 0x33, 0xc1, 0xd0, 0xb0, 0xc9, 0x73, 0x8a, + 0x65, 0x16, 0x37, 0xd9, 0xc3, 0xc9, 0x0f, 0x1c, 0x61, 0x49, 0xc2, 0x52, 0x2b, 0xa3, 0x3c, 0x0e, + 0x57, 0x96, 0xc8, 0xd7, 0xf4, 0x69, 0x91, 0xd0, 0x2c, 0x0b, 0x23, 0x5a, 0x01, 0xc7, 0x1f, 0x0a, + 0x1c, 0x04, 0x0d, 0xc8, 0x29, 0x57, 0xa1, 0x6b, 0xd0, 0xcb, 0x26, 0x61, 0xab, 0x81, 0x32, 0x52, + 0xcc, 0x7d, 0x7b, 0x82, 0xb7, 0xee, 0xc5, 0x92, 0xe0, 0xd6, 0x73, 0x9e, 0x24, 0xa0, 0x29, 0xe8, + 0x19, 0x15, 0x22, 0x4e, 0xa3, 0x6c, 0xa0, 0x8e, 0x14, 0xb3, 0x63, 0x9f, 0x6e, 0xd2, 0x2a, 0x45, + 0x5c, 0x29, 0xe2, 0xa0, 0x50, 0x9c, 0x57, 0x86, 0x9e, 0x9c, 0x1b, 0x7f, 0xa9, 0xd0, 0xf5, 0x05, + 0xa7, 0x61, 0xf2, 0x27, 0x8a, 0x8f, 0x80, 0xe4, 0xc4, 0x62, 0x43, 0x56, 0x33, 0x3b, 0x36, 0xde, + 0x95, 0x5b, 0x99, 0x79, 0x7d, 0x99, 0xf1, 0x6b, 0x10, 0x3a, 0x81, 0x5e, 0x46, 0xc9, 0x2b, 0x8f, + 0x45, 0xbe, 0x28, 0xde, 0x60, 0xa0, 0x8d, 0x14, 0xb3, 0xed, 0x75, 0x9b, 0x8f, 0xc5, 0x4f, 0x23, + 0x1f, 0xfa, 0x32, 0x24, 0x15, 0x5a, 0xa5, 0xc2, 0xae, 0xf7, 0x32, 0x1a, 0x40, 0xb3, 0x79, 0x7c, + 0x04, 0x1d, 0x97, 0xb3, 0xf7, 0xbc, 0xbe, 0x9a, 0x01, 0x9a, 0x08, 0xa3, 0xf2, 0x60, 0x6d, 0xaf, + 0x28, 0xcf, 0x66, 0xd0, 0xff, 0x75, 0x18, 0xb4, 0x07, 0x5a, 0xe0, 0xb8, 0xc6, 0xbf, 0xa2, 0xb8, + 0xbd, 0x70, 0x0d, 0x05, 0xe9, 0xd0, 0x9a, 0x5f, 0x39, 0xae, 0xa1, 0xa2, 0x1e, 0xb4, 0xef, 0xe9, + 0xd2, 0x67, 0xe4, 0x85, 0x0a, 0x43, 0x2b, 0x1a, 0xb3, 0x20, 0x70, 0x8d, 0xd6, 0xf4, 0x06, 0x8e, + 0x09, 0x4b, 0xb6, 0x1f, 0xcb, 0x55, 0x1e, 0xf4, 0xa6, 0xfe, 0x54, 0x87, 0x77, 0xb6, 0x17, 0xe6, + 0xd8, 0x29, 0xb2, 0x52, 0x02, 0x5f, 0xd6, 0xfd, 0xe5, 0xff, 0xf2, 0x79, 0xce, 0xbf, 0x03, 0x00, + 0x00, 0xff, 0xff, 0xf3, 0x7b, 0xd5, 0x57, 0x23, 0x03, 0x00, 0x00, } diff --git a/transport/internet/config.proto b/transport/internet/config.proto index ad4f34093..b6b8a7983 100644 --- a/transport/internet/config.proto +++ b/transport/internet/config.proto @@ -13,6 +13,7 @@ enum TransportProtocol { UDP = 1; MKCP = 2; WebSocket = 3; + HTTP = 4; } message TransportConfig { diff --git a/transport/internet/http/config.go b/transport/internet/http/config.go new file mode 100644 index 000000000..7bdb71fcd --- /dev/null +++ b/transport/internet/http/config.go @@ -0,0 +1,12 @@ +package http + +import ( + "v2ray.com/core/common" + "v2ray.com/core/transport/internet" +) + +func init() { + common.Must(internet.RegisterProtocolConfigCreator(internet.TransportProtocol_HTTP, func() interface{} { + return new(Config) + })) +} diff --git a/transport/internet/http/config.pb.go b/transport/internet/http/config.pb.go new file mode 100644 index 000000000..2ba5cd9bf --- /dev/null +++ b/transport/internet/http/config.pb.go @@ -0,0 +1,63 @@ +package http + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Config struct { + Host []string `protobuf:"bytes,1,rep,name=host" json:"host,omitempty"` + Path string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"` +} + +func (m *Config) Reset() { *m = Config{} } +func (m *Config) String() string { return proto.CompactTextString(m) } +func (*Config) ProtoMessage() {} +func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Config) GetHost() []string { + if m != nil { + return m.Host + } + return nil +} + +func (m *Config) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func init() { + proto.RegisterType((*Config)(nil), "v2ray.core.transport.internet.http.Config") +} + +func init() { + proto.RegisterFile("v2ray.com/core/transport/internet/http/config.proto", fileDescriptor0) +} + +var fileDescriptor0 = []byte{ + // 173 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x2e, 0x33, 0x2a, 0x4a, + 0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x2f, 0x29, 0x4a, 0xcc, 0x2b, + 0x2e, 0xc8, 0x2f, 0x2a, 0xd1, 0xcf, 0xcc, 0x2b, 0x49, 0x2d, 0xca, 0x4b, 0x2d, 0xd1, 0xcf, 0x28, + 0x29, 0x29, 0xd0, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, + 0x52, 0x82, 0x69, 0x2a, 0x4a, 0xd5, 0x83, 0x6b, 0xd0, 0x83, 0x69, 0xd0, 0x03, 0x69, 0x50, 0x32, + 0xe0, 0x62, 0x73, 0x06, 0xeb, 0x11, 0x12, 0xe2, 0x62, 0xc9, 0xc8, 0x2f, 0x2e, 0x91, 0x60, 0x54, + 0x60, 0xd6, 0xe0, 0x0c, 0x02, 0xb3, 0x41, 0x62, 0x05, 0x89, 0x25, 0x19, 0x12, 0x4c, 0x0a, 0x8c, + 0x20, 0x31, 0x10, 0xdb, 0x29, 0x94, 0x4b, 0x2d, 0x39, 0x3f, 0x57, 0x8f, 0xb0, 0xd9, 0x01, 0x8c, + 0x51, 0x2c, 0x20, 0x7a, 0x15, 0x93, 0x52, 0x98, 0x51, 0x50, 0x62, 0xa5, 0x9e, 0x33, 0x48, 0x71, + 0x08, 0x5c, 0xb1, 0x27, 0x4c, 0xb1, 0x47, 0x49, 0x49, 0x41, 0x12, 0x1b, 0xd8, 0xcd, 0xc6, 0x80, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xdc, 0xf2, 0x95, 0x63, 0xea, 0x00, 0x00, 0x00, +} diff --git a/transport/internet/http/config.proto b/transport/internet/http/config.proto new file mode 100644 index 000000000..cb995c13d --- /dev/null +++ b/transport/internet/http/config.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package v2ray.core.transport.internet.http; +option csharp_namespace = "V2Ray.Core.Transport.Internet.Http"; +option go_package = "http"; +option java_package = "com.v2ray.core.transport.internet.http"; +option java_multiple_files = true; + +message Config { + repeated string host = 1; + string path = 2; +} diff --git a/transport/internet/http/connection.go b/transport/internet/http/connection.go new file mode 100644 index 000000000..8f6808672 --- /dev/null +++ b/transport/internet/http/connection.go @@ -0,0 +1,49 @@ +package http + +import ( + "io" + "time" + + "v2ray.com/core/common" + "v2ray.com/core/common/net" +) + +type Connection struct { + Reader io.Reader + Writer io.Writer + Closer common.Closable + Local net.Addr + Remote net.Addr +} + +func (c *Connection) Read(b []byte) (int, error) { + return c.Reader.Read(b) +} + +func (c *Connection) Write(b []byte) (int, error) { + return c.Writer.Write(b) +} + +func (c *Connection) Close() error { + return c.Closer.Close() +} + +func (c *Connection) LocalAddr() net.Addr { + return c.Local +} + +func (c *Connection) RemoteAddr() net.Addr { + return c.Remote +} + +func (c *Connection) SetDeadline(t time.Time) error { + return nil +} + +func (c *Connection) SetReadDeadline(t time.Time) error { + return nil +} + +func (c *Connection) SetWriteDeadline(t time.Time) error { + return nil +} diff --git a/transport/internet/http/dialer.go b/transport/internet/http/dialer.go new file mode 100644 index 000000000..0d029853a --- /dev/null +++ b/transport/internet/http/dialer.go @@ -0,0 +1,115 @@ +package http + +import ( + "context" + gotls "crypto/tls" + "io" + "net/http" + "net/url" + "sync" + + "golang.org/x/net/http2" + + "v2ray.com/core/common" + "v2ray.com/core/common/net" + "v2ray.com/core/transport/internet" + "v2ray.com/core/transport/internet/tls" +) + +var ( + globalDialerMap = make(map[net.Destination]*http.Client) + globalDailerAccess sync.Mutex +) + +func getHTTPClient(ctx context.Context, dest net.Destination) (*http.Client, error) { + globalDailerAccess.Lock() + defer globalDailerAccess.Unlock() + + if client, found := globalDialerMap[dest]; found { + return client, nil + } + + config := tls.ConfigFromContext(ctx) + if config == nil { + return nil, newError("TLS must be enabled for http transport.").AtWarning() + } + + transport := &http2.Transport{ + DialTLS: func(network string, addr string, tlsConfig *gotls.Config) (net.Conn, error) { + rawHost, rawPort, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + if len(rawPort) == 0 { + rawPort = "443" + } + port, err := net.PortFromString(rawPort) + if err != nil { + return nil, err + } + address := net.ParseAddress(rawHost) + + pconn, err := internet.DialSystem(context.Background(), nil, net.TCPDestination(address, port)) + if err != nil { + return nil, err + } + return gotls.Client(pconn, tlsConfig), nil + }, + TLSClientConfig: config.GetTLSConfig(tls.WithDestination(dest), tls.WithNextProto("h2")), + } + + client := &http.Client{ + Transport: transport, + } + + globalDialerMap[dest] = client + return client, nil +} + +// Dial dials a new TCP connection to the given destination. +func Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) { + client, err := getHTTPClient(ctx, dest) + if err != nil { + return nil, err + } + + preader, pwriter := io.Pipe() + request := &http.Request{ + Method: "PUT", + Host: "www.v2ray.com", + Body: preader, + URL: &url.URL{ + Scheme: "https", + Host: dest.NetAddr(), + Path: "/", + }, + Proto: "HTTP/2", + ProtoMajor: 2, + ProtoMinor: 0, + } + response, err := client.Do(request) + if err != nil { + return nil, newError("failed to dial to ", dest).Base(err).AtWarning() + } + if response.StatusCode != 200 { + return nil, newError("unexpected status", response.StatusCode).AtWarning() + } + + return &Connection{ + Reader: response.Body, + Writer: pwriter, + Closer: common.NewChainedClosable(preader, pwriter, response.Body), + Local: &net.TCPAddr{ + IP: []byte{0, 0, 0, 0}, + Port: 0, + }, + Remote: &net.TCPAddr{ + IP: []byte{0, 0, 0, 0}, + Port: 0, + }, + }, nil +} + +func init() { + common.Must(internet.RegisterTransportDialer(internet.TransportProtocol_HTTP, Dial)) +} diff --git a/transport/internet/http/errors.generated.go b/transport/internet/http/errors.generated.go new file mode 100644 index 000000000..3d68c2c5c --- /dev/null +++ b/transport/internet/http/errors.generated.go @@ -0,0 +1,5 @@ +package http + +import "v2ray.com/core/common/errors" + +func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("Transport", "Internet", "HTTP") } diff --git a/transport/internet/http/http.go b/transport/internet/http/http.go new file mode 100644 index 000000000..e532ed9c1 --- /dev/null +++ b/transport/internet/http/http.go @@ -0,0 +1,3 @@ +package http + +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg http -path Transport,Internet,HTTP diff --git a/transport/internet/http/http_test.go b/transport/internet/http/http_test.go new file mode 100644 index 000000000..03b38dda1 --- /dev/null +++ b/transport/internet/http/http_test.go @@ -0,0 +1,75 @@ +package http_test + +import ( + "context" + "crypto/rand" + "testing" + "time" + + "v2ray.com/core/common" + "v2ray.com/core/common/buf" + "v2ray.com/core/common/net" + "v2ray.com/core/testing/servers/tcp" + tlsgen "v2ray.com/core/testing/tls" + "v2ray.com/core/transport/internet" + . "v2ray.com/core/transport/internet/http" + "v2ray.com/core/transport/internet/tls" + . "v2ray.com/ext/assert" +) + +func TestHTTPConnection(t *testing.T) { + assert := With(t) + + port := tcp.PickPort() + + listener, err := Listen(internet.ContextWithSecuritySettings(context.Background(), &tls.Config{ + Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()}, + }), net.LocalHostIP, port, func(conn internet.Connection) { + go func() { + defer conn.Close() + + b := buf.New() + defer b.Release() + + for { + if err := b.Reset(buf.ReadFrom(conn)); err != nil { + return + } + nBytes, err := conn.Write(b.Bytes()) + assert(err, IsNil) + assert(nBytes, Equals, b.Len()) + } + }() + }) + assert(err, IsNil) + + defer listener.Close() + + time.Sleep(time.Second) + + conn, err := Dial(internet.ContextWithSecuritySettings(context.Background(), &tls.Config{ + ServerName: "www.v2ray.com", + AllowInsecure: true, + }), net.TCPDestination(net.LocalHostIP, port)) + assert(err, IsNil) + defer conn.Close() + + const N = 1024 + b1 := make([]byte, N) + common.Must2(rand.Read(b1)) + b2 := buf.New() + + nBytes, err := conn.Write(b1) + assert(nBytes, Equals, N) + assert(err, IsNil) + + assert(b2.Reset(buf.ReadFullFrom(conn, N)), IsNil) + assert(b2.Bytes(), Equals, b1) + + nBytes, err = conn.Write(b1) + assert(nBytes, Equals, N) + assert(err, IsNil) + + assert(b2.Reset(buf.ReadFullFrom(conn, N)), IsNil) + assert(b2.Bytes(), Equals, b1) +} diff --git a/transport/internet/http/hub.go b/transport/internet/http/hub.go new file mode 100644 index 000000000..5005a22cb --- /dev/null +++ b/transport/internet/http/hub.go @@ -0,0 +1,86 @@ +package http + +import ( + "context" + "io" + "net/http" + + "v2ray.com/core/common" + "v2ray.com/core/common/net" + "v2ray.com/core/common/serial" + "v2ray.com/core/common/signal" + "v2ray.com/core/transport/internet" + "v2ray.com/core/transport/internet/tls" +) + +type Listener struct { + server *http.Server + handler internet.ConnHandler + local net.Addr +} + +func (l *Listener) Addr() net.Addr { + return l.local +} + +func (l *Listener) Close() error { + return l.server.Shutdown(context.Background()) +} + +type flushWriter struct { + w io.Writer +} + +func (fw flushWriter) Write(p []byte) (n int, err error) { + n, err = fw.w.Write(p) + if f, ok := fw.w.(http.Flusher); ok { + f.Flush() + } + return +} + +func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) { + writer.WriteHeader(200) + if f, ok := writer.(http.Flusher); ok { + f.Flush() + } + done := signal.NewDone() + l.handler(&Connection{ + Reader: request.Body, + Writer: flushWriter{writer}, + Closer: common.NewChainedClosable(request.Body, done), + Local: l.Addr(), + Remote: l.Addr(), + }) + <-done.C() +} + +func Listen(ctx context.Context, address net.Address, port net.Port, handler internet.ConnHandler) (internet.Listener, error) { + listener := &Listener{ + handler: handler, + local: &net.TCPAddr{ + IP: address.IP(), + Port: int(port), + }, + } + + config := tls.ConfigFromContext(ctx) + if config == nil { + return nil, newError("TLS must be enabled for http transport.").AtWarning() + } + + server := &http.Server{ + Addr: serial.Concat(address, ":", port), + TLSConfig: config.GetTLSConfig(tls.WithNextProto("h2")), + Handler: listener, + } + + listener.server = server + go server.ListenAndServeTLS("", "") + + return listener, nil +} + +func init() { + common.Must(internet.RegisterTransportListener(internet.TransportProtocol_HTTP, Listen)) +} diff --git a/transport/internet/tls/config.go b/transport/internet/tls/config.go index 7172df527..eb4bc34fa 100644 --- a/transport/internet/tls/config.go +++ b/transport/internet/tls/config.go @@ -28,7 +28,6 @@ func (c *Config) BuildCertificates() []tls.Certificate { func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { config := &tls.Config{ ClientSessionCache: globalSessionCache, - NextProtos: []string{"http/1.1"}, } if c == nil { return config @@ -47,6 +46,9 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config { if len(c.NextProtocol) > 0 { config.NextProtos = c.NextProtocol } + if len(config.NextProtos) == 0 { + config.NextProtos = []string{"http/1.1"} + } return config } From e5d3783ed7dd5d50cf714e298f9cbad6f206f576 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 14:22:33 +0100 Subject: [PATCH 079/183] consume config in dialer and hub --- transport/internet/http/config.go | 33 ++++++++++++++++++++++++++++ transport/internet/http/dialer.go | 10 +++++++-- transport/internet/http/http_test.go | 15 +++++++++---- transport/internet/http/hub.go | 21 ++++++++++++++++++ 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/transport/internet/http/config.go b/transport/internet/http/config.go index 7bdb71fcd..a746b595d 100644 --- a/transport/internet/http/config.go +++ b/transport/internet/http/config.go @@ -2,9 +2,42 @@ package http import ( "v2ray.com/core/common" + "v2ray.com/core/common/dice" "v2ray.com/core/transport/internet" ) +func (c *Config) getHosts() []string { + if len(c.Host) == 0 { + return []string{"www.example.com"} + } + return c.Host +} + +func (c *Config) isValidHost(host string) bool { + hosts := c.getHosts() + for _, h := range hosts { + if h == host { + return true + } + } + return false +} + +func (c *Config) getRandomHost() string { + hosts := c.getHosts() + return hosts[dice.Roll(len(hosts))] +} + +func (c *Config) getNormalizedPath() string { + if len(c.Path) == 0 { + return "/" + } + if c.Path[0] != '/' { + return "/" + c.Path + } + return c.Path +} + func init() { common.Must(internet.RegisterProtocolConfigCreator(internet.TransportProtocol_HTTP, func() interface{} { return new(Config) diff --git a/transport/internet/http/dialer.go b/transport/internet/http/dialer.go index 0d029853a..025e314de 100644 --- a/transport/internet/http/dialer.go +++ b/transport/internet/http/dialer.go @@ -68,6 +68,12 @@ func getHTTPClient(ctx context.Context, dest net.Destination) (*http.Client, err // Dial dials a new TCP connection to the given destination. func Dial(ctx context.Context, dest net.Destination) (internet.Connection, error) { + rawSettings := internet.TransportSettingsFromContext(ctx) + httpSettings, ok := rawSettings.(*Config) + if !ok { + return nil, newError("HTTP config is not set.").AtError() + } + client, err := getHTTPClient(ctx, dest) if err != nil { return nil, err @@ -76,12 +82,12 @@ func Dial(ctx context.Context, dest net.Destination) (internet.Connection, error preader, pwriter := io.Pipe() request := &http.Request{ Method: "PUT", - Host: "www.v2ray.com", + Host: httpSettings.getRandomHost(), Body: preader, URL: &url.URL{ Scheme: "https", Host: dest.NetAddr(), - Path: "/", + Path: httpSettings.getNormalizedPath(), }, Proto: "HTTP/2", ProtoMajor: 2, diff --git a/transport/internet/http/http_test.go b/transport/internet/http/http_test.go index 03b38dda1..acbe62128 100644 --- a/transport/internet/http/http_test.go +++ b/transport/internet/http/http_test.go @@ -22,9 +22,13 @@ func TestHTTPConnection(t *testing.T) { port := tcp.PickPort() - listener, err := Listen(internet.ContextWithSecuritySettings(context.Background(), &tls.Config{ + lctx := context.Background() + lctx = internet.ContextWithSecuritySettings(lctx, &tls.Config{ Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()}, - }), net.LocalHostIP, port, func(conn internet.Connection) { + }) + lctx = internet.ContextWithTransportSettings(lctx, &Config{}) + + listener, err := Listen(lctx, net.LocalHostIP, port, func(conn internet.Connection) { go func() { defer conn.Close() @@ -47,10 +51,13 @@ func TestHTTPConnection(t *testing.T) { time.Sleep(time.Second) - conn, err := Dial(internet.ContextWithSecuritySettings(context.Background(), &tls.Config{ + dctx := context.Background() + dctx = internet.ContextWithSecuritySettings(dctx, &tls.Config{ ServerName: "www.v2ray.com", AllowInsecure: true, - }), net.TCPDestination(net.LocalHostIP, port)) + }) + dctx = internet.ContextWithTransportSettings(dctx, &Config{}) + conn, err := Dial(dctx, net.TCPDestination(net.LocalHostIP, port)) assert(err, IsNil) defer conn.Close() diff --git a/transport/internet/http/hub.go b/transport/internet/http/hub.go index 5005a22cb..250ccb337 100644 --- a/transport/internet/http/hub.go +++ b/transport/internet/http/hub.go @@ -4,6 +4,7 @@ import ( "context" "io" "net/http" + "strings" "v2ray.com/core/common" "v2ray.com/core/common/net" @@ -17,6 +18,7 @@ type Listener struct { server *http.Server handler internet.ConnHandler local net.Addr + config Config } func (l *Listener) Addr() net.Addr { @@ -40,6 +42,18 @@ func (fw flushWriter) Write(p []byte) (n int, err error) { } func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) { + host := request.Host + if !l.config.isValidHost(host) { + writer.WriteHeader(404) + return + } + path := l.config.getNormalizedPath() + if !strings.HasPrefix(request.URL.Path, path) { + writer.WriteHeader(404) + return + } + + writer.Header().Set("Cache-Control", "no-store") writer.WriteHeader(200) if f, ok := writer.(http.Flusher); ok { f.Flush() @@ -56,12 +70,19 @@ func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) } func Listen(ctx context.Context, address net.Address, port net.Port, handler internet.ConnHandler) (internet.Listener, error) { + rawSettings := internet.TransportSettingsFromContext(ctx) + httpSettings, ok := rawSettings.(*Config) + if !ok { + return nil, newError("HTTP config is not set.").AtError() + } + listener := &Listener{ handler: handler, local: &net.TCPAddr{ IP: address.IP(), Port: int(port), }, + config: *httpSettings, } config := tls.ConfigFromContext(ctx) From 59958fed68a5bbacfd956d9da622c5be44d50c3a Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 14:58:13 +0100 Subject: [PATCH 080/183] refactor udp worker --- app/proxyman/inbound/worker.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/proxyman/inbound/worker.go b/app/proxyman/inbound/worker.go index afa09aa49..e431c732d 100644 --- a/app/proxyman/inbound/worker.go +++ b/app/proxyman/inbound/worker.go @@ -210,8 +210,10 @@ func (w *udpWorker) getConnection(id connID) (*udpConn, bool) { func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest net.Destination) { id := connID{ - src: source, - dest: originalDest, + src: source, + } + if originalDest.IsValid() { + id.dest = originalDest } conn, existing := w.getConnection(id) select { From c4dd86da8498894c5a26856b1f86a23579bb4c74 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 17:47:35 +0100 Subject: [PATCH 081/183] update geoip --- release/config/geoip.dat | Bin 4530870 -> 4615830 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/release/config/geoip.dat b/release/config/geoip.dat index 30544ba8dcb380b117b198db613e60c2db82d040..999c06b78aff1f6fdcecb9d5c887d7cdd1111617 100755 GIT binary patch delta 125701 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 delta 56550 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�dLmxG%?F@$04 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 From a4f6ee671868e130a02404b4d0655b5a0f2ef7f3 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 21:10:40 +0100 Subject: [PATCH 082/183] print version when starts --- v2ray.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2ray.go b/v2ray.go index aab56fe8d..ca2e4ec76 100644 --- a/v2ray.go +++ b/v2ray.go @@ -124,7 +124,7 @@ func (s *Instance) Start() error { } } - newError("V2Ray started").AtWarning().WriteToLog() + newError("V2Ray ", Version(), " started with id ", s.ID()).AtWarning().WriteToLog() return nil } From 8d679fb5c824103b9503d4d181473c125408e1a2 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 21:10:45 +0100 Subject: [PATCH 083/183] include h2 --- main/distro/all/all.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main/distro/all/all.go b/main/distro/all/all.go index 321c60150..880c94f57 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -30,6 +30,7 @@ import ( _ "v2ray.com/core/proxy/vmess/outbound" // Transports + _ "v2ray.com/core/transport/internet/http" _ "v2ray.com/core/transport/internet/kcp" _ "v2ray.com/core/transport/internet/tcp" _ "v2ray.com/core/transport/internet/tls" From ed5f0b95d611243c710ecb6c73f7893d3619b5ce Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 21:16:51 +0100 Subject: [PATCH 084/183] remove id in log --- v2ray.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2ray.go b/v2ray.go index ca2e4ec76..f2c782ba2 100644 --- a/v2ray.go +++ b/v2ray.go @@ -124,7 +124,7 @@ func (s *Instance) Start() error { } } - newError("V2Ray ", Version(), " started with id ", s.ID()).AtWarning().WriteToLog() + newError("V2Ray ", Version(), " started").AtWarning().WriteToLog() return nil } From 20be8aab613c65e8c36e47c30b39aa73fa0d3bed Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 21:22:53 +0100 Subject: [PATCH 085/183] test case for h2 transport --- testing/scenarios/tls_test.go | 136 ++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/testing/scenarios/tls_test.go b/testing/scenarios/tls_test.go index 392dbcdb5..39db3c2f8 100644 --- a/testing/scenarios/tls_test.go +++ b/testing/scenarios/tls_test.go @@ -20,6 +20,7 @@ import ( "v2ray.com/core/testing/servers/udp" tlsgen "v2ray.com/core/testing/tls" "v2ray.com/core/transport/internet" + "v2ray.com/core/transport/internet/http" "v2ray.com/core/transport/internet/tls" "v2ray.com/core/transport/internet/websocket" . "v2ray.com/ext/assert" @@ -377,3 +378,138 @@ func TestTLSOverWebSocket(t *testing.T) { CloseAllServers(servers) } + +func TestHTTP2(t *testing.T) { + assert := With(t) + + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + assert(err, IsNil) + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + serverPort := tcp.PickPort() + serverConfig := &core.Config{ + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(serverPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_HTTP, + TransportSettings: []*internet.TransportConfig{ + { + Protocol: internet.TransportProtocol_HTTP, + Settings: serial.ToTypedMessage(&http.Config{ + Host: []string{"v2ray.com"}, + Path: "/testpath", + }), + }, + }, + SecurityType: serial.GetMessageType(&tls.Config{}), + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + Certificate: []*tls.Certificate{tlsgen.GenerateCertificateForTest()}, + }), + }, + }, + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + }), + }, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + clientPort := tcp.PickPort() + clientConfig := &core.Config{ + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(clientPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + }), + }, + }, + }, + }, + }), + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + StreamSettings: &internet.StreamConfig{ + Protocol: internet.TransportProtocol_HTTP, + TransportSettings: []*internet.TransportConfig{ + { + Protocol: internet.TransportProtocol_HTTP, + Settings: serial.ToTypedMessage(&http.Config{ + Host: []string{"v2ray.com"}, + Path: "/testpath", + }), + }, + }, + SecurityType: serial.GetMessageType(&tls.Config{}), + SecuritySettings: []*serial.TypedMessage{ + serial.ToTypedMessage(&tls.Config{ + AllowInsecure: true, + }), + }, + }, + }), + }, + }, + } + + servers, err := InitializeServerConfigs(serverConfig, clientConfig) + assert(err, IsNil) + + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert(err, IsNil) + + payload := make([]byte, 10240*1024) + rand.Read(payload) + nBytes, err := conn.Write([]byte(payload)) + assert(err, IsNil) + assert(nBytes, Equals, len(payload)) + + response := readFrom(conn, time.Second*20, len(payload)) + assert(response, Equals, xor([]byte(payload))) + assert(conn.Close(), IsNil) + + CloseAllServers(servers) +} From 2e7944a9eb31ed067ddbb2182deb612e50900397 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 21:42:42 +0100 Subject: [PATCH 086/183] concurrent connection in h2 --- testing/scenarios/tls_test.go | 36 ++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/testing/scenarios/tls_test.go b/testing/scenarios/tls_test.go index 39db3c2f8..e8eb69e3b 100644 --- a/testing/scenarios/tls_test.go +++ b/testing/scenarios/tls_test.go @@ -2,6 +2,7 @@ package scenarios import ( "crypto/rand" + "sync" "testing" "time" @@ -495,21 +496,30 @@ func TestHTTP2(t *testing.T) { servers, err := InitializeServerConfigs(serverConfig, clientConfig) assert(err, IsNil) - conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ - IP: []byte{127, 0, 0, 1}, - Port: int(clientPort), - }) - assert(err, IsNil) + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() - payload := make([]byte, 10240*1024) - rand.Read(payload) - nBytes, err := conn.Write([]byte(payload)) - assert(err, IsNil) - assert(nBytes, Equals, len(payload)) + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert(err, IsNil) - response := readFrom(conn, time.Second*20, len(payload)) - assert(response, Equals, xor([]byte(payload))) - assert(conn.Close(), IsNil) + payload := make([]byte, 10240*1024) + rand.Read(payload) + nBytes, err := conn.Write([]byte(payload)) + assert(err, IsNil) + assert(nBytes, Equals, len(payload)) + + response := readFrom(conn, time.Second*20, len(payload)) + assert(response, Equals, xor([]byte(payload))) + assert(conn.Close(), IsNil) + }() + } + wg.Wait() CloseAllServers(servers) } From 8c499d8fd2d412185b39d53c0707d13b7be44380 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 1 Mar 2018 22:14:07 +0100 Subject: [PATCH 087/183] Update version --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index fc709468e..31a2ce14f 100644 --- a/core.go +++ b/core.go @@ -16,7 +16,7 @@ import ( ) var ( - version = "3.11" + version = "3.12" build = "Custom" codename = "die Commanderin" intro = "An unified platform for anti-censorship." From 7b28be596ddc88752ee5ba93c1d1023ea2aa98ca Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 2 Mar 2018 08:26:14 +0000 Subject: [PATCH 088/183] fix typo --- transport/internet/websocket/config.go | 2 +- transport/internet/websocket/dialer.go | 2 +- transport/internet/websocket/hub.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/transport/internet/websocket/config.go b/transport/internet/websocket/config.go index 4469a83a1..ef55adda3 100644 --- a/transport/internet/websocket/config.go +++ b/transport/internet/websocket/config.go @@ -7,7 +7,7 @@ import ( "v2ray.com/core/transport/internet" ) -func (c *Config) GetNormailzedPath() string { +func (c *Config) GetNormalizedPath() string { path := c.Path if len(path) == 0 { return "/" diff --git a/transport/internet/websocket/dialer.go b/transport/internet/websocket/dialer.go index 8a1b0c713..040c98791 100644 --- a/transport/internet/websocket/dialer.go +++ b/transport/internet/websocket/dialer.go @@ -50,7 +50,7 @@ func dialWebsocket(ctx context.Context, dest net.Destination) (net.Conn, error) if (protocol == "ws" && dest.Port == 80) || (protocol == "wss" && dest.Port == 443) { host = dest.Address.String() } - uri := protocol + "://" + host + wsSettings.GetNormailzedPath() + uri := protocol + "://" + host + wsSettings.GetNormalizedPath() conn, resp, err := dialer.Dial(uri, wsSettings.GetRequestHeader()) if err != nil { diff --git a/transport/internet/websocket/hub.go b/transport/internet/websocket/hub.go index 36275fe2f..f0a39fc20 100644 --- a/transport/internet/websocket/hub.go +++ b/transport/internet/websocket/hub.go @@ -92,7 +92,7 @@ func (ln *Listener) listenws(address net.Address, port net.Port) error { go func() { err := http.Serve(listener, &requestHandler{ - path: ln.config.GetNormailzedPath(), + path: ln.config.GetNormalizedPath(), ln: ln, }) if err != nil { From 6b4272d8d28efcacd6b304865f075ddd0f24176d Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 2 Mar 2018 21:13:05 +0100 Subject: [PATCH 089/183] update release key. fixes #907 --- release/verify/official_release.asc | 76 ++++++++++++++--------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/release/verify/official_release.asc b/release/verify/official_release.asc index 2f25b8da1..7a6f5253a 100644 --- a/release/verify/official_release.asc +++ b/release/verify/official_release.asc @@ -12,41 +12,41 @@ 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 -80yAOiqyRyAtruVaY72n -=CVru ------END PGP PUBLIC KEY BLOCK----- +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 +=xv/V +-----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file From a52eb8f82b864fd78a75754e9ae90786adaf8e47 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 4 Mar 2018 17:08:58 +0100 Subject: [PATCH 090/183] allow underscore in domain name. fixes #917 --- common/protocol/address.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/protocol/address.go b/common/protocol/address.go index a0ae63525..598352a05 100644 --- a/common/protocol/address.go +++ b/common/protocol/address.go @@ -61,7 +61,7 @@ func maybeIPPrefix(b byte) bool { func isValidDomain(d string) bool { for _, c := range d { - if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '.') { + if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '-' || c == '.' || c == '_') { return false } } From 9100a789147cc501151d9b390bdefa90fd60236a Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 4 Mar 2018 21:06:04 +0100 Subject: [PATCH 091/183] refactor --- common/protocol/address.go | 2 +- common/protocol/headers.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/protocol/address.go b/common/protocol/address.go index 598352a05..bdd2a2081 100644 --- a/common/protocol/address.go +++ b/common/protocol/address.go @@ -172,7 +172,7 @@ func (p *AddressParser) writeAddress(writer io.Writer, address net.Address) erro } case net.AddressFamilyDomain: domain := address.Domain() - if IsDomainTooLong(domain) { + if isDomainTooLong(domain) { return newError("Super long domain is not supported: ", domain) } if _, err := writer.Write([]byte{tb, byte(len(domain))}); err != nil { diff --git a/common/protocol/headers.go b/common/protocol/headers.go index f3c6e1538..5a3a16267 100644 --- a/common/protocol/headers.go +++ b/common/protocol/headers.go @@ -85,6 +85,6 @@ func (sc *SecurityConfig) GetSecurityType() SecurityType { return sc.Type } -func IsDomainTooLong(domain string) bool { +func isDomainTooLong(domain string) bool { return len(domain) > 256 } From 6e293f492cb89ae3ff76211cd593c124028d72d7 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 4 Mar 2018 22:38:05 +0100 Subject: [PATCH 092/183] verify command --- proxy/shadowsocks/protocol_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proxy/shadowsocks/protocol_test.go b/proxy/shadowsocks/protocol_test.go index f2cb3c0a3..6c28370f7 100644 --- a/proxy/shadowsocks/protocol_test.go +++ b/proxy/shadowsocks/protocol_test.go @@ -39,6 +39,7 @@ func TestUDPEncoding(t *testing.T) { assert(decodedData.Bytes(), Equals, data.Bytes()) assert(decodedRequest.Address, Equals, request.Address) assert(decodedRequest.Port, Equals, request.Port) + assert(decodedRequest.Command, Equals, request.Command) } func TestTCPRequest(t *testing.T) { @@ -118,6 +119,7 @@ func TestTCPRequest(t *testing.T) { assert(err, IsNil) assert(decodedRequest.Address, Equals, request.Address) assert(decodedRequest.Port, Equals, request.Port) + assert(decodedRequest.Command, Equals, request.Command) decodedData, err := reader.ReadMultiBuffer() assert(err, IsNil) From e1bdca446d1cf8092d7435cffed5672aa0c116aa Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 5 Mar 2018 21:15:41 +0100 Subject: [PATCH 093/183] fix context reference --- app/log/command/command.go | 3 ++- app/log/command/config.pb.go | 3 ++- app/proxyman/command/command.pb.go | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/log/command/command.go b/app/log/command/command.go index 7e7804996..4c6d7236a 100644 --- a/app/log/command/command.go +++ b/app/log/command/command.go @@ -3,7 +3,8 @@ package command //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg command -path App,Log,Command import ( - context "golang.org/x/net/context" + "context" + grpc "google.golang.org/grpc" "v2ray.com/core" diff --git a/app/log/command/config.pb.go b/app/log/command/config.pb.go index c7f0f5424..d0ee71eb5 100644 --- a/app/log/command/config.pb.go +++ b/app/log/command/config.pb.go @@ -5,7 +5,8 @@ import fmt "fmt" import math "math" import ( - context "golang.org/x/net/context" + "context" + grpc "google.golang.org/grpc" ) diff --git a/app/proxyman/command/command.pb.go b/app/proxyman/command/command.pb.go index fe0c1071d..0a3d92e92 100644 --- a/app/proxyman/command/command.pb.go +++ b/app/proxyman/command/command.pb.go @@ -8,7 +8,8 @@ import v2ray_core_common_serial "v2ray.com/core/common/serial" import v2ray_core "v2ray.com/core" import ( - context "golang.org/x/net/context" + "context" + grpc "google.golang.org/grpc" ) From 794dfd5bf31c0c44d7cb4613eded74ed01cae1c8 Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Tue, 6 Mar 2018 15:24:52 +0800 Subject: [PATCH 094/183] Dedup protocol registration --- transport/internet/config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/transport/internet/config.go b/transport/internet/config.go index 72d20f8e1..b97246fcd 100644 --- a/transport/internet/config.go +++ b/transport/internet/config.go @@ -10,7 +10,9 @@ var ( ) func RegisterProtocolConfigCreator(protocol TransportProtocol, creator ConfigCreator) error { - // TODO: check duplicate + if _, found := globalTransportConfigCreatorCache[protocol]; found { + return newError("protocol: " + TransportProtocol_name[int32(protocol)]+ " is already registered").AtError() + } globalTransportConfigCreatorCache[protocol] = creator return nil } From a1401e763229ca665bd3db8051504f16c3169c47 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 6 Mar 2018 10:59:37 +0100 Subject: [PATCH 095/183] fix compatibility with shadowrocket. fixes #920 --- proxy/vmess/encoding/server.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index 0642adb4b..438ca4600 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -102,7 +102,12 @@ func NewServerSession(validator protocol.UserValidator, sessionHistory *SessionH func parseSecurityType(b byte) protocol.SecurityType { if _, f := protocol.SecurityType_name[int32(b)]; f { - return protocol.SecurityType(b) + st := protocol.SecurityType(b) + // For backward compatibility. + if st == protocol.SecurityType_UNKNOWN { + st = protocol.SecurityType_LEGACY + } + return st } return protocol.SecurityType_UNKNOWN } From 4d1bb21055c8cb0972d1547bc08839bd0a85d6a7 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 6 Mar 2018 11:25:27 +0100 Subject: [PATCH 096/183] Update version --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index 31a2ce14f..13e21828d 100644 --- a/core.go +++ b/core.go @@ -16,7 +16,7 @@ import ( ) var ( - version = "3.12" + version = "3.13" build = "Custom" codename = "die Commanderin" intro = "An unified platform for anti-censorship." From 2dc6d29d9507e76f547e7c794d98b2727cd8817b Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Tue, 6 Mar 2018 22:31:31 +0800 Subject: [PATCH 097/183] Capture command output --- release/install-release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/install-release.sh b/release/install-release.sh index 0c0c71f4f..22c76d77d 100755 --- a/release/install-release.sh +++ b/release/install-release.sh @@ -206,7 +206,7 @@ startV2ray(){ copyFile() { NAME=$1 MANDATE=$2 - ERROR=`cp "/tmp/v2ray/v2ray-${NEW_VER}-linux-${VDIS}/${NAME}" "/usr/bin/v2ray/${NAME}"` + ERROR=`cp "/tmp/v2ray/v2ray-${NEW_VER}-linux-${VDIS}/${NAME}" "/usr/bin/v2ray/${NAME}" 2>&1` if [[ $? -ne 0 ]]; then colorEcho ${YELLOW} "${ERROR}" if [ "$MANDATE" = true ]; then From dde9aaa89214686d4055428f8a259e8d61a24a24 Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Tue, 6 Mar 2018 22:39:13 +0800 Subject: [PATCH 098/183] Fix typo --- release/install-release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/install-release.sh b/release/install-release.sh index 0c0c71f4f..38e3e05e6 100755 --- a/release/install-release.sh +++ b/release/install-release.sh @@ -251,7 +251,7 @@ installV2Ray(){ } -installInitScrip(){ +installInitScript(){ SYSTEMCTL_CMD=$(command -v systemctl) SERVICE_CMD=$(command -v service) @@ -389,7 +389,7 @@ main(){ stopV2ray fi installV2Ray - installInitScrip + installInitScript if [[ ${V2RAY_RUNNING} -eq 1 ]];then colorEcho ${BLUE} "Restarting V2Ray service." startV2ray From 0e12ffd9b63016b35a8e3c0ebc1d640c34632419 Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Tue, 6 Mar 2018 23:18:49 +0800 Subject: [PATCH 099/183] Fix typo --- release/install-release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/install-release.sh b/release/install-release.sh index c58b6d352..3803380f3 100755 --- a/release/install-release.sh +++ b/release/install-release.sh @@ -371,11 +371,11 @@ main(){ NEW_VER=`ls /tmp/v2ray |grep v2ray-v |cut -d "-" -f2` fi else - # dowload via network and extract + # download via network and extract installSoftware "curl" getVersion if [[ $? == 0 ]] && [[ "$FORCE" != "1" ]]; then - colorEcho ${GREEN} "Lastest version ${NEW_VER} is already installed." + colorEcho ${GREEN} "Latest version ${NEW_VER} is already installed." exit else colorEcho ${BLUE} "Installing V2Ray ${NEW_VER} on ${ARCH}" From 2c854057f7bce6e0d34b51c61d7d939cc4208f15 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 7 Mar 2018 22:47:18 +0100 Subject: [PATCH 100/183] test case for mux over shadowsocks --- testing/scenarios/shadowsocks_test.go | 124 ++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/testing/scenarios/shadowsocks_test.go b/testing/scenarios/shadowsocks_test.go index 74ed835be..c93d3923a 100644 --- a/testing/scenarios/shadowsocks_test.go +++ b/testing/scenarios/shadowsocks_test.go @@ -615,6 +615,130 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) { CloseAllServers(servers) } +func TestShadowsocksAES128GCMUDPMux(t *testing.T) { + assert := With(t) + + udpServer := udp.Server{ + MsgProcessor: xor, + } + dest, err := udpServer.Start() + assert(err, IsNil) + defer udpServer.Close() + + account := serial.ToTypedMessage(&shadowsocks.Account{ + Password: "shadowsocks-password", + CipherType: shadowsocks.CipherType_AES_128_GCM, + }) + + serverPort := tcp.PickPort() + serverConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: clog.Severity_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(serverPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{ + UdpEnabled: false, + User: &protocol.User{ + Account: account, + Level: 1, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + clientPort := tcp.PickPort() + clientConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&log.Config{ + ErrorLogLevel: clog.Severity_Debug, + ErrorLogType: log.LogType_Console, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(clientPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_UDP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ + MultiplexSettings: &proxyman.MultiplexingConfig{ + Enabled: true, + Concurrency: 8, + }, + }), + ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{ + Server: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: account, + }, + }, + }, + }, + }), + }, + }, + } + + servers, err := InitializeServerConfigs(serverConfig, clientConfig) + assert(err, IsNil) + + var wg sync.WaitGroup + wg.Add(10) + for i := 0; i < 10; i++ { + go func() { + conn, err := net.DialUDP("udp", nil, &net.UDPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert(err, IsNil) + + payload := make([]byte, 1024) + rand.Read(payload) + + nBytes, err := conn.Write([]byte(payload)) + assert(err, IsNil) + assert(nBytes, Equals, len(payload)) + + response := readFrom(conn, time.Second*5, 1024) + assert(response, Equals, xor([]byte(payload))) + assert(conn.Close(), IsNil) + wg.Done() + }() + } + wg.Wait() + + CloseAllServers(servers) +} + func TestShadowsocksAES256GCMConformance(t *testing.T) { assert := With(t) From 8864195b501e3e71303c9e3d1b0a5b3b3e9ed4de Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Thu, 8 Mar 2018 17:32:36 +0800 Subject: [PATCH 101/183] Fix executable typo --- common/platform/platform.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/platform/platform.go b/common/platform/platform.go index 16d146045..96fa7103a 100644 --- a/common/platform/platform.go +++ b/common/platform/platform.go @@ -53,7 +53,7 @@ func getExecutableDir() string { return filepath.Dir(exec) } -func getExecuableSubDir(dir string) func() string { +func getExecutableSubDir(dir string) func() string { return func() string { return filepath.Join(getExecutableDir(), dir) } @@ -67,7 +67,7 @@ func GetAssetLocation(file string) string { func GetPluginDirectory() string { const name = "v2ray.location.plugin" - pluginDir := EnvFlag{Name: name, AltName: NormalizeEnvName(name)}.GetValue(getExecuableSubDir("plugins")) + pluginDir := EnvFlag{Name: name, AltName: NormalizeEnvName(name)}.GetValue(getExecutableSubDir("plugins")) return pluginDir } From f17b865982ebed8be89daeb011cbe04e739cc1f4 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Mar 2018 21:07:01 +0100 Subject: [PATCH 102/183] gofmt --- transport/internet/config.go | 2 +- transport/internet/http/errors.generated.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/transport/internet/config.go b/transport/internet/config.go index b97246fcd..83f097549 100644 --- a/transport/internet/config.go +++ b/transport/internet/config.go @@ -11,7 +11,7 @@ var ( func RegisterProtocolConfigCreator(protocol TransportProtocol, creator ConfigCreator) error { if _, found := globalTransportConfigCreatorCache[protocol]; found { - return newError("protocol: " + TransportProtocol_name[int32(protocol)]+ " is already registered").AtError() + return newError("protocol ", TransportProtocol_name[int32(protocol)], " is already registered").AtError() } globalTransportConfigCreatorCache[protocol] = creator return nil diff --git a/transport/internet/http/errors.generated.go b/transport/internet/http/errors.generated.go index 3d68c2c5c..d89f957d7 100644 --- a/transport/internet/http/errors.generated.go +++ b/transport/internet/http/errors.generated.go @@ -2,4 +2,6 @@ package http import "v2ray.com/core/common/errors" -func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("Transport", "Internet", "HTTP") } +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).Path("Transport", "Internet", "HTTP") +} From fbc025869bd2c2f1355f8581f5944ab4bd6f3f61 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Mar 2018 21:21:50 +0100 Subject: [PATCH 103/183] fix lint errors --- app/commander/outbound.go | 2 -- common/protocol/address.go | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/commander/outbound.go b/app/commander/outbound.go index 92be34a9a..19b900108 100644 --- a/app/commander/outbound.go +++ b/app/commander/outbound.go @@ -76,8 +76,6 @@ func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) { co.listener.add(c) co.access.RUnlock() <-closeSignal.Wait() - - return } func (co *CommanderOutbound) Tag() string { diff --git a/common/protocol/address.go b/common/protocol/address.go index bdd2a2081..f7d322209 100644 --- a/common/protocol/address.go +++ b/common/protocol/address.go @@ -150,10 +150,8 @@ func (p *AddressParser) ReadAddressPort(buffer *buf.Buffer, input io.Reader) (ne } func (p *AddressParser) writePort(writer io.Writer, port net.Port) error { - if _, err := writer.Write(port.Bytes(nil)); err != nil { - return err - } - return nil + _, err := writer.Write(port.Bytes(nil)) + return err } func (p *AddressParser) writeAddress(writer io.Writer, address net.Address) error { From eaf043f1b38d332d2c2ad5f0221a48c6d676ac05 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Mar 2018 22:30:52 +0100 Subject: [PATCH 104/183] reduce memory usage of Buffer --- common/buf/buffer.go | 36 ++++++++++++++++++------------------ common/buf/buffer_pool.go | 28 +++++++++------------------- common/buf/buffer_test.go | 2 +- 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/common/buf/buffer.go b/common/buf/buffer.go index 2d6705e6b..ec2556ba1 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -13,10 +13,10 @@ type Supplier func([]byte) (int, error) // quickly. type Buffer struct { v []byte - pool Pool + pool *Pool - start int - end int + start int32 + end int32 } // Release recycles the buffer into an internal buffer pool. @@ -48,25 +48,25 @@ func (b *Buffer) AppendBytes(bytes ...byte) int { // Append appends a byte array to the end of the buffer. func (b *Buffer) Append(data []byte) int { nBytes := copy(b.v[b.end:], data) - b.end += nBytes + b.end += int32(nBytes) return nBytes } // AppendSupplier appends the content of a BytesWriter to the buffer. func (b *Buffer) AppendSupplier(writer Supplier) error { nBytes, err := writer(b.v[b.end:]) - b.end += nBytes + b.end += int32(nBytes) return err } // Byte returns the bytes at index. func (b *Buffer) Byte(index int) byte { - return b.v[b.start+index] + return b.v[b.start+int32(index)] } // SetByte sets the byte value at index. func (b *Buffer) SetByte(index int, value byte) { - b.v[b.start+index] = value + b.v[b.start+int32(index)] = value } // Bytes returns the content bytes of this Buffer. @@ -78,7 +78,7 @@ func (b *Buffer) Bytes() []byte { func (b *Buffer) Reset(writer Supplier) error { nBytes, err := writer(b.v) b.start = 0 - b.end = nBytes + b.end = int32(nBytes) return err } @@ -90,7 +90,7 @@ func (b *Buffer) BytesRange(from, to int) []byte { if to < 0 { to += b.Len() } - return b.v[b.start+from : b.start+to] + return b.v[b.start+int32(from) : b.start+int32(to)] } // BytesFrom returns a slice of this Buffer starting from the given position. @@ -98,7 +98,7 @@ func (b *Buffer) BytesFrom(from int) []byte { if from < 0 { from += b.Len() } - return b.v[b.start+from : b.end] + return b.v[b.start+int32(from) : b.end] } // BytesTo returns a slice of this Buffer from start to the given position. @@ -106,7 +106,7 @@ func (b *Buffer) BytesTo(to int) []byte { if to < 0 { to += b.Len() } - return b.v[b.start : b.start+to] + return b.v[b.start : b.start+int32(to)] } // Slice cuts the buffer at the given position. @@ -120,8 +120,8 @@ func (b *Buffer) Slice(from, to int) { if to < from { panic("Invalid slice") } - b.end = b.start + to - b.start += from + b.end = b.start + int32(to) + b.start += int32(from) } // SliceFrom cuts the buffer at the given position. @@ -129,7 +129,7 @@ func (b *Buffer) SliceFrom(from int) { if from < 0 { from += b.Len() } - b.start += from + b.start += int32(from) } // Len returns the length of the buffer content. @@ -137,7 +137,7 @@ func (b *Buffer) Len() int { if b == nil { return 0 } - return b.end - b.start + return int(b.end - b.start) } // IsEmpty returns true if the buffer is empty. @@ -147,13 +147,13 @@ func (b *Buffer) IsEmpty() bool { // IsFull returns true if the buffer has no more room to grow. func (b *Buffer) IsFull() bool { - return b.end == len(b.v) + return b.end == int32(len(b.v)) } // Write implements Write method in io.Writer. func (b *Buffer) Write(data []byte) (int, error) { nBytes := copy(b.v[b.end:], data) - b.end += nBytes + b.end += int32(nBytes) return nBytes, nil } @@ -166,7 +166,7 @@ func (b *Buffer) Read(data []byte) (int, error) { if nBytes == b.Len() { b.Clear() } else { - b.start += nBytes + b.start += int32(nBytes) } return nBytes, nil } diff --git a/common/buf/buffer_pool.go b/common/buf/buffer_pool.go index d66047305..7904ee9a7 100644 --- a/common/buf/buffer_pool.go +++ b/common/buf/buffer_pool.go @@ -5,21 +5,13 @@ import ( ) // Pool provides functionality to generate and recycle buffers on demand. -type Pool interface { - // Allocate either returns a unused buffer from the pool, or generates a new one from system. - Allocate() *Buffer - // Free recycles the given buffer. - Free(*Buffer) -} - -// SyncPool is a buffer pool based on sync.Pool -type SyncPool struct { +type Pool struct { allocator *sync.Pool } -// NewSyncPool creates a SyncPool with given buffer size. -func NewSyncPool(bufferSize uint32) *SyncPool { - pool := &SyncPool{ +// NewPool creates a SyncPool with given buffer size. +func NewPool(bufferSize uint32) *Pool { + pool := &Pool{ allocator: &sync.Pool{ New: func() interface{} { return make([]byte, bufferSize) }, }, @@ -27,16 +19,16 @@ func NewSyncPool(bufferSize uint32) *SyncPool { return pool } -// Allocate implements Pool.Allocate(). -func (p *SyncPool) Allocate() *Buffer { +// Allocate either returns a unused buffer from the pool, or generates a new one from system. +func (p *Pool) Allocate() *Buffer { return &Buffer{ v: p.allocator.Get().([]byte), pool: p, } } -// Free implements Pool.Free(). -func (p *SyncPool) Free(buffer *Buffer) { +// // Free recycles the given buffer. +func (p *Pool) Free(buffer *Buffer) { if buffer.v != nil { p.allocator.Put(buffer.v) } @@ -47,6 +39,4 @@ const ( Size = 2 * 1024 ) -var ( - mediumPool Pool = NewSyncPool(Size) -) +var mediumPool = NewPool(Size) diff --git a/common/buf/buffer_test.go b/common/buf/buffer_test.go index af05057ec..f7146b49f 100644 --- a/common/buf/buffer_test.go +++ b/common/buf/buffer_test.go @@ -58,7 +58,7 @@ func TestBufferWrite(t *testing.T) { func TestSyncPool(t *testing.T) { assert := With(t) - p := NewSyncPool(32) + p := NewPool(32) b := p.Allocate() assert(b.Len(), Equals, 0) From ed79ba6cba2210f443c956458b9129d3a566ea42 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Mar 2018 22:41:41 +0100 Subject: [PATCH 105/183] Update version --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index 13e21828d..f380edf28 100644 --- a/core.go +++ b/core.go @@ -16,7 +16,7 @@ import ( ) var ( - version = "3.13" + version = "3.14" build = "Custom" codename = "die Commanderin" intro = "An unified platform for anti-censorship." From 1179ecef27fd2abc1a1b797abc27f4596e31c52b Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 8 Mar 2018 23:40:02 +0100 Subject: [PATCH 106/183] block until rpc connection is ready --- testing/scenarios/command_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go index c3d0844b2..d3c0b10fb 100644 --- a/testing/scenarios/command_test.go +++ b/testing/scenarios/command_test.go @@ -116,7 +116,7 @@ func TestCommanderRemoveHandler(t *testing.T) { assert(conn.Close(), IsNil) } - cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure()) + cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure(), grpc.WithBlock()) assert(err, IsNil) hsClient := command.NewHandlerServiceClient(cmdConn) @@ -296,7 +296,7 @@ func TestCommanderAddRemoveUser(t *testing.T) { assert(conn.Close(), IsNil) } - cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure()) + cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure(), grpc.WithBlock()) assert(err, IsNil) hsClient := command.NewHandlerServiceClient(cmdConn) From fda85506c8c2f79adfc12fc5a9c15475eb97ca05 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 9 Mar 2018 11:26:00 +0100 Subject: [PATCH 107/183] reuse buffer --- proxy/blackhole/config.go | 2 +- proxy/shadowsocks/protocol.go | 4 ++-- proxy/socks/protocol.go | 19 ++++++++++++++----- proxy/vmess/encoding/commands.go | 2 +- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/proxy/blackhole/config.go b/proxy/blackhole/config.go index 5bfd3290e..89c8df438 100644 --- a/proxy/blackhole/config.go +++ b/proxy/blackhole/config.go @@ -27,7 +27,7 @@ func (*NoneResponse) WriteTo(buf.Writer) {} // WriteTo implements ResponseConfig.WriteTo(). func (*HTTPResponse) WriteTo(writer buf.Writer) { - b := buf.NewLocal(512) + b := buf.New() common.Must(b.AppendSupplier(serial.WriteString(http403response))) writer.WriteMultiBuffer(buf.NewMultiBufferValue(b)) } diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index 4ad68a884..a52e310f2 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -35,7 +35,7 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea } account := rawAccount.(*MemoryAccount) - buffer := buf.NewLocal(512) + buffer := buf.New() defer buffer.Release() ivLen := account.Cipher.IVSize() @@ -149,7 +149,7 @@ func WriteTCPRequest(request *protocol.RequestHeader, writer io.Writer) (buf.Wri return nil, newError("failed to create encoding stream").Base(err).AtError() } - header := buf.NewLocal(512) + header := buf.New() if err := addrParser.WriteAddressPort(header, request.Address, request.Port); err != nil { return nil, newError("failed to write address").Base(err) diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index 2090296a9..8aba9c5c9 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -46,7 +46,9 @@ type ServerSession struct { } func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) { - buffer := buf.NewLocal(512) + buffer := buf.New() + defer buffer.Release() + request := new(protocol.RequestHeader) if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 2)); err != nil { @@ -177,7 +179,7 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol } func readUsernamePassword(reader io.Reader) (string, string, error) { - buffer := buf.NewLocal(512) + buffer := buf.New() defer buffer.Release() if err := buffer.Reset(buf.ReadFullFrom(reader, 2)); err != nil { @@ -234,7 +236,9 @@ func writeSocks5AuthenticationResponse(writer io.Writer, version byte, auth byte } func writeSocks5Response(writer io.Writer, errCode byte, address net.Address, port net.Port) error { - buffer := buf.NewLocal(64) + buffer := buf.New() + defer buffer.Release() + buffer.AppendBytes(socks5Version, errCode, 0x00 /* reserved */) if err := addrParser.WriteAddressPort(buffer, address, port); err != nil { return err @@ -245,7 +249,9 @@ func writeSocks5Response(writer io.Writer, errCode byte, address net.Address, po } func writeSocks4Response(writer io.Writer, errCode byte, address net.Address, port net.Port) error { - buffer := buf.NewLocal(32) + buffer := buf.New() + defer buffer.Release() + buffer.AppendBytes(0x00, errCode) common.Must(buffer.AppendSupplier(serial.WriteUint16(port.Value()))) buffer.Append(address.IP()) @@ -282,6 +288,7 @@ func EncodeUDPPacket(request *protocol.RequestHeader, data []byte) (*buf.Buffer, b := buf.New() b.AppendBytes(0, 0, 0 /* Fragment */) if err := addrParser.WriteAddressPort(b, request.Address, request.Port); err != nil { + b.Release() return nil, err } b.Append(data) @@ -338,7 +345,9 @@ func ClientHandshake(request *protocol.RequestHeader, reader io.Reader, writer i authByte = byte(authPassword) } - b := buf.NewLocal(512) + b := buf.New() + defer b.Release() + b.AppendBytes(socks5Version, 0x01, authByte) if authByte == authPassword { rawAccount, err := request.User.GetTypedAccount() diff --git a/proxy/vmess/encoding/commands.go b/proxy/vmess/encoding/commands.go index eb25139a4..1b61f7773 100644 --- a/proxy/vmess/encoding/commands.go +++ b/proxy/vmess/encoding/commands.go @@ -32,7 +32,7 @@ func MarshalCommand(command interface{}, writer io.Writer) error { return ErrUnknownCommand } - buffer := buf.NewLocal(512) + buffer := buf.New() defer buffer.Release() err := factory.Marshal(command, buffer) From 649119493ba6295bc51a9e20aa12a73938664196 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 10 Mar 2018 09:34:38 +0100 Subject: [PATCH 108/183] remove pending request from cache when error. fixes #942 --- app/dns/nameserver.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index f3e19e040..88e9c26ab 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -177,6 +177,9 @@ func (s *UDPNameServer) QueryA(domain string) <-chan *ARecord { b, err := msgToBuffer(msg) if err != nil { newError("failed to build A query for domain ", domain).Base(err).WriteToLog() + s.Lock() + delete(s.requests, id) + s.Unlock() close(response) return response } From 87dd1ed8774494875223a68a4a5714ba66230be8 Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Sun, 11 Mar 2018 14:43:20 +0800 Subject: [PATCH 109/183] Fix receivced, InboundBound, tranport, Enpoint typo --- common/net/destination.go | 2 +- transport/ray/ray.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/net/destination.go b/common/net/destination.go index f7f5b720b..f1166f95f 100644 --- a/common/net/destination.go +++ b/common/net/destination.go @@ -56,7 +56,7 @@ func (d Destination) IsValid() bool { return d.Network != Network_Unknown } -// AsDestination converts current Enpoint into Destination. +// AsDestination converts current Endpoint into Destination. func (p *Endpoint) AsDestination() Destination { return Destination{ Network: p.Network, diff --git a/transport/ray/ray.go b/transport/ray/ray.go index 557bc64eb..0c779784c 100644 --- a/transport/ray/ray.go +++ b/transport/ray/ray.go @@ -13,7 +13,7 @@ type OutboundRay interface { // OutboundOutput provides a stream to retrieve the response from the // outbound connection. The outbound connection shall close the channel - // after all responses are receivced and put into the channel. + // after all responses are received and put into the channel. OutboundOutput() OutputStream } @@ -24,13 +24,13 @@ type InboundRay interface { // is received and put into the channel. InboundInput() OutputStream - // InboudBound provides a stream of data for the inbound connection to write + // InboundOutput provides a stream of data for the inbound connection to write // as response. The inbound connection shall write all the data from the // channel until it is closed. InboundOutput() InputStream } -// Ray is an internal tranport channel between inbound and outbound connection. +// Ray is an internal transport channel between inbound and outbound connection. type Ray interface { InboundRay OutboundRay From f97e6fa3d2bbff44c3ce2f3fbede887f4fe34f18 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 11 Mar 2018 23:06:04 +0100 Subject: [PATCH 110/183] refine buffer allocation --- app/proxyman/mux/reader.go | 2 +- common/buf/buffer.go | 51 +++++++++++++++++++++++++++++-------- common/buf/buffer_pool.go | 52 +++++++++++++++----------------------- common/buf/buffer_test.go | 27 -------------------- common/buf/reader.go | 10 +++++--- common/crypto/auth.go | 2 +- proxy/shadowsocks/ota.go | 2 +- 7 files changed, 72 insertions(+), 74 deletions(-) diff --git a/app/proxyman/mux/reader.go b/app/proxyman/mux/reader.go index 98b6e4400..f3a2574b6 100644 --- a/app/proxyman/mux/reader.go +++ b/app/proxyman/mux/reader.go @@ -55,7 +55,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if size <= buf.Size { b = buf.New() } else { - b = buf.NewLocal(int(size)) + b = buf.NewLocal(uint32(size)) } if err := b.AppendSupplier(buf.ReadFullFrom(r.reader, int(size))); err != nil { b.Release() diff --git a/common/buf/buffer.go b/common/buf/buffer.go index ec2556ba1..4128cb325 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -12,8 +12,7 @@ type Supplier func([]byte) (int, error) // the buffer into an internal buffer pool, in order to recreate a buffer more // quickly. type Buffer struct { - v []byte - pool *Pool + v []byte start int32 end int32 @@ -24,11 +23,8 @@ func (b *Buffer) Release() { if b == nil || b.v == nil { return } - if b.pool != nil { - b.pool.Free(b) - } + FreeBytes(b.v) b.v = nil - b.pool = nil b.start = 0 b.end = 0 } @@ -178,13 +174,48 @@ func (b *Buffer) String() string { // New creates a Buffer with 0 length and 8K capacity. func New() *Buffer { - return mediumPool.Allocate() + return &Buffer{ + v: pool2k.Get().([]byte), + } } // NewLocal creates and returns a buffer with 0 length and given capacity on current thread. -func NewLocal(size int) *Buffer { +func NewLocal(size uint32) *Buffer { return &Buffer{ - v: make([]byte, size), - pool: nil, + v: NewBytes(size), + } +} + +func NewBytes(size uint32) []byte { + if size > 128*1024 { + return make([]byte, size) + } + + if size > 64*1024 { + return pool128k.Get().([]byte) + } + + if size > 8*1024 { + return pool64k.Get().([]byte) + } + + if size > 2*1024 { + return pool8k.Get().([]byte) + } + + return pool2k.Get().([]byte) +} + +func FreeBytes(b []byte) { + size := len(b) + switch { + case size >= 128*1024: + pool128k.Put(b) + case size >= 64*1024: + pool64k.Put(b) + case size >= 8*1024: + pool8k.Put(b) + case size >= 2*1024: + pool2k.Put(b) } } diff --git a/common/buf/buffer_pool.go b/common/buf/buffer_pool.go index 7904ee9a7..92dcd34cd 100644 --- a/common/buf/buffer_pool.go +++ b/common/buf/buffer_pool.go @@ -4,39 +4,29 @@ import ( "sync" ) -// Pool provides functionality to generate and recycle buffers on demand. -type Pool struct { - allocator *sync.Pool -} - -// NewPool creates a SyncPool with given buffer size. -func NewPool(bufferSize uint32) *Pool { - pool := &Pool{ - allocator: &sync.Pool{ - New: func() interface{} { return make([]byte, bufferSize) }, - }, - } - return pool -} - -// Allocate either returns a unused buffer from the pool, or generates a new one from system. -func (p *Pool) Allocate() *Buffer { - return &Buffer{ - v: p.allocator.Get().([]byte), - pool: p, - } -} - -// // Free recycles the given buffer. -func (p *Pool) Free(buffer *Buffer) { - if buffer.v != nil { - p.allocator.Put(buffer.v) - } -} - const ( // Size of a regular buffer. Size = 2 * 1024 ) -var mediumPool = NewPool(Size) +func createAllocFunc(size uint32) func() interface{} { + return func() interface{} { + return make([]byte, size) + } +} + +var pool2k = &sync.Pool{ + New: createAllocFunc(2 * 1024), +} + +var pool8k = &sync.Pool{ + New: createAllocFunc(8 * 1024), +} + +var pool64k = &sync.Pool{ + New: createAllocFunc(64 * 1024), +} + +var pool128k = &sync.Pool{ + New: createAllocFunc(128 * 1024), +} diff --git a/common/buf/buffer_test.go b/common/buf/buffer_test.go index f7146b49f..be0ae42ef 100644 --- a/common/buf/buffer_test.go +++ b/common/buf/buffer_test.go @@ -1,7 +1,6 @@ package buf_test import ( - "crypto/rand" "testing" . "v2ray.com/core/common/buf" @@ -42,32 +41,6 @@ func TestBufferString(t *testing.T) { assert(buffer.String(), Equals, "Test String") } -func TestBufferWrite(t *testing.T) { - assert := With(t) - - buffer := NewLocal(8) - nBytes, err := buffer.Write([]byte("abcd")) - assert(err, IsNil) - assert(nBytes, Equals, 4) - nBytes, err = buffer.Write([]byte("abcde")) - assert(err, IsNil) - assert(nBytes, Equals, 4) - assert(buffer.String(), Equals, "abcdabcd") -} - -func TestSyncPool(t *testing.T) { - assert := With(t) - - p := NewPool(32) - b := p.Allocate() - assert(b.Len(), Equals, 0) - - assert(b.AppendSupplier(ReadFrom(rand.Reader)), IsNil) - assert(b.Len(), Equals, 32) - - b.Release() -} - func BenchmarkNewBuffer(b *testing.B) { for i := 0; i < b.N; i++ { buffer := New() diff --git a/common/buf/reader.go b/common/buf/reader.go index 45dc4ef32..f040a9371 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -21,12 +21,13 @@ func NewBytesToBufferReader(reader io.Reader) Reader { const mediumSize = 8 * 1024 const largeSize = 64 * 1024 +const xlSize = 128 * 1024 func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) { b := New() err := b.Reset(ReadFrom(r.Reader)) if b.IsFull() { - r.buffer = make([]byte, mediumSize) + r.buffer = NewBytes(mediumSize) } if !b.IsEmpty() { return NewMultiBufferValue(b), nil @@ -45,11 +46,14 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) { if nBytes > 0 { mb := NewMultiBufferCap(nBytes/Size + 1) mb.Write(r.buffer[:nBytes]) - if nBytes == len(r.buffer) && len(r.buffer) == mediumSize { - r.buffer = make([]byte, largeSize) + if nBytes == len(r.buffer) && nBytes < xlSize { + FreeBytes(r.buffer) + r.buffer = NewBytes(uint32(nBytes) + 1) } return mb, nil } + FreeBytes(r.buffer) + r.buffer = nil return nil, err } diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 2d591bfbd..e1bf8d0d9 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -128,7 +128,7 @@ func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if size <= buf.Size { b = buf.New() } else { - b = buf.NewLocal(size) + b = buf.NewLocal(uint32(size)) } if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil { b.Release() diff --git a/proxy/shadowsocks/ota.go b/proxy/shadowsocks/ota.go index 3d9396822..bca2eaca7 100644 --- a/proxy/shadowsocks/ota.go +++ b/proxy/shadowsocks/ota.go @@ -81,7 +81,7 @@ func (v *ChunkReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if length > buf.Size { // Theoretically the size of a chunk is 64K, but most Shadowsocks implementations used <4K buffer. buffer.Release() - buffer = buf.NewLocal(int(length) + 128) + buffer = buf.NewLocal(uint32(length) + 128) } buffer.Clear() From 994aecd13c9d578029a8dc8b5c5ad655d33d82ee Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 11 Mar 2018 23:29:17 +0100 Subject: [PATCH 111/183] rename NewLocal to NewSize --- app/proxyman/mux/reader.go | 2 +- common/buf/buffer.go | 6 +++--- common/buf/buffer_test.go | 2 +- common/crypto/auth.go | 2 +- common/crypto/auth_test.go | 6 +++--- common/crypto/chunk_test.go | 2 +- proxy/shadowsocks/ota.go | 2 +- proxy/shadowsocks/ota_test.go | 4 ++-- proxy/shadowsocks/protocol_test.go | 2 +- transport/internet/headers/http/http_test.go | 2 +- transport/internet/headers/srtp/srtp_test.go | 2 +- transport/internet/headers/utp/utp_test.go | 2 +- transport/internet/headers/wechat/wechat_test.go | 2 +- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/proxyman/mux/reader.go b/app/proxyman/mux/reader.go index f3a2574b6..33453b715 100644 --- a/app/proxyman/mux/reader.go +++ b/app/proxyman/mux/reader.go @@ -55,7 +55,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if size <= buf.Size { b = buf.New() } else { - b = buf.NewLocal(uint32(size)) + b = buf.NewSize(uint32(size)) } if err := b.AppendSupplier(buf.ReadFullFrom(r.reader, int(size))); err != nil { b.Release() diff --git a/common/buf/buffer.go b/common/buf/buffer.go index 4128cb325..1b94936ff 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -172,15 +172,15 @@ func (b *Buffer) String() string { return string(b.Bytes()) } -// New creates a Buffer with 0 length and 8K capacity. +// New creates a Buffer with 0 length and 2K capacity. func New() *Buffer { return &Buffer{ v: pool2k.Get().([]byte), } } -// NewLocal creates and returns a buffer with 0 length and given capacity on current thread. -func NewLocal(size uint32) *Buffer { +// NewSize creates and returns a buffer given capacity. +func NewSize(size uint32) *Buffer { return &Buffer{ v: NewBytes(size), } diff --git a/common/buf/buffer_test.go b/common/buf/buffer_test.go index be0ae42ef..a51687d64 100644 --- a/common/buf/buffer_test.go +++ b/common/buf/buffer_test.go @@ -50,7 +50,7 @@ func BenchmarkNewBuffer(b *testing.B) { func BenchmarkNewLocalBuffer(b *testing.B) { for i := 0; i < b.N; i++ { - buffer := NewLocal(Size) + buffer := NewSize(Size) buffer.Release() } } diff --git a/common/crypto/auth.go b/common/crypto/auth.go index e1bf8d0d9..ecdb0189f 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -128,7 +128,7 @@ func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if size <= buf.Size { b = buf.New() } else { - b = buf.NewLocal(uint32(size)) + b = buf.NewSize(uint32(size)) } if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil { b.Release() diff --git a/common/crypto/auth_test.go b/common/crypto/auth_test.go index 3bc35fa3f..ada4181c5 100644 --- a/common/crypto/auth_test.go +++ b/common/crypto/auth_test.go @@ -27,10 +27,10 @@ func TestAuthenticationReaderWriter(t *testing.T) { rawPayload := make([]byte, 8192*10) rand.Read(rawPayload) - payload := buf.NewLocal(8192 * 10) + payload := buf.NewSize(8192 * 10) payload.Append(rawPayload) - cache := buf.NewLocal(160 * 1024) + cache := buf.NewSize(160 * 1024) iv := make([]byte, 12) rand.Read(iv) @@ -83,7 +83,7 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) { aead, err := cipher.NewGCM(block) assert(err, IsNil) - cache := buf.NewLocal(1024) + cache := buf.NewSize(1024) iv := make([]byte, 12) rand.Read(iv) diff --git a/common/crypto/chunk_test.go b/common/crypto/chunk_test.go index 4131ebfb2..d5d8c9e5b 100644 --- a/common/crypto/chunk_test.go +++ b/common/crypto/chunk_test.go @@ -12,7 +12,7 @@ import ( func TestChunkStreamIO(t *testing.T) { assert := With(t) - cache := buf.NewLocal(8192) + cache := buf.NewSize(8192) writer := NewChunkStreamWriter(PlainChunkSizeParser{}, cache) reader := NewChunkStreamReader(PlainChunkSizeParser{}, cache) diff --git a/proxy/shadowsocks/ota.go b/proxy/shadowsocks/ota.go index bca2eaca7..d1015e0f5 100644 --- a/proxy/shadowsocks/ota.go +++ b/proxy/shadowsocks/ota.go @@ -81,7 +81,7 @@ func (v *ChunkReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if length > buf.Size { // Theoretically the size of a chunk is 64K, but most Shadowsocks implementations used <4K buffer. buffer.Release() - buffer = buf.NewLocal(uint32(length) + 128) + buffer = buf.NewSize(uint32(length) + 128) } buffer.Clear() diff --git a/proxy/shadowsocks/ota_test.go b/proxy/shadowsocks/ota_test.go index 95064444f..106466389 100644 --- a/proxy/shadowsocks/ota_test.go +++ b/proxy/shadowsocks/ota_test.go @@ -24,11 +24,11 @@ func TestNormalChunkReading(t *testing.T) { func TestNormalChunkWriting(t *testing.T) { assert := With(t) - buffer := buf.NewLocal(512) + buffer := buf.NewSize(512) writer := NewChunkWriter(buffer, NewAuthenticator(ChunkKeyGenerator( []byte{21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36}))) - b := buf.NewLocal(256) + b := buf.NewSize(256) b.Append([]byte{11, 12, 13, 14, 15, 16, 17, 18}) err := writer.WriteMultiBuffer(buf.NewMultiBufferValue(b)) assert(err, IsNil) diff --git a/proxy/shadowsocks/protocol_test.go b/proxy/shadowsocks/protocol_test.go index 6c28370f7..65f72a139 100644 --- a/proxy/shadowsocks/protocol_test.go +++ b/proxy/shadowsocks/protocol_test.go @@ -29,7 +29,7 @@ func TestUDPEncoding(t *testing.T) { }, } - data := buf.NewLocal(256) + data := buf.NewSize(256) data.AppendSupplier(serial.WriteString("test string")) encodedData, err := EncodeUDPPacket(request, data.Bytes()) assert(err, IsNil) diff --git a/transport/internet/headers/http/http_test.go b/transport/internet/headers/http/http_test.go index dbe6d3f85..315cad762 100644 --- a/transport/internet/headers/http/http_test.go +++ b/transport/internet/headers/http/http_test.go @@ -16,7 +16,7 @@ func TestReaderWriter(t *testing.T) { assert := With(t) cache := buf.New() - b := buf.NewLocal(256) + b := buf.NewSize(256) b.AppendSupplier(serial.WriteString("abcd" + ENDING)) writer := NewHeaderWriter(b) err := writer.Write(cache) diff --git a/transport/internet/headers/srtp/srtp_test.go b/transport/internet/headers/srtp/srtp_test.go index b4f498d88..b959c8b1e 100644 --- a/transport/internet/headers/srtp/srtp_test.go +++ b/transport/internet/headers/srtp/srtp_test.go @@ -18,7 +18,7 @@ func TestSRTPWrite(t *testing.T) { srtp := srtpRaw.(*SRTP) - payload := buf.NewLocal(2048) + payload := buf.NewSize(2048) payload.AppendSupplier(srtp.Write) payload.Append(content) diff --git a/transport/internet/headers/utp/utp_test.go b/transport/internet/headers/utp/utp_test.go index a8b637dcb..4736001da 100644 --- a/transport/internet/headers/utp/utp_test.go +++ b/transport/internet/headers/utp/utp_test.go @@ -18,7 +18,7 @@ func TestUTPWrite(t *testing.T) { utp := utpRaw.(*UTP) - payload := buf.NewLocal(2048) + payload := buf.NewSize(2048) payload.AppendSupplier(utp.Write) payload.Append(content) diff --git a/transport/internet/headers/wechat/wechat_test.go b/transport/internet/headers/wechat/wechat_test.go index 2afc94665..6fde0bfd5 100644 --- a/transport/internet/headers/wechat/wechat_test.go +++ b/transport/internet/headers/wechat/wechat_test.go @@ -17,7 +17,7 @@ func TestUTPWrite(t *testing.T) { video := videoRaw.(*VideoChat) - payload := buf.NewLocal(2048) + payload := buf.NewSize(2048) payload.AppendSupplier(video.Write) assert(payload.Len(), Equals, video.Size()) From 931c8597cad16ba9bc79d27c4fea68565da6ce5c Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 11 Mar 2018 23:30:51 +0100 Subject: [PATCH 112/183] fix len -> cap --- common/buf/buffer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/buf/buffer.go b/common/buf/buffer.go index 1b94936ff..c15e3f968 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -207,7 +207,7 @@ func NewBytes(size uint32) []byte { } func FreeBytes(b []byte) { - size := len(b) + size := cap(b) switch { case size >= 128*1024: pool128k.Put(b) From 34c12c1af6edcf8e5ae6b8442c45787f0459a0ce Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 11 Mar 2018 23:31:37 +0100 Subject: [PATCH 113/183] extend buffer --- common/buf/buffer.go | 1 + 1 file changed, 1 insertion(+) diff --git a/common/buf/buffer.go b/common/buf/buffer.go index c15e3f968..f89b334ea 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -208,6 +208,7 @@ func NewBytes(size uint32) []byte { func FreeBytes(b []byte) { size := cap(b) + b = b[0:cap(b)] switch { case size >= 128*1024: pool128k.Put(b) From d1898b995fe5338d186ad803f70f017929f39152 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 11 Mar 2018 23:45:24 +0100 Subject: [PATCH 114/183] remove unnecessary NewSize --- transport/internet/headers/srtp/srtp_test.go | 2 +- transport/internet/headers/utp/utp_test.go | 2 +- transport/internet/headers/wechat/wechat_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/transport/internet/headers/srtp/srtp_test.go b/transport/internet/headers/srtp/srtp_test.go index b959c8b1e..1b08dae4e 100644 --- a/transport/internet/headers/srtp/srtp_test.go +++ b/transport/internet/headers/srtp/srtp_test.go @@ -18,7 +18,7 @@ func TestSRTPWrite(t *testing.T) { srtp := srtpRaw.(*SRTP) - payload := buf.NewSize(2048) + payload := buf.New() payload.AppendSupplier(srtp.Write) payload.Append(content) diff --git a/transport/internet/headers/utp/utp_test.go b/transport/internet/headers/utp/utp_test.go index 4736001da..a41480e2f 100644 --- a/transport/internet/headers/utp/utp_test.go +++ b/transport/internet/headers/utp/utp_test.go @@ -18,7 +18,7 @@ func TestUTPWrite(t *testing.T) { utp := utpRaw.(*UTP) - payload := buf.NewSize(2048) + payload := buf.New() payload.AppendSupplier(utp.Write) payload.Append(content) diff --git a/transport/internet/headers/wechat/wechat_test.go b/transport/internet/headers/wechat/wechat_test.go index 6fde0bfd5..80185d1e2 100644 --- a/transport/internet/headers/wechat/wechat_test.go +++ b/transport/internet/headers/wechat/wechat_test.go @@ -17,7 +17,7 @@ func TestUTPWrite(t *testing.T) { video := videoRaw.(*VideoChat) - payload := buf.NewSize(2048) + payload := buf.New() payload.AppendSupplier(video.Write) assert(payload.Len(), Equals, video.Size()) From 5bbece14afdc8db25eb61584b931c35c0389eceb Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Mar 2018 16:21:39 +0100 Subject: [PATCH 115/183] simplify pool creation --- common/buf/buffer.go | 41 +++--------------------------------- common/buf/buffer_pool.go | 44 ++++++++++++++++++++++++++++++--------- common/buf/reader.go | 10 ++++----- common/buf/reader_test.go | 10 ++++++++- 4 files changed, 50 insertions(+), 55 deletions(-) diff --git a/common/buf/buffer.go b/common/buf/buffer.go index f89b334ea..356a9c85f 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -23,7 +23,7 @@ func (b *Buffer) Release() { if b == nil || b.v == nil { return } - FreeBytes(b.v) + freeBytes(b.v) b.v = nil b.start = 0 b.end = 0 @@ -175,48 +175,13 @@ func (b *Buffer) String() string { // New creates a Buffer with 0 length and 2K capacity. func New() *Buffer { return &Buffer{ - v: pool2k.Get().([]byte), + v: pool[0].Get().([]byte), } } // NewSize creates and returns a buffer given capacity. func NewSize(size uint32) *Buffer { return &Buffer{ - v: NewBytes(size), - } -} - -func NewBytes(size uint32) []byte { - if size > 128*1024 { - return make([]byte, size) - } - - if size > 64*1024 { - return pool128k.Get().([]byte) - } - - if size > 8*1024 { - return pool64k.Get().([]byte) - } - - if size > 2*1024 { - return pool8k.Get().([]byte) - } - - return pool2k.Get().([]byte) -} - -func FreeBytes(b []byte) { - size := cap(b) - b = b[0:cap(b)] - switch { - case size >= 128*1024: - pool128k.Put(b) - case size >= 64*1024: - pool64k.Put(b) - case size >= 8*1024: - pool8k.Put(b) - case size >= 2*1024: - pool2k.Put(b) + v: newBytes(size), } } diff --git a/common/buf/buffer_pool.go b/common/buf/buffer_pool.go index 92dcd34cd..1c7a9ffe0 100644 --- a/common/buf/buffer_pool.go +++ b/common/buf/buffer_pool.go @@ -15,18 +15,42 @@ func createAllocFunc(size uint32) func() interface{} { } } -var pool2k = &sync.Pool{ - New: createAllocFunc(2 * 1024), +const ( + numPools = 5 + sizeMulti = 4 +) + +var ( + pool [numPools]*sync.Pool + poolSize [numPools]uint32 +) + +func init() { + size := uint32(Size) + for i := 0; i < numPools; i++ { + pool[i] = &sync.Pool{ + New: createAllocFunc(size), + } + poolSize[i] = size + size *= sizeMulti + } } -var pool8k = &sync.Pool{ - New: createAllocFunc(8 * 1024), +func newBytes(size uint32) []byte { + for idx, ps := range poolSize { + if size <= ps { + return pool[idx].Get().([]byte) + } + } + return make([]byte, size) } -var pool64k = &sync.Pool{ - New: createAllocFunc(64 * 1024), -} - -var pool128k = &sync.Pool{ - New: createAllocFunc(128 * 1024), +func freeBytes(b []byte) { + size := uint32(cap(b)) + for i := numPools - 1; i >= 0; i-- { + ps := poolSize[i] + if size >= ps { + pool[i].Put(b) + } + } } diff --git a/common/buf/reader.go b/common/buf/reader.go index f040a9371..87f368086 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -19,15 +19,13 @@ func NewBytesToBufferReader(reader io.Reader) Reader { } } -const mediumSize = 8 * 1024 -const largeSize = 64 * 1024 const xlSize = 128 * 1024 func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) { b := New() err := b.Reset(ReadFrom(r.Reader)) if b.IsFull() { - r.buffer = NewBytes(mediumSize) + r.buffer = newBytes(Size + 1) } if !b.IsEmpty() { return NewMultiBufferValue(b), nil @@ -47,12 +45,12 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) { mb := NewMultiBufferCap(nBytes/Size + 1) mb.Write(r.buffer[:nBytes]) if nBytes == len(r.buffer) && nBytes < xlSize { - FreeBytes(r.buffer) - r.buffer = NewBytes(uint32(nBytes) + 1) + freeBytes(r.buffer) + r.buffer = newBytes(uint32(nBytes) + 1) } return mb, nil } - FreeBytes(r.buffer) + freeBytes(r.buffer) r.buffer = nil return nil, err } diff --git a/common/buf/reader_test.go b/common/buf/reader_test.go index 9c90cfe05..f5964ff92 100644 --- a/common/buf/reader_test.go +++ b/common/buf/reader_test.go @@ -25,7 +25,15 @@ func TestAdaptiveReader(t *testing.T) { b, err = reader.ReadMultiBuffer() assert(err, IsNil) - assert(b.Len(), Equals, 64*1024) + assert(b.Len(), Equals, 32*1024) + + b, err = reader.ReadMultiBuffer() + assert(err, IsNil) + assert(b.Len(), Equals, 128*1024) + + b, err = reader.ReadMultiBuffer() + assert(err, IsNil) + assert(b.Len(), Equals, 128*1024) } func TestBytesReaderWriteTo(t *testing.T) { From 0c213ccd209a6eea213ed0d98ca1483feaa80d98 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Mar 2018 16:24:31 +0100 Subject: [PATCH 116/183] reset buffer when free --- common/buf/buffer_pool.go | 1 + 1 file changed, 1 insertion(+) diff --git a/common/buf/buffer_pool.go b/common/buf/buffer_pool.go index 1c7a9ffe0..9e0cadf2c 100644 --- a/common/buf/buffer_pool.go +++ b/common/buf/buffer_pool.go @@ -47,6 +47,7 @@ func newBytes(size uint32) []byte { func freeBytes(b []byte) { size := uint32(cap(b)) + b = b[0:cap(b)] for i := numPools - 1; i >= 0; i-- { ps := poolSize[i] if size >= ps { From 1cbfeea0cd8eb4ba21b83dea08f589514925bac3 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 12 Mar 2018 22:10:13 +0100 Subject: [PATCH 117/183] simplify NewSize calls --- app/proxyman/mux/reader.go | 7 +------ common/crypto/auth.go | 7 +------ proxy/shadowsocks/ota.go | 20 ++++++-------------- 3 files changed, 8 insertions(+), 26 deletions(-) diff --git a/app/proxyman/mux/reader.go b/app/proxyman/mux/reader.go index 33453b715..b2e1a4117 100644 --- a/app/proxyman/mux/reader.go +++ b/app/proxyman/mux/reader.go @@ -51,12 +51,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { return nil, err } - var b *buf.Buffer - if size <= buf.Size { - b = buf.New() - } else { - b = buf.NewSize(uint32(size)) - } + b := buf.NewSize(uint32(size)) if err := b.AppendSupplier(buf.ReadFullFrom(r.reader, int(size))); err != nil { b.Release() return nil, err diff --git a/common/crypto/auth.go b/common/crypto/auth.go index ecdb0189f..84f5b41ab 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -124,12 +124,7 @@ func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) { return nil, io.EOF } - var b *buf.Buffer - if size <= buf.Size { - b = buf.New() - } else { - b = buf.NewSize(uint32(size)) - } + b := buf.NewSize(uint32(size)) if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil { b.Release() return nil, err diff --git a/proxy/shadowsocks/ota.go b/proxy/shadowsocks/ota.go index d1015e0f5..be18be8c7 100644 --- a/proxy/shadowsocks/ota.go +++ b/proxy/shadowsocks/ota.go @@ -70,22 +70,14 @@ func NewChunkReader(reader io.Reader, auth *Authenticator) *ChunkReader { } func (v *ChunkReader) ReadMultiBuffer() (buf.MultiBuffer, error) { - buffer := buf.New() - if err := buffer.AppendSupplier(buf.ReadFullFrom(v.reader, 2)); err != nil { - buffer.Release() - return nil, err - } - // There is a potential buffer overflow here. Large buffer is 64K bytes, - // while uin16 + 10 will be more than that - length := serial.BytesToUint16(buffer.BytesTo(2)) + AuthSize - if length > buf.Size { - // Theoretically the size of a chunk is 64K, but most Shadowsocks implementations used <4K buffer. - buffer.Release() - buffer = buf.NewSize(uint32(length) + 128) + size, err := serial.ReadUint16(v.reader) + if err != nil { + return nil, newError("failed to read size") } + size += AuthSize - buffer.Clear() - if err := buffer.AppendSupplier(buf.ReadFullFrom(v.reader, int(length))); err != nil { + buffer := buf.NewSize(uint32(size)) + if err := buffer.AppendSupplier(buf.ReadFullFrom(v.reader, int(size))); err != nil { buffer.Release() return nil, err } From 5a227ec35694a25c1879e948efef632ab8657e33 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 13 Mar 2018 03:39:36 +0100 Subject: [PATCH 118/183] int to int32 --- app/proxyman/mux/reader.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/proxyman/mux/reader.go b/app/proxyman/mux/reader.go index b2e1a4117..d812c6b66 100644 --- a/app/proxyman/mux/reader.go +++ b/app/proxyman/mux/reader.go @@ -63,7 +63,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { // StreamReader reads Mux frame as a stream. type StreamReader struct { reader *buf.BufferedReader - leftOver int + leftOver int32 } // NewStreamReader creates a new StreamReader. @@ -86,10 +86,10 @@ func (r *StreamReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if err != nil { return nil, err } - r.leftOver = int(size) + r.leftOver = int32(size) } - mb, err := r.reader.ReadAtMost(r.leftOver) - r.leftOver -= mb.Len() + mb, err := r.reader.ReadAtMost(int(r.leftOver)) + r.leftOver -= int32(mb.Len()) return mb, err } From b4e124016021683bf237654df17e77684fff3fc4 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 13 Mar 2018 09:02:21 +0100 Subject: [PATCH 119/183] comments --- common/buf/buffer.go | 2 +- config.proto | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/common/buf/buffer.go b/common/buf/buffer.go index 356a9c85f..51d3ed8b7 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -179,7 +179,7 @@ func New() *Buffer { } } -// NewSize creates and returns a buffer given capacity. +// NewSize creates and returns a buffer with 0 length and at least the given capacity. func NewSize(size uint32) *Buffer { return &Buffer{ v: newBytes(size), diff --git a/config.proto b/config.proto index de8461f45..26d660e3c 100644 --- a/config.proto +++ b/config.proto @@ -19,7 +19,7 @@ message Config { reserved 3; - // App configuration. Must be one in the app directory. + // App is for configurations of all features in V2Ray. A feature must implement the Feature interface, and its config type must be registered through common.RegisterConfig. repeated v2ray.core.common.serial.TypedMessage app = 4; // Transport settings. @@ -30,19 +30,21 @@ message Config { repeated v2ray.core.common.serial.TypedMessage extension = 6; } +// InboundHandlerConfig is the configuration for inbound handler. message InboundHandlerConfig { - // Tag of the inbound handler. + // Tag of the inbound handler. The tag must be unique among all inbound handlers string tag = 1; - // Settings for how this inbound proxy is handled. Must be ReceiverConfig above. + // Settings for how this inbound proxy is handled. v2ray.core.common.serial.TypedMessage receiver_settings = 2; // Settings for inbound proxy. Must be one of the inbound proxies. v2ray.core.common.serial.TypedMessage proxy_settings = 3; } +// OutboundHandlerConfig is the configuration for outbound handler. message OutboundHandlerConfig { // Tag of this outbound handler. string tag = 1; - // Settings for how to dial connection for this outbound handler. Must be SenderConfig above. + // Settings for how to dial connection for this outbound handler. v2ray.core.common.serial.TypedMessage sender_settings = 2; // Settings for this outbound proxy. Must be one of the outbound proxies. v2ray.core.common.serial.TypedMessage proxy_settings = 3; From 087c0c14994b1ef7010e7cdccc8a05d779f21880 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 13 Mar 2018 09:02:34 +0100 Subject: [PATCH 120/183] refine address family type --- common/net/address.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/net/address.go b/common/net/address.go index 6ab0587f4..04d40f0c7 100644 --- a/common/net/address.go +++ b/common/net/address.go @@ -22,7 +22,7 @@ var ( ) // AddressFamily is the type of address. -type AddressFamily int +type AddressFamily byte const ( // AddressFamilyIPv4 represents address as IPv4 From 855925a8052cf470de099f252da6492b69100236 Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Thu, 15 Mar 2018 10:32:10 +0800 Subject: [PATCH 121/183] Fix typo --- app/commander/outbound.go | 2 +- app/dispatcher/default.go | 4 ++-- app/proxyman/command/command.go | 2 +- app/proxyman/proxyman.go | 2 +- common/signal/done.go | 2 +- common/signal/exec.go | 2 +- common/signal/notifier.go | 2 +- v2ray.go | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/commander/outbound.go b/app/commander/outbound.go index 19b900108..f82ae6f28 100644 --- a/app/commander/outbound.go +++ b/app/commander/outbound.go @@ -27,7 +27,7 @@ func (l *OutboundListener) add(conn net.Conn) { func (l *OutboundListener) Accept() (net.Conn, error) { select { case <-l.done.C(): - return nil, newError("listern closed") + return nil, newError("listen closed") case c := <-l.buffer: return c, nil } diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 738fbb539..50f39741b 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -55,7 +55,7 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin ctx = proxy.ContextWithTarget(ctx, destination) outbound := ray.NewRay(ctx) - snifferList := proxyman.ProtocoSniffersFromContext(ctx) + snifferList := proxyman.ProtocolSniffersFromContext(ctx) if destination.Address.Family().IsDomain() || len(snifferList) == 0 { go d.routedDispatch(ctx, outbound, destination) } else { @@ -110,7 +110,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, outbound ray.Out newError("taking detour [", tag, "] for [", destination, "]").WithContext(ctx).WriteToLog() dispatcher = handler } else { - newError("nonexisting tag: ", tag).AtWarning().WithContext(ctx).WriteToLog() + newError("non existing tag: ", tag).AtWarning().WithContext(ctx).WriteToLog() } } else { newError("default route for ", destination).WithContext(ctx).WriteToLog() diff --git a/app/proxyman/command/command.go b/app/proxyman/command/command.go index 023b83c5e..ca0b662c4 100644 --- a/app/proxyman/command/command.go +++ b/app/proxyman/command/command.go @@ -11,7 +11,7 @@ import ( // InboundOperation is the interface for operations that applies to inbound handlers. type InboundOperation interface { - // ApplyInbound appliess this operation to the given inbound handler. + // ApplyInbound applies this operation to the given inbound handler. ApplyInbound(context.Context, core.InboundHandler) error } diff --git a/app/proxyman/proxyman.go b/app/proxyman/proxyman.go index 1ae393aa3..34bf481f7 100644 --- a/app/proxyman/proxyman.go +++ b/app/proxyman/proxyman.go @@ -17,7 +17,7 @@ func ContextWithProtocolSniffers(ctx context.Context, list []KnownProtocols) con return context.WithValue(ctx, protocolsKey, list) } -func ProtocoSniffersFromContext(ctx context.Context) []KnownProtocols { +func ProtocolSniffersFromContext(ctx context.Context) []KnownProtocols { if list, ok := ctx.Value(protocolsKey).([]KnownProtocols); ok { return list } diff --git a/common/signal/done.go b/common/signal/done.go index ab3655137..bedf50abe 100644 --- a/common/signal/done.go +++ b/common/signal/done.go @@ -38,7 +38,7 @@ func (d *Done) Wait() { <-d.c } -// Close marks this Done 'done'. This method may be called mutliple times. All calls after first call will have no effect on its status. +// Close marks this Done 'done'. This method may be called multiple times. All calls after first call will have no effect on its status. func (d *Done) Close() error { d.access.Lock() defer d.access.Unlock() diff --git a/common/signal/exec.go b/common/signal/exec.go index 3a3400f9b..0547a607d 100644 --- a/common/signal/exec.go +++ b/common/signal/exec.go @@ -12,7 +12,7 @@ func executeAndFulfill(f func() error, done chan<- error) { close(done) } -// ExecuteAsync executes a function asychrously and return its result. +// ExecuteAsync executes a function asynchronously and return its result. func ExecuteAsync(f func() error) <-chan error { done := make(chan error, 1) go executeAndFulfill(f, done) diff --git a/common/signal/notifier.go b/common/signal/notifier.go index 702bab065..0a0362785 100644 --- a/common/signal/notifier.go +++ b/common/signal/notifier.go @@ -1,6 +1,6 @@ package signal -// Notifier is an utility for notifying changes. The change producer may notify changes multiple time, and the consumer may get notified asychronously. +// Notifier is an utility for notifying changes. The change producer may notify changes multiple time, and the consumer may get notified asynchronously. type Notifier struct { c chan struct{} } diff --git a/v2ray.go b/v2ray.go index f2c782ba2..d68842169 100644 --- a/v2ray.go +++ b/v2ray.go @@ -130,7 +130,7 @@ func (s *Instance) Start() error { } // RegisterFeature registers the given feature into V2Ray. -// If feature is one of the following types, the corressponding feature in this Instance +// If feature is one of the following types, the corresponding feature in this Instance // will be replaced: DNSClient, PolicyManager, Router, Dispatcher, InboundHandlerManager, OutboundHandlerManager. func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error { running := false From b5b9a83823270707c8e6ab8b50910e1c73ee5262 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 16 Mar 2018 10:13:10 +0700 Subject: [PATCH 122/183] reorder fields in Destination for faster hashing --- common/net/destination.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/net/destination.go b/common/net/destination.go index f1166f95f..8cbfbccd1 100644 --- a/common/net/destination.go +++ b/common/net/destination.go @@ -6,9 +6,9 @@ import ( // Destination represents a network destination including address and protocol (tcp / udp). type Destination struct { - Network Network - Port Port Address Address + Port Port + Network Network } // DestinationFromAddr generates a Destination from a net address. From 000e0804e8413c812d0500ac39ad131bff30ae72 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 16 Mar 2018 16:22:22 +0700 Subject: [PATCH 123/183] fix buffer recycling --- common/buf/buffer_pool.go | 5 +++-- common/crypto/auth_test.go | 13 ++++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/common/buf/buffer_pool.go b/common/buf/buffer_pool.go index 9e0cadf2c..728228f3f 100644 --- a/common/buf/buffer_pool.go +++ b/common/buf/buffer_pool.go @@ -21,14 +21,14 @@ const ( ) var ( - pool [numPools]*sync.Pool + pool [numPools]sync.Pool poolSize [numPools]uint32 ) func init() { size := uint32(Size) for i := 0; i < numPools; i++ { - pool[i] = &sync.Pool{ + pool[i] = sync.Pool{ New: createAllocFunc(size), } poolSize[i] = size @@ -52,6 +52,7 @@ func freeBytes(b []byte) { ps := poolSize[i] if size >= ps { pool[i].Put(b) + return } } } diff --git a/common/crypto/auth_test.go b/common/crypto/auth_test.go index ada4181c5..0986a5839 100644 --- a/common/crypto/auth_test.go +++ b/common/crypto/auth_test.go @@ -24,11 +24,13 @@ func TestAuthenticationReaderWriter(t *testing.T) { aead, err := cipher.NewGCM(block) assert(err, IsNil) - rawPayload := make([]byte, 8192*10) + const payloadSize = 1024 * 80 + rawPayload := make([]byte, payloadSize) rand.Read(rawPayload) - payload := buf.NewSize(8192 * 10) + payload := buf.NewSize(payloadSize) payload.Append(rawPayload) + assert(payload.Len(), Equals, payloadSize) cache := buf.NewSize(160 * 1024) iv := make([]byte, 12) @@ -45,7 +47,6 @@ func TestAuthenticationReaderWriter(t *testing.T) { assert(writer.WriteMultiBuffer(buf.NewMultiBufferValue(payload)), IsNil) assert(cache.Len(), Equals, 82658) assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil) - assert(err, IsNil) reader := NewAuthenticationReader(&AEADAuthenticator{ AEAD: aead, @@ -57,14 +58,16 @@ func TestAuthenticationReaderWriter(t *testing.T) { var mb buf.MultiBuffer - for mb.Len() < len(rawPayload) { + for mb.Len() < payloadSize { mb2, err := reader.ReadMultiBuffer() assert(err, IsNil) mb.AppendMulti(mb2) } - mbContent := make([]byte, 8192*10) + assert(mb.Len(), Equals, payloadSize) + + mbContent := make([]byte, payloadSize) mb.Read(mbContent) assert(mbContent, Equals, rawPayload) From feb421d9e12298ef0a5ae340b7802ea7d43b521e Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 16 Mar 2018 16:36:47 +0700 Subject: [PATCH 124/183] Update version --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index f380edf28..607f94f5a 100644 --- a/core.go +++ b/core.go @@ -16,7 +16,7 @@ import ( ) var ( - version = "3.14" + version = "3.15" build = "Custom" codename = "die Commanderin" intro = "An unified platform for anti-censorship." From b2d9364cb541dc14591b49e730b28dee292d0d5c Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 16 Mar 2018 22:32:03 +0700 Subject: [PATCH 125/183] release buffer if payload becomes small --- common/buf/reader.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/buf/reader.go b/common/buf/reader.go index 87f368086..7c9fa8ce8 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -47,6 +47,9 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) { if nBytes == len(r.buffer) && nBytes < xlSize { freeBytes(r.buffer) r.buffer = newBytes(uint32(nBytes) + 1) + } else if nBytes < Size { + freeBytes(r.buffer) + r.buffer = nil } return mb, nil } From e8438a522a27a75e88cd8cad2a78283a2f227359 Mon Sep 17 00:00:00 2001 From: Wuxiang Date: Mon, 19 Mar 2018 16:40:47 +0800 Subject: [PATCH 126/183] fix typo fix typo --- app/proxyman/proxyman.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/proxyman/proxyman.go b/app/proxyman/proxyman.go index 1ae393aa3..a8ec466b9 100644 --- a/app/proxyman/proxyman.go +++ b/app/proxyman/proxyman.go @@ -1,4 +1,4 @@ -// Package proxyman defines applications for manageing inbound and outbound proxies. +// Package proxyman defines applications for managing inbound and outbound proxies. package proxyman //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg proxyman -path App,Proxyman From 8ffedde5a95f711d429c9b6e632b9e682262dd48 Mon Sep 17 00:00:00 2001 From: mubiale Date: Mon, 19 Mar 2018 17:01:15 +0800 Subject: [PATCH 127/183] fix typos --- release/config/systemv/v2ray | 0 release/install-release.sh | 6 +++--- release/make-release.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 release/config/systemv/v2ray diff --git a/release/config/systemv/v2ray b/release/config/systemv/v2ray old mode 100644 new mode 100755 diff --git a/release/install-release.sh b/release/install-release.sh index 0c0c71f4f..c6a7c82e9 100755 --- a/release/install-release.sh +++ b/release/install-release.sh @@ -149,7 +149,7 @@ extract(){ mkdir -p /tmp/v2ray unzip $1 -d "/tmp/v2ray/" if [[ $? -ne 0 ]]; then - colorEcho ${RED} "Extracting V2Ray faile!" + colorEcho ${RED} "Extracting V2Ray failed!" exit fi return 0 @@ -371,11 +371,11 @@ main(){ NEW_VER=`ls /tmp/v2ray |grep v2ray-v |cut -d "-" -f2` fi else - # dowload via network and extract + # download via network and extract installSoftware "curl" getVersion if [[ $? == 0 ]] && [[ "$FORCE" != "1" ]]; then - colorEcho ${GREEN} "Lastest version ${NEW_VER} is already installed." + colorEcho ${GREEN} "Latest version ${NEW_VER} is already installed." exit else colorEcho ${BLUE} "Installing V2Ray ${NEW_VER} on ${ARCH}" diff --git a/release/make-release.sh b/release/make-release.sh index 387e1bdcf..d2caa0ba8 100755 --- a/release/make-release.sh +++ b/release/make-release.sh @@ -27,7 +27,7 @@ pushd $GOPATH/src/v2ray.com/core echo "Adding a new tag: " "v$VER" git tag -s -a "v$VER" -m "Version ${VER}" sed -i '' "s/\(version *= *\"\).*\(\"\)/\1$VERN\2/g" core.go -echo "Commiting core.go (may not necessary)" +echo "Committing core.go (may not necessary)" git commit core.go -S -m "Update version" echo "Pushing changes" git push --follow-tags From 10618b629b68f882675df8b21dae7f154e60eae2 Mon Sep 17 00:00:00 2001 From: mubiale Date: Mon, 19 Mar 2018 17:33:52 +0800 Subject: [PATCH 128/183] fix typo --- v2ray.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 v2ray.go diff --git a/v2ray.go b/v2ray.go old mode 100644 new mode 100755 index f2c782ba2..d68842169 --- a/v2ray.go +++ b/v2ray.go @@ -130,7 +130,7 @@ func (s *Instance) Start() error { } // RegisterFeature registers the given feature into V2Ray. -// If feature is one of the following types, the corressponding feature in this Instance +// If feature is one of the following types, the corresponding feature in this Instance // will be replaced: DNSClient, PolicyManager, Router, Dispatcher, InboundHandlerManager, OutboundHandlerManager. func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error { running := false From e647292b9a091720357e7b090f51abc404501a27 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 21 Mar 2018 12:53:57 +0100 Subject: [PATCH 129/183] protect against writing to closed body --- transport/internet/http/hub.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/transport/internet/http/hub.go b/transport/internet/http/hub.go index 250ccb337..a8e38ed8f 100644 --- a/transport/internet/http/hub.go +++ b/transport/internet/http/hub.go @@ -31,9 +31,14 @@ func (l *Listener) Close() error { type flushWriter struct { w io.Writer + d *signal.Done } func (fw flushWriter) Write(p []byte) (n int, err error) { + if fw.d.Done() { + return 0, io.ErrClosedPipe + } + n, err = fw.w.Write(p) if f, ok := fw.w.(http.Flusher); ok { f.Flush() @@ -61,8 +66,8 @@ func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request) done := signal.NewDone() l.handler(&Connection{ Reader: request.Body, - Writer: flushWriter{writer}, - Closer: common.NewChainedClosable(request.Body, done), + Writer: flushWriter{w: writer, d: done}, + Closer: common.NewChainedClosable(done, request.Body), Local: l.Addr(), Remote: l.Addr(), }) From 169b901c2dca5e3bab60d2dadb94cd408f946acf Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 21 Mar 2018 23:28:28 +0100 Subject: [PATCH 130/183] lazy init of dialer map --- transport/internet/http/dialer.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/transport/internet/http/dialer.go b/transport/internet/http/dialer.go index 025e314de..b4b835308 100644 --- a/transport/internet/http/dialer.go +++ b/transport/internet/http/dialer.go @@ -17,7 +17,7 @@ import ( ) var ( - globalDialerMap = make(map[net.Destination]*http.Client) + globalDialerMap map[net.Destination]*http.Client globalDailerAccess sync.Mutex ) @@ -25,6 +25,10 @@ func getHTTPClient(ctx context.Context, dest net.Destination) (*http.Client, err globalDailerAccess.Lock() defer globalDailerAccess.Unlock() + if globalDialerMap == nil { + globalDialerMap = make(map[net.Destination]*http.Client) + } + if client, found := globalDialerMap[dest]; found { return client, nil } From 3ff570a050d54b662f26575e80acc3d820718b71 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 21 Mar 2018 23:38:49 +0100 Subject: [PATCH 131/183] write listen error to log --- transport/internet/http/hub.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/transport/internet/http/hub.go b/transport/internet/http/hub.go index a8e38ed8f..0ad91f1bc 100644 --- a/transport/internet/http/hub.go +++ b/transport/internet/http/hub.go @@ -102,7 +102,12 @@ func Listen(ctx context.Context, address net.Address, port net.Port, handler int } listener.server = server - go server.ListenAndServeTLS("", "") + go func() { + err := server.ListenAndServeTLS("", "") + if err != nil { + newError("stoping serving TLS").Base(err).WriteToLog() + } + }() return listener, nil } From 33e090befe5e71da0f9de8ef90d0df4e769fe932 Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Fri, 23 Mar 2018 14:20:14 +0800 Subject: [PATCH 132/183] Clean useless code --- app/dns/server.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/dns/server.go b/app/dns/server.go index 866c5ff0f..8f5c3cd54 100644 --- a/app/dns/server.go +++ b/app/dns/server.go @@ -28,11 +28,6 @@ func (r *DomainRecord) Expired() bool { return r.Expire.Before(time.Now()) } -func (r *DomainRecord) Inactive() bool { - now := time.Now() - return r.Expire.Before(now) || r.LastAccess.Add(time.Minute*5).Before(now) -} - type Server struct { sync.Mutex hosts map[string]net.IP From 2d5aa9514fcfb74d05d666628fc592236ed87fea Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Fri, 23 Mar 2018 16:18:57 +0800 Subject: [PATCH 133/183] consistent ServerSpec var --- common/protocol/server_spec.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/protocol/server_spec.go b/common/protocol/server_spec.go index 107dbb1c7..3f35d8f39 100644 --- a/common/protocol/server_spec.go +++ b/common/protocol/server_spec.go @@ -110,10 +110,10 @@ func (s *ServerSpec) PickUser() *User { } } -func (v *ServerSpec) IsValid() bool { - return v.valid.IsValid() +func (s *ServerSpec) IsValid() bool { + return s.valid.IsValid() } -func (v *ServerSpec) Invalidate() { - v.valid.Invalidate() +func (s *ServerSpec) Invalidate() { + s.valid.Invalidate() } From eb38f4865ec891022410cb09c8178a1062569a3f Mon Sep 17 00:00:00 2001 From: Jinqiu Yu Date: Fri, 23 Mar 2018 23:17:29 +0800 Subject: [PATCH 134/183] Fix another typo --- common/buf/buffer.go | 2 +- proxy/context.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/buf/buffer.go b/common/buf/buffer.go index 51d3ed8b7..d2ca828e6 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -78,7 +78,7 @@ func (b *Buffer) Reset(writer Supplier) error { return err } -// BytesRange returns a slice of this buffer with given from and to bounary. +// BytesRange returns a slice of this buffer with given from and to boundary. func (b *Buffer) BytesRange(from, to int) []byte { if from < 0 { from += b.Len() diff --git a/proxy/context.go b/proxy/context.go index c3a1378c1..7bc551255 100644 --- a/proxy/context.go +++ b/proxy/context.go @@ -22,7 +22,7 @@ func ContextWithSource(ctx context.Context, src net.Destination) context.Context return context.WithValue(ctx, sourceKey, src) } -// SourceFromContext retreives source from the given context. +// SourceFromContext retrieves source from the given context. func SourceFromContext(ctx context.Context) (net.Destination, bool) { v, ok := ctx.Value(sourceKey).(net.Destination) return v, ok From 5791064763b3435e71573726c1f6c1f57a7b3727 Mon Sep 17 00:00:00 2001 From: Wuxiang Date: Mon, 26 Mar 2018 09:47:45 +0800 Subject: [PATCH 135/183] fix typo --- router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router.go b/router.go index 6cbb3e792..4ea16cd4f 100644 --- a/router.go +++ b/router.go @@ -70,7 +70,7 @@ var ( ErrNoClue = errors.New("not enough information for making a decision") ) -// Router is a feature to choose a outbound tag for the given request. +// Router is a feature to choose an outbound tag for the given request. type Router interface { Feature From e44394ade5f3279df70355cfc4915c1b9915a15e Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 26 Mar 2018 16:56:19 +0200 Subject: [PATCH 136/183] more informational error --- proxy/socks/protocol.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index 8aba9c5c9..10c0f2d92 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -135,16 +135,21 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol } cmd := buffer.Byte(1) - if cmd == cmdTCPBind || (cmd == cmdUDPPort && !s.config.UdpEnabled) { - writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) - return nil, newError("unsupported command: ", cmd) - } - switch cmd { case cmdTCPConnect: request.Command = protocol.RequestCommandTCP case cmdUDPPort: + if !s.config.UdpEnabled { + writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) + return nil, newError("UDP is not enabled.") + } request.Command = protocol.RequestCommandUDP + case cmdTCPBind: + writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) + return nil, newError("TCP bind is not supported.") + default: + writeSocks5Response(writer, statusCmdNotSupport, net.AnyIP, net.Port(0)) + return nil, newError("unknown command ", cmd) } buffer.Clear() From eeed65f8265dcf0605471ab2225c2ac2b3253613 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 28 Mar 2018 14:33:34 +0200 Subject: [PATCH 137/183] fix #999 --- common/buf/multi_buffer.go | 2 +- common/buf/multi_buffer_test.go | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/common/buf/multi_buffer.go b/common/buf/multi_buffer.go index 85830fc7b..a442ce115 100644 --- a/common/buf/multi_buffer.go +++ b/common/buf/multi_buffer.go @@ -167,7 +167,7 @@ func (mb *MultiBuffer) SliceBySize(size int) MultiBuffer { } *mb = (*mb)[endIndex:] if endIndex == 0 && len(*mb) > 0 { - b := New() + b := NewSize(uint32(size)) common.Must(b.Reset(ReadFullFrom((*mb)[0], size))) return NewMultiBufferValue(b) } diff --git a/common/buf/multi_buffer_test.go b/common/buf/multi_buffer_test.go index 92df32f02..e78918f44 100644 --- a/common/buf/multi_buffer_test.go +++ b/common/buf/multi_buffer_test.go @@ -1,8 +1,10 @@ package buf_test import ( + "crypto/rand" "testing" + "v2ray.com/core/common" . "v2ray.com/core/common/buf" . "v2ray.com/ext/assert" ) @@ -33,3 +35,16 @@ func TestMultiBufferAppend(t *testing.T) { mb.Append(b) assert(mb.Len(), Equals, 2) } + +func TestMultiBufferSliceBySizeLarge(t *testing.T) { + assert := With(t) + + lb := NewSize(8 * 1024) + common.Must(lb.Reset(ReadFrom(rand.Reader))) + + var mb MultiBuffer + mb.Append(lb) + + mb2 := mb.SliceBySize(4 * 1024) + assert(mb2.Len(), Equals, 4*1024) +} From 12181f527fa27d5073193d6b8b802af0580c8aa8 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 28 Mar 2018 14:40:51 +0200 Subject: [PATCH 138/183] defensive fix for improper usage of buf.New(). --- app/proxyman/mux/reader.go | 2 +- proxy/http/server.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/proxyman/mux/reader.go b/app/proxyman/mux/reader.go index d812c6b66..d25132571 100644 --- a/app/proxyman/mux/reader.go +++ b/app/proxyman/mux/reader.go @@ -17,7 +17,7 @@ func ReadMetadata(reader io.Reader) (*FrameMetadata, error) { return nil, newError("invalid metalen ", metaLen).AtError() } - b := buf.New() + b := buf.NewSize(uint32(metaLen)) defer b.Release() if err := b.Reset(buf.ReadFullFrom(reader, int(metaLen))); err != nil { diff --git a/proxy/http/server.go b/proxy/http/server.go index 48e0520f9..2e939803b 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -173,7 +173,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade } if reader.Buffered() > 0 { - payload := buf.New() + payload := buf.NewSize(uint32(reader.Buffered())) common.Must(payload.Reset(func(b []byte) (int, error) { return reader.Read(b[:reader.Buffered()]) })) From fa63beceb6863c7fedc88525910ebc90a2a6442d Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 28 Mar 2018 22:23:24 +0200 Subject: [PATCH 139/183] fix double release on buffers --- app/proxyman/mux/mux.go | 5 ----- proxy/shadowsocks/server.go | 15 ++++++++------- proxy/vmess/inbound/inbound.go | 17 +++++++++-------- proxy/vmess/outbound/outbound.go | 17 +++++++++-------- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index 2119dcebc..c46312bc4 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -150,11 +150,6 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) { defer s.Close() newError("dispatching request to ", dest).WithContext(ctx).WriteToLog() - data, _ := s.input.ReadTimeout(time.Millisecond * 500) - if err := writer.WriteMultiBuffer(data); err != nil { - newError("failed to write first payload").Base(err).WithContext(ctx).WriteToLog() - return - } if err := buf.Copy(s.input, writer); err != nil { newError("failed to fetch all input").Base(err).WithContext(ctx).WriteToLog() } diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index ffb676920..5b75b9e05 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -178,14 +178,15 @@ func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, return newError("failed to write response").Base(err) } - payload, err := ray.InboundOutput().ReadMultiBuffer() - if err != nil { - return err + { + payload, err := ray.InboundOutput().ReadMultiBuffer() + if err != nil { + return err + } + if err := responseWriter.WriteMultiBuffer(payload); err != nil { + return err + } } - if err := responseWriter.WriteMultiBuffer(payload); err != nil { - return err - } - payload.Release() if err := bufferedWriter.SetBuffered(false); err != nil { return err diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index df85770e7..df9988536 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -182,16 +182,17 @@ func transferResponse(timer signal.ActivityUpdater, session *encoding.ServerSess bodyWriter := session.EncodeResponseBody(request, output) - // Optimize for small response packet - data, err := input.ReadMultiBuffer() - if err != nil { - return err - } + { + // Optimize for small response packet + data, err := input.ReadMultiBuffer() + if err != nil { + return err + } - if err := bodyWriter.WriteMultiBuffer(data); err != nil { - return err + if err := bodyWriter.WriteMultiBuffer(data); err != nil { + return err + } } - data.Release() if bufferedWriter, ok := output.(*buf.BufferedWriter); ok { if err := bufferedWriter.SetBuffered(false); err != nil { diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index a6b937c12..a59904d39 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -113,15 +113,16 @@ func (v *Handler) Process(ctx context.Context, outboundRay ray.OutboundRay, dial } bodyWriter := session.EncodeRequestBody(request, writer) - firstPayload, err := input.ReadTimeout(time.Millisecond * 500) - if err != nil && err != buf.ErrReadTimeout { - return newError("failed to get first payload").Base(err) - } - if !firstPayload.IsEmpty() { - if err := bodyWriter.WriteMultiBuffer(firstPayload); err != nil { - return newError("failed to write first payload").Base(err) + { + firstPayload, err := input.ReadTimeout(time.Millisecond * 500) + if err != nil && err != buf.ErrReadTimeout { + return newError("failed to get first payload").Base(err) + } + if !firstPayload.IsEmpty() { + if err := bodyWriter.WriteMultiBuffer(firstPayload); err != nil { + return newError("failed to write first payload").Base(err) + } } - firstPayload.Release() } if err := writer.SetBuffered(false); err != nil { From 47c92fafc83cab4c6500fafa1b14300b5014d9e3 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 28 Mar 2018 22:23:38 +0200 Subject: [PATCH 140/183] clear leftOver after write --- common/buf/reader.go | 1 + 1 file changed, 1 insertion(+) diff --git a/common/buf/reader.go b/common/buf/reader.go index 7c9fa8ce8..3324ad88f 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -154,6 +154,7 @@ func (r *BufferedReader) writeToInternal(writer io.Writer) (int64, error) { if err := mbWriter.WriteMultiBuffer(r.leftOver); err != nil { return 0, err } + r.leftOver = nil } for { From 141b31eb19ea34d406bd7223d47102130d7959c1 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 28 Mar 2018 22:23:49 +0200 Subject: [PATCH 141/183] simplify code --- common/buf/buffer_pool.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/buf/buffer_pool.go b/common/buf/buffer_pool.go index 728228f3f..fac6aa304 100644 --- a/common/buf/buffer_pool.go +++ b/common/buf/buffer_pool.go @@ -49,8 +49,7 @@ func freeBytes(b []byte) { size := uint32(cap(b)) b = b[0:cap(b)] for i := numPools - 1; i >= 0; i-- { - ps := poolSize[i] - if size >= ps { + if size >= poolSize[i] { pool[i].Put(b) return } From fc7da93d06febf7f18e44cdc5cbfb977b8827758 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 28 Mar 2018 22:24:01 +0200 Subject: [PATCH 142/183] less cap --- common/crypto/auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 84f5b41ab..416645315 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -204,7 +204,7 @@ func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error { func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error { defer mb.Release() - mb2Write := buf.NewMultiBufferCap(len(mb) * 2) + mb2Write := buf.NewMultiBufferCap(len(mb) + 1) for { b := mb.SplitFirst() From babd0107cfd7b8254da14fb61c739dc5bdc94089 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 29 Mar 2018 10:40:18 +0200 Subject: [PATCH 143/183] prevent empty read in Reader --- common/buf/reader.go | 48 ++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/common/buf/reader.go b/common/buf/reader.go index 3324ad88f..782f5642d 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -23,15 +23,19 @@ const xlSize = 128 * 1024 func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) { b := New() - err := b.Reset(ReadFrom(r.Reader)) - if b.IsFull() { - r.buffer = newBytes(Size + 1) + for { + err := b.Reset(ReadFrom(r.Reader)) + if b.IsFull() { + r.buffer = newBytes(Size + 1) + } + if !b.IsEmpty() { + return NewMultiBufferValue(b), nil + } + if err != nil { + b.Release() + return nil, err + } } - if !b.IsEmpty() { - return NewMultiBufferValue(b), nil - } - b.Release() - return nil, err } // ReadMultiBuffer implements Reader. @@ -40,22 +44,26 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) { return r.readSmall() } - nBytes, err := r.Reader.Read(r.buffer) - if nBytes > 0 { - mb := NewMultiBufferCap(nBytes/Size + 1) - mb.Write(r.buffer[:nBytes]) - if nBytes == len(r.buffer) && nBytes < xlSize { - freeBytes(r.buffer) - r.buffer = newBytes(uint32(nBytes) + 1) - } else if nBytes < Size { + for { + nBytes, err := r.Reader.Read(r.buffer) + if nBytes > 0 { + mb := NewMultiBufferCap(nBytes/Size + 1) + mb.Write(r.buffer[:nBytes]) + if nBytes == len(r.buffer) && nBytes < xlSize { + freeBytes(r.buffer) + r.buffer = newBytes(uint32(nBytes) + 1) + } else if nBytes < Size { + freeBytes(r.buffer) + r.buffer = nil + } + return mb, nil + } + if err != nil { freeBytes(r.buffer) r.buffer = nil + return nil, err } - return mb, nil } - freeBytes(r.buffer) - r.buffer = nil - return nil, err } // BufferedReader is a Reader that keeps its internal buffer. From bb46a96f04ac321eb369cb0626791153937d75ee Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 29 Mar 2018 18:15:50 +0200 Subject: [PATCH 144/183] refine handling for empty read --- common/buf/reader.go | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/common/buf/reader.go b/common/buf/reader.go index 782f5642d..8ad3bba17 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -23,7 +23,7 @@ const xlSize = 128 * 1024 func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) { b := New() - for { + for i := 0; i < 64; i++ { err := b.Reset(ReadFrom(r.Reader)) if b.IsFull() { r.buffer = newBytes(Size + 1) @@ -36,6 +36,13 @@ func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) { return nil, err } } + + return nil, newError("Reader returns too many empty payloads.") +} + +func (r *BytesToBufferReader) freeBuffer() { + freeBytes(r.buffer) + r.buffer = nil } // ReadMultiBuffer implements Reader. @@ -44,26 +51,27 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) { return r.readSmall() } - for { - nBytes, err := r.Reader.Read(r.buffer) - if nBytes > 0 { - mb := NewMultiBufferCap(nBytes/Size + 1) - mb.Write(r.buffer[:nBytes]) - if nBytes == len(r.buffer) && nBytes < xlSize { - freeBytes(r.buffer) - r.buffer = newBytes(uint32(nBytes) + 1) - } else if nBytes < Size { - freeBytes(r.buffer) - r.buffer = nil - } - return mb, nil - } - if err != nil { + nBytes, err := r.Reader.Read(r.buffer) + if nBytes > 0 { + mb := NewMultiBufferCap(nBytes/Size + 1) + mb.Write(r.buffer[:nBytes]) + if nBytes == len(r.buffer) && nBytes < xlSize { freeBytes(r.buffer) - r.buffer = nil - return nil, err + r.buffer = newBytes(uint32(nBytes) + 1) + } else if nBytes < Size { + r.freeBuffer() } + return mb, nil } + + r.freeBuffer() + + if err != nil { + return nil, err + } + + // Read() returns empty payload and nil err. We don't expect this to happen, but just in case. + return r.readSmall() } // BufferedReader is a Reader that keeps its internal buffer. From fa6ff77ceebd9569c92222e265f8dba49d9d8d42 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 29 Mar 2018 21:40:23 +0200 Subject: [PATCH 145/183] prevent appending nil buffer --- common/buf/multi_buffer.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/buf/multi_buffer.go b/common/buf/multi_buffer.go index a442ce115..a245123c8 100644 --- a/common/buf/multi_buffer.go +++ b/common/buf/multi_buffer.go @@ -57,7 +57,9 @@ func NewMultiBufferValue(b ...*Buffer) MultiBuffer { // Append appends buffer to the end of this MultiBuffer func (mb *MultiBuffer) Append(buf *Buffer) { - *mb = append(*mb, buf) + if buf != nil { + *mb = append(*mb, buf) + } } // AppendMulti appends a MultiBuffer to the end of this one. From 24f393a23f826bdb1903f49b43386c5bb9577c62 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 29 Mar 2018 21:52:43 +0200 Subject: [PATCH 146/183] Update version --- core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.go b/core.go index 607f94f5a..bcc3fc686 100644 --- a/core.go +++ b/core.go @@ -16,7 +16,7 @@ import ( ) var ( - version = "3.15" + version = "3.16" build = "Custom" codename = "die Commanderin" intro = "An unified platform for anti-censorship." From 35e160a1ff8efad8f62acacbce5ad585a7e2ab85 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 30 Mar 2018 19:56:59 +0200 Subject: [PATCH 147/183] stats feature --- app/stats/config.go | 13 +++++++ app/stats/config.pb.go | 42 +++++++++++++++++++++ app/stats/config.proto | 11 ++++++ app/stats/errors.generated.go | 5 +++ app/stats/stats.go | 68 +++++++++++++++++++++++++++++++++ app/stats/stats_test.go | 15 ++++++++ config.pb.go | 10 +++-- stats.go | 71 +++++++++++++++++++++++++++++++++++ v2ray.go | 9 ++++- 9 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 app/stats/config.go create mode 100644 app/stats/config.pb.go create mode 100644 app/stats/config.proto create mode 100644 app/stats/errors.generated.go create mode 100644 app/stats/stats.go create mode 100644 app/stats/stats_test.go create mode 100644 stats.go diff --git a/app/stats/config.go b/app/stats/config.go new file mode 100644 index 000000000..4e3490034 --- /dev/null +++ b/app/stats/config.go @@ -0,0 +1,13 @@ +package stats + +import ( + "context" + + "v2ray.com/core/common" +) + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return NewManager(ctx, config.(*Config)) + })) +} diff --git a/app/stats/config.pb.go b/app/stats/config.pb.go new file mode 100644 index 000000000..a4b3d244a --- /dev/null +++ b/app/stats/config.pb.go @@ -0,0 +1,42 @@ +package stats + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Config struct { +} + +func (m *Config) Reset() { *m = Config{} } +func (m *Config) String() string { return proto.CompactTextString(m) } +func (*Config) ProtoMessage() {} +func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func init() { + proto.RegisterType((*Config)(nil), "v2ray.core.app.stats.Config") +} + +func init() { proto.RegisterFile("v2ray.com/core/app/stats/config.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 123 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2d, 0x33, 0x2a, 0x4a, + 0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x4f, 0x2c, 0x28, 0xd0, 0x2f, + 0x2e, 0x49, 0x2c, 0x29, 0xd6, 0x4f, 0xce, 0xcf, 0x4b, 0xcb, 0x4c, 0xd7, 0x2b, 0x28, 0xca, 0x2f, + 0xc9, 0x17, 0x12, 0x81, 0x29, 0x2b, 0x4a, 0xd5, 0x4b, 0x2c, 0x28, 0xd0, 0x03, 0x2b, 0x51, 0xe2, + 0xe0, 0x62, 0x73, 0x06, 0xab, 0x72, 0xb2, 0xe2, 0x92, 0x48, 0xce, 0xcf, 0xd5, 0xc3, 0xa6, 0x2a, + 0x80, 0x31, 0x8a, 0x15, 0xcc, 0x58, 0xc5, 0x24, 0x12, 0x66, 0x14, 0x94, 0x58, 0xa9, 0xe7, 0x0c, + 0x92, 0x77, 0x2c, 0x28, 0xd0, 0x0b, 0x06, 0x09, 0x27, 0xb1, 0x81, 0xad, 0x30, 0x06, 0x04, 0x00, + 0x00, 0xff, 0xff, 0x88, 0x24, 0xc6, 0x41, 0x8b, 0x00, 0x00, 0x00, +} diff --git a/app/stats/config.proto b/app/stats/config.proto new file mode 100644 index 000000000..9407fd391 --- /dev/null +++ b/app/stats/config.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package v2ray.core.app.stats; +option csharp_namespace = "V2Ray.Core.App.Stats"; +option go_package = "stats"; +option java_package = "com.v2ray.core.app.stats"; +option java_multiple_files = true; + +message Config { + +} diff --git a/app/stats/errors.generated.go b/app/stats/errors.generated.go new file mode 100644 index 000000000..1bfbe328a --- /dev/null +++ b/app/stats/errors.generated.go @@ -0,0 +1,5 @@ +package stats + +import "v2ray.com/core/common/errors" + +func newError(values ...interface{}) *errors.Error { return errors.New(values...).Path("App", "Stats") } diff --git a/app/stats/stats.go b/app/stats/stats.go new file mode 100644 index 000000000..9177e2a20 --- /dev/null +++ b/app/stats/stats.go @@ -0,0 +1,68 @@ +package stats + +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg stats -path App,Stats + +import ( + "context" + "sync" + "sync/atomic" + + "v2ray.com/core" +) + +type Counter struct { + value int64 +} + +func (c *Counter) Value() int64 { + return atomic.LoadInt64(&c.value) +} + +func (c *Counter) Exchange(newValue int64) int64 { + return atomic.SwapInt64(&c.value, newValue) +} + +func (c *Counter) Add(delta int64) int64 { + return atomic.AddInt64(&c.value, delta) +} + +type Manager struct { + access sync.RWMutex + counters map[string]*Counter +} + +func NewManager(ctx context.Context, config *Config) (*Manager, error) { + return &Manager{ + counters: make(map[string]*Counter), + }, nil +} + +func (m *Manager) RegisterCounter(name string) (core.StatCounter, error) { + m.access.Lock() + defer m.access.Unlock() + + if _, found := m.counters[name]; found { + return nil, newError("Counter ", name, " already registered.") + } + c := new(Counter) + m.counters[name] = c + return c, nil +} + +func (m *Manager) GetCounter(name string) core.StatCounter { + m.access.RLock() + defer m.access.RUnlock() + + if c, found := m.counters[name]; found { + return c + } + return nil +} + +func (m *Manager) Start() error { + return nil +} + +func (m *Manager) Close() error { + return nil +} diff --git a/app/stats/stats_test.go b/app/stats/stats_test.go new file mode 100644 index 000000000..9a3a1e5cd --- /dev/null +++ b/app/stats/stats_test.go @@ -0,0 +1,15 @@ +package stats_test + +import ( + "testing" + + "v2ray.com/core" + . "v2ray.com/core/app/stats" + . "v2ray.com/ext/assert" +) + +func TestInternface(t *testing.T) { + assert := With(t) + + assert((*Manager)(nil), Implements, (*core.StatManager)(nil)) +} diff --git a/config.pb.go b/config.pb.go index f63e1f5ab..beee21273 100644 --- a/config.pb.go +++ b/config.pb.go @@ -23,7 +23,7 @@ type Config struct { Inbound []*InboundHandlerConfig `protobuf:"bytes,1,rep,name=inbound" json:"inbound,omitempty"` // Outbound handler configurations. Must have at least one item. The first item is used as default for routing. Outbound []*OutboundHandlerConfig `protobuf:"bytes,2,rep,name=outbound" json:"outbound,omitempty"` - // App configuration. Must be one in the app directory. + // App is for configurations of all features in V2Ray. A feature must implement the Feature interface, and its config type must be registered through common.RegisterConfig. App []*v2ray_core_common_serial.TypedMessage `protobuf:"bytes,4,rep,name=app" json:"app,omitempty"` // Transport settings. Transport *v2ray_core_transport.Config `protobuf:"bytes,5,opt,name=transport" json:"transport,omitempty"` @@ -72,10 +72,11 @@ func (m *Config) GetExtension() []*v2ray_core_common_serial.TypedMessage { return nil } +// InboundHandlerConfig is the configuration for inbound handler. type InboundHandlerConfig struct { - // Tag of the inbound handler. + // Tag of the inbound handler. The tag must be unique among all inbound handlers Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` - // Settings for how this inbound proxy is handled. Must be ReceiverConfig above. + // Settings for how this inbound proxy is handled. ReceiverSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=receiver_settings,json=receiverSettings" json:"receiver_settings,omitempty"` // Settings for inbound proxy. Must be one of the inbound proxies. ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"` @@ -107,10 +108,11 @@ func (m *InboundHandlerConfig) GetProxySettings() *v2ray_core_common_serial.Type return nil } +// OutboundHandlerConfig is the configuration for outbound handler. type OutboundHandlerConfig struct { // Tag of this outbound handler. Tag string `protobuf:"bytes,1,opt,name=tag" json:"tag,omitempty"` - // Settings for how to dial connection for this outbound handler. Must be SenderConfig above. + // Settings for how to dial connection for this outbound handler. SenderSettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,2,opt,name=sender_settings,json=senderSettings" json:"sender_settings,omitempty"` // Settings for this outbound proxy. Must be one of the outbound proxies. ProxySettings *v2ray_core_common_serial.TypedMessage `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings" json:"proxy_settings,omitempty"` diff --git a/stats.go b/stats.go new file mode 100644 index 000000000..42eb5e74c --- /dev/null +++ b/stats.go @@ -0,0 +1,71 @@ +package core + +import ( + "sync" +) + +type StatCounter interface { + Value() int64 + Exchange(int64) int64 + Add(int64) int64 +} + +type StatManager interface { + Feature + + RegisterCounter(string) (StatCounter, error) + GetCounter(string) StatCounter +} + +type syncStatManager struct { + sync.RWMutex + StatManager +} + +func (s *syncStatManager) Start() error { + s.RLock() + defer s.RUnlock() + + if s.StatManager == nil { + return newError("StatManager not set.") + } + + return s.StatManager.Start() +} + +func (s *syncStatManager) Close() error { + s.RLock() + defer s.RUnlock() + + if s.StatManager == nil { + return newError("StatManager not set.") + } + return s.StatManager.Close() +} + +func (s *syncStatManager) RegisterCounter(name string) (StatCounter, error) { + s.RLock() + defer s.RUnlock() + + if s.StatManager == nil { + return nil, newError("StatManager not set.") + } + return s.StatManager.RegisterCounter(name) +} + +func (s *syncStatManager) GetCounter(name string) StatCounter { + s.RLock() + defer s.RUnlock() + + if s.StatManager == nil { + return nil + } + return s.StatManager.GetCounter(name) +} + +func (s *syncStatManager) Set(m StatManager) { + s.Lock() + defer s.Unlock() + + s.StatManager = m +} diff --git a/v2ray.go b/v2ray.go index d68842169..8bd4dc718 100755 --- a/v2ray.go +++ b/v2ray.go @@ -28,6 +28,7 @@ type Instance struct { router syncRouter ihm syncInboundHandlerManager ohm syncOutboundHandlerManager + stats syncStatManager access sync.Mutex features []Feature @@ -148,6 +149,8 @@ func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error s.ihm.Set(instance.(InboundHandlerManager)) case OutboundHandlerManager, *OutboundHandlerManager: s.ohm.Set(instance.(OutboundHandlerManager)) + case StatManager, *StatManager: + s.stats.Set(instance.(StatManager)) default: s.access.Lock() s.features = append(s.features, instance) @@ -162,7 +165,7 @@ func (s *Instance) RegisterFeature(feature interface{}, instance Feature) error } func (s *Instance) allFeatures() []Feature { - return append([]Feature{s.DNSClient(), s.PolicyManager(), s.Dispatcher(), s.Router(), s.InboundHandlerManager(), s.OutboundHandlerManager()}, s.features...) + return append([]Feature{s.DNSClient(), s.PolicyManager(), s.Dispatcher(), s.Router(), s.InboundHandlerManager(), s.OutboundHandlerManager(), s.Stats()}, s.features...) } // GetFeature returns a feature that was registered in this Instance. Nil if not found. @@ -207,3 +210,7 @@ func (s *Instance) InboundHandlerManager() InboundHandlerManager { func (s *Instance) OutboundHandlerManager() OutboundHandlerManager { return &(s.ohm) } + +func (s *Instance) Stats() StatManager { + return &(s.stats) +} From f7dd7e627940bec864090d18fade845296a769f2 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 30 Mar 2018 23:17:28 +0200 Subject: [PATCH 148/183] support stats counter in ray stream --- app/dispatcher/default.go | 2 +- app/proxyman/mux/mux.go | 4 +-- app/proxyman/outbound/handler.go | 2 +- app/stats/stats.go | 2 +- app/stats/stats_test.go | 17 ++++++++++ stats.go | 2 +- transport/internet/udp/dispatcher_test.go | 2 +- transport/ray/direct.go | 38 ++++++++++++++++++----- transport/ray/direct_test.go | 16 ++++++++++ 9 files changed, 71 insertions(+), 14 deletions(-) diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 50f39741b..3987ee53b 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -54,7 +54,7 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin } ctx = proxy.ContextWithTarget(ctx, destination) - outbound := ray.NewRay(ctx) + outbound := ray.New(ctx) snifferList := proxyman.ProtocolSniffersFromContext(ctx) if destination.Address.Family().IsDomain() || len(snifferList) == 0 { go d.routedDispatch(ctx, outbound, destination) diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index c46312bc4..a94c416de 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -87,7 +87,7 @@ var muxCoolPort = net.Port(9527) func NewClient(p proxy.Outbound, dialer proxy.Dialer, m *ClientManager) (*Client, error) { ctx := proxy.ContextWithTarget(context.Background(), net.TCPDestination(muxCoolAddress, muxCoolPort)) ctx, cancel := context.WithCancel(ctx) - pipe := ray.NewRay(ctx) + pipe := ray.New(ctx) c := &Client{ sessionManager: NewSessionManager(), @@ -266,7 +266,7 @@ func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (ray.Inboun return s.dispatcher.Dispatch(ctx, dest) } - ray := ray.NewRay(ctx) + ray := ray.New(ctx) worker := &ServerWorker{ dispatcher: s.dispatcher, outboundRay: ray, diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index a70513078..3bc57ea08 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -103,7 +103,7 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn if handler != nil { newError("proxying to ", tag, " for dest ", dest).AtDebug().WithContext(ctx).WriteToLog() ctx = proxy.ContextWithTarget(ctx, dest) - stream := ray.NewRay(ctx) + stream := ray.New(ctx) go handler.Dispatch(ctx, stream) return ray.NewConnection(stream.InboundOutput(), stream.InboundInput()), nil } diff --git a/app/stats/stats.go b/app/stats/stats.go index 9177e2a20..622982387 100644 --- a/app/stats/stats.go +++ b/app/stats/stats.go @@ -18,7 +18,7 @@ func (c *Counter) Value() int64 { return atomic.LoadInt64(&c.value) } -func (c *Counter) Exchange(newValue int64) int64 { +func (c *Counter) Set(newValue int64) int64 { return atomic.SwapInt64(&c.value, newValue) } diff --git a/app/stats/stats_test.go b/app/stats/stats_test.go index 9a3a1e5cd..51a145acf 100644 --- a/app/stats/stats_test.go +++ b/app/stats/stats_test.go @@ -1,10 +1,12 @@ package stats_test import ( + "context" "testing" "v2ray.com/core" . "v2ray.com/core/app/stats" + "v2ray.com/core/common" . "v2ray.com/ext/assert" ) @@ -13,3 +15,18 @@ func TestInternface(t *testing.T) { assert((*Manager)(nil), Implements, (*core.StatManager)(nil)) } + +func TestStatsCounter(t *testing.T) { + assert := With(t) + + raw, err := common.CreateObject(context.Background(), &Config{}) + assert(err, IsNil) + + m := raw.(core.StatManager) + c, err := m.RegisterCounter("test.counter") + assert(err, IsNil) + + assert(c.Add(1), Equals, int64(1)) + assert(c.Set(0), Equals, int64(1)) + assert(c.Value(), Equals, int64(0)) +} diff --git a/stats.go b/stats.go index 42eb5e74c..0505c0b13 100644 --- a/stats.go +++ b/stats.go @@ -6,7 +6,7 @@ import ( type StatCounter interface { Value() int64 - Exchange(int64) int64 + Set(int64) int64 Add(int64) int64 } diff --git a/transport/internet/udp/dispatcher_test.go b/transport/internet/udp/dispatcher_test.go index e6c1958ab..22f4a4277 100644 --- a/transport/internet/udp/dispatcher_test.go +++ b/transport/internet/udp/dispatcher_test.go @@ -33,7 +33,7 @@ func TestSameDestinationDispatching(t *testing.T) { assert := With(t) ctx, cancel := context.WithCancel(context.Background()) - link := ray.NewRay(ctx) + link := ray.New(ctx) go func() { for { data, err := link.OutboundInput().ReadMultiBuffer() diff --git a/transport/ray/direct.go b/transport/ray/direct.go index 9be2aff64..ab48021a2 100644 --- a/transport/ray/direct.go +++ b/transport/ray/direct.go @@ -12,11 +12,25 @@ import ( "v2ray.com/core/common/signal" ) -// NewRay creates a new Ray for direct traffic transport. -func NewRay(ctx context.Context) Ray { +type Option func(*Stream) + +type addInt64 interface { + Add(int64) int64 +} + +func WithStatCounter(c addInt64) Option { + return func(s *Stream) { + s.onDataSize = append(s.onDataSize, func(delta uint64) { + c.Add(int64(delta)) + }) + } +} + +// New creates a new Ray for direct traffic transport. +func New(ctx context.Context, opts ...Option) Ray { return &directRay{ - Input: NewStream(ctx), - Output: NewStream(ctx), + Input: NewStream(ctx, opts...), + Output: NewStream(ctx, opts...), } } @@ -60,18 +74,23 @@ type Stream struct { ctx context.Context readSignal *signal.Notifier writeSignal *signal.Notifier + onDataSize []func(uint64) close bool err bool } // NewStream creates a new Stream. -func NewStream(ctx context.Context) *Stream { - return &Stream{ +func NewStream(ctx context.Context, opts ...Option) *Stream { + s := &Stream{ ctx: ctx, readSignal: signal.NewNotifier(), writeSignal: signal.NewNotifier(), size: 0, } + for _, opt := range opts { + opt(s) + } + return s } func (s *Stream) getData() (buf.MultiBuffer, error) { @@ -201,8 +220,13 @@ func (s *Stream) WriteMultiBuffer(data buf.MultiBuffer) error { s.data = buf.NewMultiBufferCap(128) } + dataSize := uint64(data.Len()) + for _, f := range s.onDataSize { + f(dataSize) + } + s.data.AppendMulti(data) - s.size += uint64(data.Len()) + s.size += dataSize s.writeSignal.Signal() return nil diff --git a/transport/ray/direct_test.go b/transport/ray/direct_test.go index 64c0ab1d0..c365ac621 100644 --- a/transport/ray/direct_test.go +++ b/transport/ray/direct_test.go @@ -5,6 +5,7 @@ import ( "io" "testing" + "v2ray.com/core/app/stats" "v2ray.com/core/common/buf" . "v2ray.com/core/transport/ray" . "v2ray.com/ext/assert" @@ -47,3 +48,18 @@ func TestStreamClose(t *testing.T) { _, err = stream.ReadMultiBuffer() assert(err, Equals, io.EOF) } + +func TestStreamStatCounter(t *testing.T) { + assert := With(t) + + c := new(stats.Counter) + stream := NewStream(context.Background(), WithStatCounter(c)) + + b1 := buf.New() + b1.AppendBytes('a', 'b', 'c', 'd') + assert(stream.WriteMultiBuffer(buf.NewMultiBufferValue(b1)), IsNil) + + stream.Close() + + assert(c.Value(), Equals, int64(4)) +} From 695a4f8493cfe485a1ef45f6e59dc7f109f830be Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 30 Mar 2018 23:39:54 +0200 Subject: [PATCH 149/183] support stats in policy --- app/policy/config.go | 7 ++++ app/policy/config.pb.go | 75 ++++++++++++++++++++++++++++------------- app/policy/config.proto | 5 +++ policy.go | 8 +++++ 4 files changed, 72 insertions(+), 23 deletions(-) diff --git a/app/policy/config.go b/app/policy/config.go index 4fc14f8c5..3504d16f9 100644 --- a/app/policy/config.go +++ b/app/policy/config.go @@ -46,6 +46,10 @@ func (p *Policy) overrideWith(another *Policy) { if another.Timeout != nil { p.Timeout.overrideWith(another.Timeout) } + if another.Stats != nil && p.Stats == nil { + p.Stats = new(Policy_Stats) + *p.Stats = *another.Stats + } } func (p *Policy) ToCorePolicy() core.Policy { @@ -56,5 +60,8 @@ func (p *Policy) ToCorePolicy() core.Policy { cp.Timeouts.DownlinkOnly = p.Timeout.DownlinkOnly.Duration() cp.Timeouts.UplinkOnly = p.Timeout.UplinkOnly.Duration() } + if p.Stats != nil { + cp.Stats.EnablePerUser = p.Stats.EnablePerUser + } return cp } diff --git a/app/policy/config.pb.go b/app/policy/config.pb.go index 98063a4b9..c02dc16a7 100644 --- a/app/policy/config.pb.go +++ b/app/policy/config.pb.go @@ -33,6 +33,7 @@ func (m *Second) GetValue() uint32 { type Policy struct { Timeout *Policy_Timeout `protobuf:"bytes,1,opt,name=timeout" json:"timeout,omitempty"` + Stats *Policy_Stats `protobuf:"bytes,2,opt,name=stats" json:"stats,omitempty"` } func (m *Policy) Reset() { *m = Policy{} } @@ -47,6 +48,13 @@ func (m *Policy) GetTimeout() *Policy_Timeout { return nil } +func (m *Policy) GetStats() *Policy_Stats { + if m != nil { + return m.Stats + } + return nil +} + // Timeout is a message for timeout settings in various stages, in seconds. type Policy_Timeout struct { Handshake *Second `protobuf:"bytes,1,opt,name=handshake" json:"handshake,omitempty"` @@ -88,6 +96,22 @@ func (m *Policy_Timeout) GetDownlinkOnly() *Second { return nil } +type Policy_Stats struct { + EnablePerUser bool `protobuf:"varint,1,opt,name=enable_per_user,json=enablePerUser" json:"enable_per_user,omitempty"` +} + +func (m *Policy_Stats) Reset() { *m = Policy_Stats{} } +func (m *Policy_Stats) String() string { return proto.CompactTextString(m) } +func (*Policy_Stats) ProtoMessage() {} +func (*Policy_Stats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 1} } + +func (m *Policy_Stats) GetEnablePerUser() bool { + if m != nil { + return m.EnablePerUser + } + return false +} + type Config struct { Level map[uint32]*Policy `protobuf:"bytes,1,rep,name=level" json:"level,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` } @@ -108,33 +132,38 @@ func init() { proto.RegisterType((*Second)(nil), "v2ray.core.app.policy.Second") proto.RegisterType((*Policy)(nil), "v2ray.core.app.policy.Policy") proto.RegisterType((*Policy_Timeout)(nil), "v2ray.core.app.policy.Policy.Timeout") + proto.RegisterType((*Policy_Stats)(nil), "v2ray.core.app.policy.Policy.Stats") proto.RegisterType((*Config)(nil), "v2ray.core.app.policy.Config") } func init() { proto.RegisterFile("v2ray.com/core/app/policy/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 349 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0x4a, 0xeb, 0x40, - 0x14, 0x86, 0x49, 0x7a, 0x9b, 0x72, 0x4f, 0x6f, 0xaf, 0x32, 0x58, 0x88, 0x05, 0xa5, 0x14, 0x94, - 0xae, 0x26, 0x90, 0x6e, 0x44, 0xb1, 0x62, 0x45, 0x41, 0x10, 0x2c, 0x51, 0x14, 0xdc, 0x94, 0x71, - 0x32, 0xda, 0xd0, 0xe9, 0x9c, 0x21, 0xa6, 0x95, 0xbc, 0x86, 0x6f, 0xe0, 0xd6, 0x87, 0xf2, 0x59, - 0x24, 0x99, 0x84, 0x6c, 0x5a, 0xe9, 0x6e, 0x72, 0xf8, 0xfe, 0x8f, 0x43, 0xfe, 0x03, 0x87, 0x4b, - 0x3f, 0x66, 0x29, 0xe5, 0x38, 0xf7, 0x38, 0xc6, 0xc2, 0x63, 0x5a, 0x7b, 0x1a, 0x65, 0xc4, 0x53, - 0x8f, 0xa3, 0x7a, 0x89, 0x5e, 0xa9, 0x8e, 0x31, 0x41, 0xd2, 0x2e, 0xb9, 0x58, 0x50, 0xa6, 0x35, - 0x35, 0x4c, 0x6f, 0x1f, 0x9c, 0x3b, 0xc1, 0x51, 0x85, 0x64, 0x07, 0xea, 0x4b, 0x26, 0x17, 0xc2, - 0xb5, 0xba, 0x56, 0xbf, 0x15, 0x98, 0x8f, 0xde, 0xb7, 0x0d, 0xce, 0x38, 0x47, 0xc9, 0x19, 0x34, - 0x92, 0x68, 0x2e, 0x70, 0x91, 0xe4, 0x48, 0xd3, 0x3f, 0xa0, 0x2b, 0x9d, 0xd4, 0xf0, 0xf4, 0xde, - 0xc0, 0x41, 0x99, 0xea, 0x7c, 0xd8, 0xd0, 0x28, 0x86, 0xe4, 0x04, 0xfe, 0x4e, 0x99, 0x0a, 0xdf, - 0xa6, 0x6c, 0x26, 0x0a, 0xdd, 0xde, 0x1a, 0x9d, 0xd9, 0x2f, 0xa8, 0x78, 0x72, 0x05, 0x5b, 0x1c, - 0x95, 0x12, 0x3c, 0x89, 0x50, 0x4d, 0xa2, 0x50, 0x0a, 0xd7, 0xde, 0x44, 0xf1, 0xbf, 0x4a, 0x5d, - 0x87, 0x52, 0x90, 0x21, 0x34, 0x17, 0x5a, 0x46, 0x6a, 0x36, 0x41, 0x25, 0x53, 0xb7, 0xb6, 0x89, - 0x03, 0x4c, 0xe2, 0x56, 0xc9, 0x94, 0x8c, 0xa0, 0x15, 0xe2, 0xbb, 0xaa, 0x0c, 0x7f, 0x36, 0x31, - 0xfc, 0x2b, 0x33, 0x99, 0xa3, 0xf7, 0x69, 0x81, 0x73, 0x91, 0x17, 0x45, 0x86, 0x50, 0x97, 0x62, - 0x29, 0xa4, 0x6b, 0x75, 0x6b, 0xfd, 0xa6, 0xdf, 0x5f, 0xa3, 0x31, 0x34, 0xbd, 0xc9, 0xd0, 0x4b, - 0x95, 0xc4, 0x69, 0x60, 0x62, 0x9d, 0x47, 0x80, 0x6a, 0x48, 0xb6, 0xa1, 0x36, 0x13, 0x69, 0xd1, - 0x66, 0xf6, 0x24, 0x83, 0xb2, 0xe1, 0xdf, 0x7f, 0x96, 0xa9, 0xaf, 0x38, 0x80, 0x63, 0xfb, 0xc8, - 0x1a, 0x9d, 0xc2, 0x2e, 0xc7, 0xf9, 0x6a, 0x7c, 0x6c, 0x3d, 0x39, 0xe6, 0xf5, 0x65, 0xb7, 0x1f, - 0xfc, 0x80, 0x65, 0x0b, 0xc6, 0x82, 0x9e, 0x6b, 0x5d, 0x98, 0x9e, 0x9d, 0xfc, 0x02, 0x07, 0x3f, - 0x01, 0x00, 0x00, 0xff, 0xff, 0xcf, 0x25, 0x25, 0xc2, 0xab, 0x02, 0x00, 0x00, + // 403 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xdd, 0x8a, 0xd3, 0x40, + 0x14, 0xc7, 0x49, 0x62, 0xb2, 0x7a, 0x6a, 0x5d, 0x19, 0x5c, 0x88, 0x05, 0x65, 0xa9, 0xb8, 0xf4, + 0x6a, 0x02, 0xd9, 0x1b, 0x3f, 0x70, 0xc5, 0x15, 0x05, 0x41, 0xb0, 0xa4, 0x7e, 0x80, 0x37, 0x61, + 0x3a, 0x39, 0xda, 0xd0, 0xe9, 0xcc, 0x30, 0x49, 0x2a, 0x79, 0x0d, 0xdf, 0xc0, 0x5b, 0x1f, 0xc5, + 0xa7, 0x92, 0x64, 0x12, 0x72, 0xd3, 0x76, 0x7b, 0x37, 0x19, 0x7e, 0xff, 0x5f, 0x4e, 0x4e, 0xfe, + 0x70, 0xb1, 0x8d, 0x0d, 0xab, 0x29, 0x57, 0x9b, 0x88, 0x2b, 0x83, 0x11, 0xd3, 0x3a, 0xd2, 0x4a, + 0xe4, 0xbc, 0x8e, 0xb8, 0x92, 0x3f, 0xf2, 0x9f, 0x54, 0x1b, 0x55, 0x2a, 0x72, 0xd6, 0x73, 0x06, + 0x29, 0xd3, 0x9a, 0x5a, 0x66, 0xfa, 0x18, 0x82, 0x05, 0x72, 0x25, 0x33, 0xf2, 0x00, 0xfc, 0x2d, + 0x13, 0x15, 0x86, 0xce, 0xb9, 0x33, 0x1b, 0x27, 0xf6, 0x61, 0xfa, 0xcf, 0x83, 0x60, 0xde, 0xa2, + 0xe4, 0x35, 0x9c, 0x94, 0xf9, 0x06, 0x55, 0x55, 0xb6, 0xc8, 0x28, 0x7e, 0x4a, 0x77, 0x3a, 0xa9, + 0xe5, 0xe9, 0x67, 0x0b, 0x27, 0x7d, 0x8a, 0x3c, 0x07, 0xbf, 0x28, 0x59, 0x59, 0x84, 0x6e, 0x1b, + 0x7f, 0x72, 0x38, 0xbe, 0x68, 0xd0, 0xc4, 0x26, 0x26, 0xbf, 0x5d, 0x38, 0xe9, 0x7c, 0xe4, 0x25, + 0xdc, 0x59, 0x31, 0x99, 0x15, 0x2b, 0xb6, 0xc6, 0x6e, 0x92, 0x47, 0x7b, 0x54, 0xf6, 0xd3, 0x92, + 0x81, 0x27, 0xef, 0xe1, 0x94, 0x2b, 0x29, 0x91, 0x97, 0xb9, 0x92, 0x69, 0x9e, 0x09, 0xec, 0xa6, + 0xb9, 0x41, 0x71, 0x6f, 0x48, 0x7d, 0xc8, 0x04, 0x92, 0x2b, 0x18, 0x55, 0x5a, 0xe4, 0x72, 0x9d, + 0x2a, 0x29, 0xea, 0xd0, 0x3b, 0xc6, 0x01, 0x36, 0xf1, 0x49, 0x8a, 0x9a, 0x5c, 0xc3, 0x38, 0x53, + 0xbf, 0xe4, 0x60, 0xb8, 0x75, 0x8c, 0xe1, 0x6e, 0x9f, 0x69, 0x1c, 0x93, 0x08, 0xfc, 0x76, 0x49, + 0xe4, 0x02, 0x4e, 0x51, 0xb2, 0xa5, 0xc0, 0x54, 0xa3, 0x49, 0xab, 0x02, 0x4d, 0xbb, 0x97, 0xdb, + 0xc9, 0xd8, 0x5e, 0xcf, 0xd1, 0x7c, 0x29, 0xd0, 0x4c, 0xff, 0x38, 0x10, 0xbc, 0x6d, 0x4b, 0x41, + 0xae, 0xc0, 0x17, 0xb8, 0x45, 0x11, 0x3a, 0xe7, 0xde, 0x6c, 0x14, 0xcf, 0xf6, 0xbc, 0xd7, 0xd2, + 0xf4, 0x63, 0x83, 0xbe, 0x93, 0xa5, 0xa9, 0x13, 0x1b, 0x9b, 0x7c, 0x03, 0x18, 0x2e, 0xc9, 0x7d, + 0xf0, 0xd6, 0x58, 0x77, 0xcd, 0x69, 0x8e, 0xe4, 0xb2, 0x6f, 0xd3, 0xe1, 0xed, 0xda, 0x7f, 0xdd, + 0x95, 0xed, 0x85, 0xfb, 0xcc, 0xb9, 0x7e, 0x05, 0x0f, 0xb9, 0xda, 0xec, 0xc6, 0xe7, 0xce, 0xf7, + 0xc0, 0x9e, 0xfe, 0xba, 0x67, 0x5f, 0xe3, 0x84, 0x35, 0x03, 0x1a, 0xa4, 0x6f, 0xb4, 0xee, 0x4c, + 0xcb, 0xa0, 0x6d, 0xfb, 0xe5, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0xad, 0x5f, 0x54, 0x17, + 0x03, 0x00, 0x00, } diff --git a/app/policy/config.proto b/app/policy/config.proto index b00aefc5c..2ca1ec1da 100644 --- a/app/policy/config.proto +++ b/app/policy/config.proto @@ -19,7 +19,12 @@ message Policy { Second downlink_only = 4; } + message Stats { + bool enable_per_user = 1; + } + Timeout timeout = 1; + Stats stats = 2; } message Config { diff --git a/policy.go b/policy.go index 52d84a8a7..ec04eaace 100644 --- a/policy.go +++ b/policy.go @@ -19,9 +19,14 @@ type TimeoutPolicy struct { DownlinkOnly time.Duration } +type StatsPolicy struct { + EnablePerUser bool +} + // Policy is session based settings for controlling V2Ray requests. It contains various settings (or limits) that may differ for different users in the context. type Policy struct { Timeouts TimeoutPolicy // Timeout settings + Stats StatsPolicy } // PolicyManager is a feature that provides Policy for the given user by its id or level. @@ -41,6 +46,9 @@ func DefaultPolicy() Policy { UplinkOnly: time.Second * 5, DownlinkOnly: time.Second * 30, }, + Stats: StatsPolicy{ + EnablePerUser: false, + }, } } From e5a5c30b67bc70543159be0e3f561dd43accfdd4 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 30 Mar 2018 23:48:45 +0200 Subject: [PATCH 150/183] support stats counter in dispatcher --- app/dispatcher/default.go | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 3987ee53b..39a2335b9 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -11,6 +11,7 @@ import ( "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" "v2ray.com/core/proxy" "v2ray.com/core/transport/ray" ) @@ -23,6 +24,8 @@ var ( type DefaultDispatcher struct { ohm core.OutboundHandlerManager router core.Router + policy core.PolicyManager + stats core.StatManager } // NewDefaultDispatcher create a new DefaultDispatcher. @@ -31,6 +34,8 @@ func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatch d := &DefaultDispatcher{ ohm: v.OutboundHandlerManager(), router: v.Router(), + policy: v.PolicyManager(), + stats: v.Stats(), } if err := v.RegisterFeature((*core.Dispatcher)(nil), d); err != nil { @@ -47,6 +52,10 @@ func (*DefaultDispatcher) Start() error { // Close implements app.Application. func (*DefaultDispatcher) Close() error { return nil } +func getStatsName(u *protocol.User) string { + return "user>traffic>" + u.Email +} + // Dispatch implements core.Dispatcher. func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (ray.InboundRay, error) { if !destination.IsValid() { @@ -54,7 +63,26 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin } ctx = proxy.ContextWithTarget(ctx, destination) - outbound := ray.New(ctx) + var rayOptions []ray.Option + + user := protocol.UserFromContext(ctx) + if user != nil && len(user.Email) > 0 { + name := getStatsName(user) + c, err := d.stats.RegisterCounter(name) + if err != nil { + c = d.stats.GetCounter(name) + } + if c == nil { + newError("failed to get stats counter ", name).AtWarning().WithContext(ctx).WriteToLog() + } + + p := d.policy.ForLevel(user.Level) + if p.Stats.EnablePerUser { + rayOptions = append(rayOptions, ray.WithStatCounter(c)) + } + } + + outbound := ray.New(ctx, rayOptions...) snifferList := proxyman.ProtocolSniffersFromContext(ctx) if destination.Address.Family().IsDomain() || len(snifferList) == 0 { go d.routedDispatch(ctx, outbound, destination) From eafb726a8d206eb80d599e2b06561e1e462b1b8c Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Fri, 30 Mar 2018 23:52:51 +0200 Subject: [PATCH 151/183] use go 1.10.1 --- .travis.yml | 2 +- release/release-ci.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7096eff13..b30581336 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: required language: go go: -- "1.10" +- "1.10.1" go_import_path: v2ray.com/core git: depth: 5 diff --git a/release/release-ci.sh b/release/release-ci.sh index fc1925578..40be7c73e 100755 --- a/release/release-ci.sh +++ b/release/release-ci.sh @@ -25,7 +25,7 @@ echo ${SIGN_KEY_PASS} | gpg --passphrase-fd 0 --batch --import /v2ray/build/sign curl -L -o /v2ray/build/releases https://api.github.com/repos/v2ray/v2ray-core/releases GO_INSTALL=golang.tar.gz -curl -L -o ${GO_INSTALL} https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz +curl -L -o ${GO_INSTALL} https://storage.googleapis.com/golang/go1.10.1.linux-amd64.tar.gz tar -C /usr/local -xzf ${GO_INSTALL} export PATH=$PATH:/usr/local/go/bin From 0975e26ed13848dabe88a4cf1c1fc2769ce62f29 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 31 Mar 2018 10:30:12 +0200 Subject: [PATCH 152/183] command handler for stats --- app/stats/command/command.go | 51 +++++++ app/stats/command/command.pb.go | 198 ++++++++++++++++++++++++++ app/stats/command/command.proto | 29 ++++ app/stats/command/errors.generated.go | 7 + app/stats/stats.go | 14 +- main/distro/all/all.go | 4 +- stats.go | 4 +- testing/scenarios/command_test.go | 182 ++++++++++++++++++++++- 8 files changed, 482 insertions(+), 7 deletions(-) create mode 100644 app/stats/command/command.go create mode 100644 app/stats/command/command.pb.go create mode 100644 app/stats/command/command.proto create mode 100644 app/stats/command/errors.generated.go diff --git a/app/stats/command/command.go b/app/stats/command/command.go new file mode 100644 index 000000000..5ff3e40eb --- /dev/null +++ b/app/stats/command/command.go @@ -0,0 +1,51 @@ +package command + +//go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg command -path App,Stats,Command + +import ( + "context" + + grpc "google.golang.org/grpc" + "v2ray.com/core" + "v2ray.com/core/common" +) + +type statsServer struct { + stats core.StatManager +} + +func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) { + c := s.stats.GetCounter(request.Name) + if c == nil { + return nil, newError(request.Name, " not found.") + } + var value int64 + if request.Reset_ { + value = c.Set(0) + } else { + value = c.Value() + } + return &GetStatsResponse{ + Stat: &Stat{ + Name: request.Name, + Value: value, + }, + }, nil +} + +type service struct { + v *core.Instance +} + +func (s *service) Register(server *grpc.Server) { + RegisterStatsServiceServer(server, &statsServer{ + stats: s.v.Stats(), + }) +} + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { + s := core.MustFromContext(ctx) + return &service{v: s}, nil + })) +} diff --git a/app/stats/command/command.pb.go b/app/stats/command/command.pb.go new file mode 100644 index 000000000..0d148746e --- /dev/null +++ b/app/stats/command/command.pb.go @@ -0,0 +1,198 @@ +package command + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + "context" + + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type GetStatsRequest struct { + // Name of the stat counter. + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + // Whether or not to reset the counter to fetching its value. + Reset_ bool `protobuf:"varint,2,opt,name=reset" json:"reset,omitempty"` +} + +func (m *GetStatsRequest) Reset() { *m = GetStatsRequest{} } +func (m *GetStatsRequest) String() string { return proto.CompactTextString(m) } +func (*GetStatsRequest) ProtoMessage() {} +func (*GetStatsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *GetStatsRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *GetStatsRequest) GetReset_() bool { + if m != nil { + return m.Reset_ + } + return false +} + +type Stat struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Value int64 `protobuf:"varint,2,opt,name=value" json:"value,omitempty"` +} + +func (m *Stat) Reset() { *m = Stat{} } +func (m *Stat) String() string { return proto.CompactTextString(m) } +func (*Stat) ProtoMessage() {} +func (*Stat) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *Stat) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Stat) GetValue() int64 { + if m != nil { + return m.Value + } + return 0 +} + +type GetStatsResponse struct { + Stat *Stat `protobuf:"bytes,1,opt,name=stat" json:"stat,omitempty"` +} + +func (m *GetStatsResponse) Reset() { *m = GetStatsResponse{} } +func (m *GetStatsResponse) String() string { return proto.CompactTextString(m) } +func (*GetStatsResponse) ProtoMessage() {} +func (*GetStatsResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *GetStatsResponse) GetStat() *Stat { + if m != nil { + return m.Stat + } + return nil +} + +type Config struct { +} + +func (m *Config) Reset() { *m = Config{} } +func (m *Config) String() string { return proto.CompactTextString(m) } +func (*Config) ProtoMessage() {} +func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func init() { + proto.RegisterType((*GetStatsRequest)(nil), "v2ray.core.app.stats.command.GetStatsRequest") + proto.RegisterType((*Stat)(nil), "v2ray.core.app.stats.command.Stat") + proto.RegisterType((*GetStatsResponse)(nil), "v2ray.core.app.stats.command.GetStatsResponse") + proto.RegisterType((*Config)(nil), "v2ray.core.app.stats.command.Config") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for StatsService service + +type StatsServiceClient interface { + GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) +} + +type statsServiceClient struct { + cc *grpc.ClientConn +} + +func NewStatsServiceClient(cc *grpc.ClientConn) StatsServiceClient { + return &statsServiceClient{cc} +} + +func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) { + out := new(GetStatsResponse) + err := grpc.Invoke(ctx, "/v2ray.core.app.stats.command.StatsService/GetStats", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for StatsService service + +type StatsServiceServer interface { + GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) +} + +func RegisterStatsServiceServer(s *grpc.Server, srv StatsServiceServer) { + s.RegisterService(&_StatsService_serviceDesc, srv) +} + +func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetStatsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StatsServiceServer).GetStats(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v2ray.core.app.stats.command.StatsService/GetStats", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StatsServiceServer).GetStats(ctx, req.(*GetStatsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _StatsService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v2ray.core.app.stats.command.StatsService", + HandlerType: (*StatsServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetStats", + Handler: _StatsService_GetStats_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "v2ray.com/core/app/stats/command/command.proto", +} + +func init() { proto.RegisterFile("v2ray.com/core/app/stats/command/command.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 267 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x3f, 0x4b, 0x03, 0x31, + 0x14, 0xc0, 0xbd, 0x5a, 0xeb, 0xf9, 0x14, 0x94, 0xe0, 0x50, 0xa4, 0xc3, 0x91, 0xa9, 0x8b, 0xef, + 0xe4, 0x04, 0x17, 0x27, 0xbd, 0x41, 0x10, 0x07, 0x49, 0xc1, 0xc1, 0x2d, 0xc6, 0xa7, 0x14, 0xcd, + 0x25, 0x26, 0xe9, 0x41, 0xf1, 0x1b, 0xf9, 0x29, 0x25, 0xb9, 0x1e, 0x82, 0xe0, 0xe1, 0x94, 0xf7, + 0x92, 0xdf, 0xef, 0xfd, 0x21, 0x80, 0x6d, 0xe5, 0xe4, 0x1a, 0x95, 0xd1, 0xa5, 0x32, 0x8e, 0x4a, + 0x69, 0x6d, 0xe9, 0x83, 0x0c, 0xbe, 0x54, 0x46, 0x6b, 0xd9, 0x3c, 0xf7, 0x27, 0x5a, 0x67, 0x82, + 0x61, 0xb3, 0x9e, 0x77, 0x84, 0xd2, 0x5a, 0x4c, 0x2c, 0x6e, 0x18, 0x7e, 0x09, 0x87, 0x37, 0x14, + 0x16, 0xf1, 0x4e, 0xd0, 0xc7, 0x8a, 0x7c, 0x60, 0x0c, 0xc6, 0x8d, 0xd4, 0x34, 0xcd, 0x8a, 0x6c, + 0xbe, 0x27, 0x52, 0xcc, 0x8e, 0x61, 0xc7, 0x91, 0xa7, 0x30, 0x1d, 0x15, 0xd9, 0x3c, 0x17, 0x5d, + 0xc2, 0xcf, 0x60, 0x1c, 0xcd, 0xbf, 0x8c, 0x56, 0xbe, 0xaf, 0x28, 0x19, 0xdb, 0xa2, 0x4b, 0xf8, + 0x2d, 0x1c, 0xfd, 0xb4, 0xf3, 0xd6, 0x34, 0x9e, 0xd8, 0x05, 0x8c, 0xe3, 0x4c, 0xc9, 0xde, 0xaf, + 0x38, 0x0e, 0xcd, 0x8b, 0x51, 0x15, 0x89, 0xe7, 0x39, 0x4c, 0x6a, 0xd3, 0xbc, 0x2c, 0x5f, 0xab, + 0x4f, 0x38, 0x48, 0x25, 0x17, 0xe4, 0xda, 0xa5, 0x22, 0xf6, 0x06, 0x79, 0xdf, 0x85, 0x9d, 0x0e, + 0xd7, 0xfb, 0xb5, 0xfc, 0x09, 0xfe, 0x17, 0xef, 0x86, 0xe7, 0x5b, 0xd7, 0x77, 0x50, 0x28, 0xa3, + 0x07, 0xb5, 0xfb, 0xec, 0x71, 0x77, 0x13, 0x7e, 0x8d, 0x66, 0x0f, 0x95, 0x90, 0x6b, 0xac, 0x23, + 0x79, 0x65, 0x6d, 0xda, 0xc8, 0x63, 0xdd, 0x3d, 0x3f, 0x4d, 0xd2, 0xa7, 0x9d, 0x7f, 0x07, 0x00, + 0x00, 0xff, 0xff, 0x10, 0x3a, 0x8a, 0xf3, 0xe6, 0x01, 0x00, 0x00, +} diff --git a/app/stats/command/command.proto b/app/stats/command/command.proto new file mode 100644 index 000000000..fbd57a42c --- /dev/null +++ b/app/stats/command/command.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package v2ray.core.app.stats.command; +option csharp_namespace = "V2Ray.Core.App.Stats.Command"; +option go_package = "command"; +option java_package = "com.v2ray.core.app.stats.command"; +option java_multiple_files = true; + +message GetStatsRequest { + // Name of the stat counter. + string name = 1; + // Whether or not to reset the counter to fetching its value. + bool reset = 2; +} + +message Stat { + string name = 1; + int64 value = 2; +} + +message GetStatsResponse { + Stat stat = 1; +} + +service StatsService { + rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {} +} + +message Config {} diff --git a/app/stats/command/errors.generated.go b/app/stats/command/errors.generated.go new file mode 100644 index 000000000..a5030ca0d --- /dev/null +++ b/app/stats/command/errors.generated.go @@ -0,0 +1,7 @@ +package command + +import "v2ray.com/core/common/errors" + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).Path("App", "Stats", "Command") +} diff --git a/app/stats/stats.go b/app/stats/stats.go index 622982387..27fffc1bd 100644 --- a/app/stats/stats.go +++ b/app/stats/stats.go @@ -32,9 +32,18 @@ type Manager struct { } func NewManager(ctx context.Context, config *Config) (*Manager, error) { - return &Manager{ + m := &Manager{ counters: make(map[string]*Counter), - }, nil + } + + v := core.FromContext(ctx) + if v != nil { + if err := v.RegisterFeature((*core.StatManager)(nil), m); err != nil { + return nil, newError("failed to register StatManager").Base(err) + } + } + + return m, nil } func (m *Manager) RegisterCounter(name string) (core.StatCounter, error) { @@ -44,6 +53,7 @@ func (m *Manager) RegisterCounter(name string) (core.StatCounter, error) { if _, found := m.counters[name]; found { return nil, newError("Counter ", name, " already registered.") } + newError("create new counter ", name).AtDebug().WriteToLog() c := new(Counter) m.counters[name] = c return c, nil diff --git a/main/distro/all/all.go b/main/distro/all/all.go index 880c94f57..989d7c2e0 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -8,16 +8,18 @@ import ( _ "v2ray.com/core/app/proxyman/inbound" _ "v2ray.com/core/app/proxyman/outbound" - // Default commander and all its services. + // Default commander and all its services. This is an optional feature. _ "v2ray.com/core/app/commander" _ "v2ray.com/core/app/log/command" _ "v2ray.com/core/app/proxyman/command" + _ "v2ray.com/core/app/stats/command" // Other optional features. _ "v2ray.com/core/app/dns" _ "v2ray.com/core/app/log" _ "v2ray.com/core/app/policy" _ "v2ray.com/core/app/router" + _ "v2ray.com/core/app/stats" // Inbound and outbound proxies. _ "v2ray.com/core/proxy/blackhole" diff --git a/stats.go b/stats.go index 0505c0b13..c44943350 100644 --- a/stats.go +++ b/stats.go @@ -27,7 +27,7 @@ func (s *syncStatManager) Start() error { defer s.RUnlock() if s.StatManager == nil { - return newError("StatManager not set.") + return nil } return s.StatManager.Start() @@ -38,7 +38,7 @@ func (s *syncStatManager) Close() error { defer s.RUnlock() if s.StatManager == nil { - return newError("StatManager not set.") + return nil } return s.StatManager.Close() } diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go index d3c0b10fb..98737a400 100644 --- a/testing/scenarios/command_test.go +++ b/testing/scenarios/command_test.go @@ -2,18 +2,22 @@ package scenarios import ( "context" + "crypto/rand" "fmt" "io" "testing" - - "v2ray.com/core/app/policy" + "time" "google.golang.org/grpc" + "v2ray.com/core" "v2ray.com/core/app/commander" + "v2ray.com/core/app/policy" "v2ray.com/core/app/proxyman" "v2ray.com/core/app/proxyman/command" "v2ray.com/core/app/router" + "v2ray.com/core/app/stats" + statscmd "v2ray.com/core/app/stats/command" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/serial" @@ -344,3 +348,177 @@ func TestCommanderAddRemoveUser(t *testing.T) { CloseAllServers(servers) } + +func TestCommanderStats(t *testing.T) { + assert := With(t) + + tcpServer := tcp.Server{ + MsgProcessor: xor, + } + dest, err := tcpServer.Start() + assert(err, IsNil) + defer tcpServer.Close() + + userID := protocol.NewID(uuid.New()) + serverPort := tcp.PickPort() + cmdPort := tcp.PickPort() + + serverConfig := &core.Config{ + App: []*serial.TypedMessage{ + serial.ToTypedMessage(&stats.Config{}), + serial.ToTypedMessage(&commander.Config{ + Tag: "api", + Service: []*serial.TypedMessage{ + serial.ToTypedMessage(&statscmd.Config{}), + }, + }), + serial.ToTypedMessage(&router.Config{ + Rule: []*router.RoutingRule{ + { + InboundTag: []string{"api"}, + Tag: "api", + }, + }, + }), + serial.ToTypedMessage(&policy.Config{ + Level: map[uint32]*policy.Policy{ + 0: { + Timeout: &policy.Policy_Timeout{ + UplinkOnly: &policy.Second{Value: 0}, + DownlinkOnly: &policy.Second{Value: 0}, + }, + }, + 1: { + Stats: &policy.Policy_Stats{ + EnablePerUser: true, + }, + }, + }, + }), + }, + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(serverPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&inbound.Config{ + User: []*protocol.User{ + { + Level: 1, + Email: "test", + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + }), + }, + }, + }), + }, + { + Tag: "api", + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(cmdPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&freedom.Config{}), + }, + }, + } + + clientPort := tcp.PickPort() + clientConfig := &core.Config{ + Inbound: []*core.InboundHandlerConfig{ + { + ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ + PortRange: net.SinglePortRange(clientPort), + Listen: net.NewIPOrDomain(net.LocalHostIP), + }), + ProxySettings: serial.ToTypedMessage(&dokodemo.Config{ + Address: net.NewIPOrDomain(dest.Address), + Port: uint32(dest.Port), + NetworkList: &net.NetworkList{ + Network: []net.Network{net.Network_TCP}, + }, + }), + }, + }, + Outbound: []*core.OutboundHandlerConfig{ + { + ProxySettings: serial.ToTypedMessage(&outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: net.NewIPOrDomain(net.LocalHostIP), + Port: uint32(serverPort), + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vmess.Account{ + Id: userID.String(), + AlterId: 64, + SecuritySettings: &protocol.SecurityConfig{ + Type: protocol.SecurityType_AES128_GCM, + }, + }), + }, + }, + }, + }, + }), + }, + }, + } + + servers, err := InitializeServerConfigs(serverConfig, clientConfig) + assert(err, IsNil) + + conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{ + IP: []byte{127, 0, 0, 1}, + Port: int(clientPort), + }) + assert(err, IsNil) + + payload := make([]byte, 10240*1024) + rand.Read(payload) + + nBytes, err := conn.Write([]byte(payload)) + assert(err, IsNil) + assert(nBytes, Equals, len(payload)) + + response := readFrom(conn, time.Second*20, 10240*1024) + assert(response, Equals, xor([]byte(payload))) + assert(conn.Close(), IsNil) + + cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure(), grpc.WithBlock()) + assert(err, IsNil) + + const name = "user>traffic>test" + sClient := statscmd.NewStatsServiceClient(cmdConn) + + sresp, err := sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{ + Name: name, + Reset_: true, + }) + assert(err, IsNil) + assert(sresp.Stat.Name, Equals, name) + assert(sresp.Stat.Value, Equals, int64(10240*1024*2)) + + sresp, err = sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{ + Name: name, + }) + assert(err, IsNil) + assert(sresp.Stat.Name, Equals, name) + assert(sresp.Stat.Value, Equals, int64(0)) + + CloseAllServers(servers) +} From 6d98bc460714ca2f54c1530d9636684d0f5c1adf Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sat, 31 Mar 2018 20:30:49 +0200 Subject: [PATCH 153/183] split uplink and downlink traffic --- app/dispatcher/default.go | 52 +++++++++++++++-------- app/policy/config.go | 3 +- app/policy/config.pb.go | 68 +++++++++++++++++-------------- app/policy/config.proto | 3 +- policy.go | 6 ++- testing/scenarios/command_test.go | 7 ++-- transport/ray/direct.go | 31 +++++++++----- 7 files changed, 105 insertions(+), 65 deletions(-) diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 39a2335b9..865a643b4 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -56,6 +56,40 @@ func getStatsName(u *protocol.User) string { return "user>traffic>" + u.Email } +func (d *DefaultDispatcher) getStatCounter(name string) core.StatCounter { + c := d.stats.GetCounter(name) + if c != nil { + return c + } + c, err := d.stats.RegisterCounter(name) + if err != nil { + return nil + } + return c +} + +func (d *DefaultDispatcher) getRayOption(user *protocol.User) []ray.Option { + var rayOptions []ray.Option + + if user != nil && len(user.Email) > 0 { + p := d.policy.ForLevel(user.Level) + if p.Stats.UserUplink { + name := "user>>>" + user.Email + ">>>traffic>>>uplink" + if c := d.getStatCounter(name); c != nil { + rayOptions = append(rayOptions, ray.WithUplinkStatCounter(c)) + } + } + if p.Stats.UserDownlink { + name := "user>>>" + user.Email + ">>>traffic>>>downlink" + if c := d.getStatCounter(name); c != nil { + rayOptions = append(rayOptions, ray.WithDownlinkStatCounter(c)) + } + } + } + + return rayOptions +} + // Dispatch implements core.Dispatcher. func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destination) (ray.InboundRay, error) { if !destination.IsValid() { @@ -63,24 +97,8 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin } ctx = proxy.ContextWithTarget(ctx, destination) - var rayOptions []ray.Option - user := protocol.UserFromContext(ctx) - if user != nil && len(user.Email) > 0 { - name := getStatsName(user) - c, err := d.stats.RegisterCounter(name) - if err != nil { - c = d.stats.GetCounter(name) - } - if c == nil { - newError("failed to get stats counter ", name).AtWarning().WithContext(ctx).WriteToLog() - } - - p := d.policy.ForLevel(user.Level) - if p.Stats.EnablePerUser { - rayOptions = append(rayOptions, ray.WithStatCounter(c)) - } - } + rayOptions := d.getRayOption(user) outbound := ray.New(ctx, rayOptions...) snifferList := proxyman.ProtocolSniffersFromContext(ctx) diff --git a/app/policy/config.go b/app/policy/config.go index 3504d16f9..49cb55303 100644 --- a/app/policy/config.go +++ b/app/policy/config.go @@ -61,7 +61,8 @@ func (p *Policy) ToCorePolicy() core.Policy { cp.Timeouts.UplinkOnly = p.Timeout.UplinkOnly.Duration() } if p.Stats != nil { - cp.Stats.EnablePerUser = p.Stats.EnablePerUser + cp.Stats.UserUplink = p.Stats.UserUplink + cp.Stats.UserDownlink = p.Stats.UserDownlink } return cp } diff --git a/app/policy/config.pb.go b/app/policy/config.pb.go index c02dc16a7..10e77c0e8 100644 --- a/app/policy/config.pb.go +++ b/app/policy/config.pb.go @@ -97,7 +97,8 @@ func (m *Policy_Timeout) GetDownlinkOnly() *Second { } type Policy_Stats struct { - EnablePerUser bool `protobuf:"varint,1,opt,name=enable_per_user,json=enablePerUser" json:"enable_per_user,omitempty"` + UserUplink bool `protobuf:"varint,1,opt,name=user_uplink,json=userUplink" json:"user_uplink,omitempty"` + UserDownlink bool `protobuf:"varint,2,opt,name=user_downlink,json=userDownlink" json:"user_downlink,omitempty"` } func (m *Policy_Stats) Reset() { *m = Policy_Stats{} } @@ -105,9 +106,16 @@ func (m *Policy_Stats) String() string { return proto.CompactTextStri func (*Policy_Stats) ProtoMessage() {} func (*Policy_Stats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 1} } -func (m *Policy_Stats) GetEnablePerUser() bool { +func (m *Policy_Stats) GetUserUplink() bool { if m != nil { - return m.EnablePerUser + return m.UserUplink + } + return false +} + +func (m *Policy_Stats) GetUserDownlink() bool { + if m != nil { + return m.UserDownlink } return false } @@ -139,31 +147,31 @@ func init() { func init() { proto.RegisterFile("v2ray.com/core/app/policy/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 403 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xdd, 0x8a, 0xd3, 0x40, - 0x14, 0xc7, 0x49, 0x62, 0xb2, 0x7a, 0x6a, 0x5d, 0x19, 0x5c, 0x88, 0x05, 0x65, 0xa9, 0xb8, 0xf4, - 0x6a, 0x02, 0xd9, 0x1b, 0x3f, 0x70, 0xc5, 0x15, 0x05, 0x41, 0xb0, 0xa4, 0x7e, 0x80, 0x37, 0x61, - 0x3a, 0x39, 0xda, 0xd0, 0xe9, 0xcc, 0x30, 0x49, 0x2a, 0x79, 0x0d, 0xdf, 0xc0, 0x5b, 0x1f, 0xc5, - 0xa7, 0x92, 0x64, 0x12, 0x72, 0xd3, 0x76, 0x7b, 0x37, 0x19, 0x7e, 0xff, 0x5f, 0x4e, 0x4e, 0xfe, - 0x70, 0xb1, 0x8d, 0x0d, 0xab, 0x29, 0x57, 0x9b, 0x88, 0x2b, 0x83, 0x11, 0xd3, 0x3a, 0xd2, 0x4a, - 0xe4, 0xbc, 0x8e, 0xb8, 0x92, 0x3f, 0xf2, 0x9f, 0x54, 0x1b, 0x55, 0x2a, 0x72, 0xd6, 0x73, 0x06, - 0x29, 0xd3, 0x9a, 0x5a, 0x66, 0xfa, 0x18, 0x82, 0x05, 0x72, 0x25, 0x33, 0xf2, 0x00, 0xfc, 0x2d, - 0x13, 0x15, 0x86, 0xce, 0xb9, 0x33, 0x1b, 0x27, 0xf6, 0x61, 0xfa, 0xcf, 0x83, 0x60, 0xde, 0xa2, - 0xe4, 0x35, 0x9c, 0x94, 0xf9, 0x06, 0x55, 0x55, 0xb6, 0xc8, 0x28, 0x7e, 0x4a, 0x77, 0x3a, 0xa9, - 0xe5, 0xe9, 0x67, 0x0b, 0x27, 0x7d, 0x8a, 0x3c, 0x07, 0xbf, 0x28, 0x59, 0x59, 0x84, 0x6e, 0x1b, - 0x7f, 0x72, 0x38, 0xbe, 0x68, 0xd0, 0xc4, 0x26, 0x26, 0xbf, 0x5d, 0x38, 0xe9, 0x7c, 0xe4, 0x25, - 0xdc, 0x59, 0x31, 0x99, 0x15, 0x2b, 0xb6, 0xc6, 0x6e, 0x92, 0x47, 0x7b, 0x54, 0xf6, 0xd3, 0x92, - 0x81, 0x27, 0xef, 0xe1, 0x94, 0x2b, 0x29, 0x91, 0x97, 0xb9, 0x92, 0x69, 0x9e, 0x09, 0xec, 0xa6, - 0xb9, 0x41, 0x71, 0x6f, 0x48, 0x7d, 0xc8, 0x04, 0x92, 0x2b, 0x18, 0x55, 0x5a, 0xe4, 0x72, 0x9d, - 0x2a, 0x29, 0xea, 0xd0, 0x3b, 0xc6, 0x01, 0x36, 0xf1, 0x49, 0x8a, 0x9a, 0x5c, 0xc3, 0x38, 0x53, - 0xbf, 0xe4, 0x60, 0xb8, 0x75, 0x8c, 0xe1, 0x6e, 0x9f, 0x69, 0x1c, 0x93, 0x08, 0xfc, 0x76, 0x49, - 0xe4, 0x02, 0x4e, 0x51, 0xb2, 0xa5, 0xc0, 0x54, 0xa3, 0x49, 0xab, 0x02, 0x4d, 0xbb, 0x97, 0xdb, - 0xc9, 0xd8, 0x5e, 0xcf, 0xd1, 0x7c, 0x29, 0xd0, 0x4c, 0xff, 0x38, 0x10, 0xbc, 0x6d, 0x4b, 0x41, - 0xae, 0xc0, 0x17, 0xb8, 0x45, 0x11, 0x3a, 0xe7, 0xde, 0x6c, 0x14, 0xcf, 0xf6, 0xbc, 0xd7, 0xd2, - 0xf4, 0x63, 0x83, 0xbe, 0x93, 0xa5, 0xa9, 0x13, 0x1b, 0x9b, 0x7c, 0x03, 0x18, 0x2e, 0xc9, 0x7d, - 0xf0, 0xd6, 0x58, 0x77, 0xcd, 0x69, 0x8e, 0xe4, 0xb2, 0x6f, 0xd3, 0xe1, 0xed, 0xda, 0x7f, 0xdd, - 0x95, 0xed, 0x85, 0xfb, 0xcc, 0xb9, 0x7e, 0x05, 0x0f, 0xb9, 0xda, 0xec, 0xc6, 0xe7, 0xce, 0xf7, - 0xc0, 0x9e, 0xfe, 0xba, 0x67, 0x5f, 0xe3, 0x84, 0x35, 0x03, 0x1a, 0xa4, 0x6f, 0xb4, 0xee, 0x4c, - 0xcb, 0xa0, 0x6d, 0xfb, 0xe5, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0xad, 0x5f, 0x54, 0x17, - 0x03, 0x00, 0x00, + // 410 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x5d, 0xab, 0xd3, 0x30, + 0x18, 0xc7, 0x69, 0x6b, 0x7b, 0x8e, 0x4f, 0xcf, 0x54, 0x82, 0x07, 0xea, 0x40, 0x3d, 0x6c, 0x28, + 0xbb, 0x4a, 0xa1, 0xbb, 0xf1, 0x05, 0x27, 0xce, 0x17, 0x10, 0x14, 0x47, 0xe6, 0x0b, 0x78, 0x33, + 0x62, 0x1a, 0x5d, 0x59, 0x96, 0x84, 0xbe, 0x4c, 0xfa, 0x35, 0xfc, 0x06, 0xde, 0xfa, 0xc9, 0xfc, + 0x18, 0xd2, 0xa4, 0xa5, 0x37, 0xdb, 0xdc, 0x5d, 0xfa, 0xf0, 0xfb, 0xff, 0x78, 0x12, 0xfe, 0x85, + 0x87, 0xbb, 0x24, 0xa7, 0x35, 0x66, 0x6a, 0x1b, 0x33, 0x95, 0xf3, 0x98, 0x6a, 0x1d, 0x6b, 0x25, + 0x32, 0x56, 0xc7, 0x4c, 0xc9, 0xef, 0xd9, 0x0f, 0xac, 0x73, 0x55, 0x2a, 0x74, 0xd9, 0x71, 0x39, + 0xc7, 0x54, 0x6b, 0x6c, 0x99, 0xd1, 0x3d, 0x08, 0x96, 0x9c, 0x29, 0x99, 0xa2, 0xdb, 0xe0, 0xef, + 0xa8, 0xa8, 0x78, 0xe4, 0x5c, 0x39, 0x93, 0x01, 0xb1, 0x1f, 0xa3, 0xbf, 0x1e, 0x04, 0x0b, 0x83, + 0xa2, 0xe7, 0x70, 0x56, 0x66, 0x5b, 0xae, 0xaa, 0xd2, 0x20, 0x61, 0xf2, 0x00, 0xef, 0x75, 0x62, + 0xcb, 0xe3, 0x8f, 0x16, 0x26, 0x5d, 0x0a, 0x3d, 0x06, 0xbf, 0x28, 0x69, 0x59, 0x44, 0xae, 0x89, + 0x8f, 0x8f, 0xc7, 0x97, 0x0d, 0x4a, 0x6c, 0x62, 0xf8, 0xcb, 0x85, 0xb3, 0xd6, 0x87, 0x9e, 0xc2, + 0xf5, 0x35, 0x95, 0x69, 0xb1, 0xa6, 0x1b, 0xde, 0x6e, 0x72, 0xf7, 0x80, 0xca, 0x5e, 0x8d, 0xf4, + 0x3c, 0x7a, 0x03, 0x37, 0x99, 0x92, 0x92, 0xb3, 0x32, 0x53, 0x72, 0x95, 0xa5, 0x82, 0xb7, 0xdb, + 0xfc, 0x47, 0x71, 0xa3, 0x4f, 0xbd, 0x4d, 0x05, 0x47, 0x33, 0x08, 0x2b, 0x2d, 0x32, 0xb9, 0x59, + 0x29, 0x29, 0xea, 0xc8, 0x3b, 0xc5, 0x01, 0x36, 0xf1, 0x41, 0x8a, 0x1a, 0xcd, 0x61, 0x90, 0xaa, + 0x9f, 0xb2, 0x37, 0x5c, 0x3b, 0xc5, 0x70, 0xd1, 0x65, 0x1a, 0xc7, 0xf0, 0x3d, 0xf8, 0xe6, 0x91, + 0xd0, 0x7d, 0x08, 0xab, 0x82, 0xe7, 0x2b, 0xeb, 0x37, 0x6f, 0x72, 0x4e, 0xa0, 0x19, 0x7d, 0x32, + 0x13, 0x34, 0x86, 0x81, 0x01, 0xba, 0xb8, 0xb9, 0xf3, 0x39, 0xb9, 0x68, 0x86, 0xaf, 0xda, 0xd9, + 0xe8, 0xb7, 0x03, 0xc1, 0x4b, 0x53, 0x19, 0x34, 0x03, 0x5f, 0xf0, 0x1d, 0x17, 0x91, 0x73, 0xe5, + 0x4d, 0xc2, 0x64, 0x72, 0x60, 0x2b, 0x4b, 0xe3, 0x77, 0x0d, 0xfa, 0x5a, 0x96, 0x79, 0x4d, 0x6c, + 0x6c, 0xf8, 0x05, 0xa0, 0x1f, 0xa2, 0x5b, 0xe0, 0x6d, 0x78, 0xdd, 0xf6, 0xaa, 0x39, 0xa2, 0x69, + 0xd7, 0xb5, 0xe3, 0x6f, 0x6f, 0x9b, 0xd0, 0x56, 0xf1, 0x89, 0xfb, 0xc8, 0x99, 0x3f, 0x83, 0x3b, + 0x4c, 0x6d, 0xf7, 0xe3, 0x0b, 0xe7, 0x6b, 0x60, 0x4f, 0x7f, 0xdc, 0xcb, 0xcf, 0x09, 0xa1, 0xcd, + 0x82, 0x39, 0xc7, 0x2f, 0xb4, 0x6e, 0x4d, 0xdf, 0x02, 0xf3, 0x2f, 0x4c, 0xff, 0x05, 0x00, 0x00, + 0xff, 0xff, 0x98, 0xa8, 0x2f, 0xbe, 0x35, 0x03, 0x00, 0x00, } diff --git a/app/policy/config.proto b/app/policy/config.proto index 2ca1ec1da..e82bff3e3 100644 --- a/app/policy/config.proto +++ b/app/policy/config.proto @@ -20,7 +20,8 @@ message Policy { } message Stats { - bool enable_per_user = 1; + bool user_uplink = 1; + bool user_downlink = 2; } Timeout timeout = 1; diff --git a/policy.go b/policy.go index ec04eaace..d9e0682b9 100644 --- a/policy.go +++ b/policy.go @@ -20,7 +20,8 @@ type TimeoutPolicy struct { } type StatsPolicy struct { - EnablePerUser bool + UserUplink bool + UserDownlink bool } // Policy is session based settings for controlling V2Ray requests. It contains various settings (or limits) that may differ for different users in the context. @@ -47,7 +48,8 @@ func DefaultPolicy() Policy { DownlinkOnly: time.Second * 30, }, Stats: StatsPolicy{ - EnablePerUser: false, + UserUplink: false, + UserDownlink: false, }, } } diff --git a/testing/scenarios/command_test.go b/testing/scenarios/command_test.go index 98737a400..67ef8d07e 100644 --- a/testing/scenarios/command_test.go +++ b/testing/scenarios/command_test.go @@ -390,7 +390,8 @@ func TestCommanderStats(t *testing.T) { }, 1: { Stats: &policy.Policy_Stats{ - EnablePerUser: true, + UserUplink: true, + UserDownlink: true, }, }, }, @@ -502,7 +503,7 @@ func TestCommanderStats(t *testing.T) { cmdConn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%d", cmdPort), grpc.WithInsecure(), grpc.WithBlock()) assert(err, IsNil) - const name = "user>traffic>test" + const name = "user>>>test>>>traffic>>>uplink" sClient := statscmd.NewStatsServiceClient(cmdConn) sresp, err := sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{ @@ -511,7 +512,7 @@ func TestCommanderStats(t *testing.T) { }) assert(err, IsNil) assert(sresp.Stat.Name, Equals, name) - assert(sresp.Stat.Value, Equals, int64(10240*1024*2)) + assert(sresp.Stat.Value, Equals, int64(10240*1024)) sresp, err = sClient.GetStats(context.Background(), &statscmd.GetStatsRequest{ Name: name, diff --git a/transport/ray/direct.go b/transport/ray/direct.go index ab48021a2..c84b0b227 100644 --- a/transport/ray/direct.go +++ b/transport/ray/direct.go @@ -12,15 +12,23 @@ import ( "v2ray.com/core/common/signal" ) -type Option func(*Stream) +type Option func(*directRay) type addInt64 interface { Add(int64) int64 } -func WithStatCounter(c addInt64) Option { - return func(s *Stream) { - s.onDataSize = append(s.onDataSize, func(delta uint64) { +func WithUplinkStatCounter(c addInt64) Option { + return func(s *directRay) { + s.Input.onDataSize = append(s.Input.onDataSize, func(delta uint64) { + c.Add(int64(delta)) + }) + } +} + +func WithDownlinkStatCounter(c addInt64) Option { + return func(s *directRay) { + s.Output.onDataSize = append(s.Output.onDataSize, func(delta uint64) { c.Add(int64(delta)) }) } @@ -28,10 +36,14 @@ func WithStatCounter(c addInt64) Option { // New creates a new Ray for direct traffic transport. func New(ctx context.Context, opts ...Option) Ray { - return &directRay{ - Input: NewStream(ctx, opts...), - Output: NewStream(ctx, opts...), + r := &directRay{ + Input: NewStream(ctx), + Output: NewStream(ctx), } + for _, opt := range opts { + opt(r) + } + return r } type directRay struct { @@ -80,16 +92,13 @@ type Stream struct { } // NewStream creates a new Stream. -func NewStream(ctx context.Context, opts ...Option) *Stream { +func NewStream(ctx context.Context) *Stream { s := &Stream{ ctx: ctx, readSignal: signal.NewNotifier(), writeSignal: signal.NewNotifier(), size: 0, } - for _, opt := range opts { - opt(s) - } return s } From 50c4e3389cfe50f56a1d2314cdee47576e4c4cdc Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 1 Apr 2018 09:51:44 +0200 Subject: [PATCH 154/183] remove unnecessary test --- transport/ray/direct_test.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/transport/ray/direct_test.go b/transport/ray/direct_test.go index c365ac621..64c0ab1d0 100644 --- a/transport/ray/direct_test.go +++ b/transport/ray/direct_test.go @@ -5,7 +5,6 @@ import ( "io" "testing" - "v2ray.com/core/app/stats" "v2ray.com/core/common/buf" . "v2ray.com/core/transport/ray" . "v2ray.com/ext/assert" @@ -48,18 +47,3 @@ func TestStreamClose(t *testing.T) { _, err = stream.ReadMultiBuffer() assert(err, Equals, io.EOF) } - -func TestStreamStatCounter(t *testing.T) { - assert := With(t) - - c := new(stats.Counter) - stream := NewStream(context.Background(), WithStatCounter(c)) - - b1 := buf.New() - b1.AppendBytes('a', 'b', 'c', 'd') - assert(stream.WriteMultiBuffer(buf.NewMultiBufferValue(b1)), IsNil) - - stream.Close() - - assert(c.Value(), Equals, int64(4)) -} From 90f24800374e6526b52e619c06bae21fd32a033a Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 1 Apr 2018 09:57:26 +0200 Subject: [PATCH 155/183] trace context --- proxy/shadowsocks/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index 317a6588d..9d103cc78 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -63,7 +63,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale if err != nil { return newError("failed to find an available destination").AtWarning().Base(err) } - newError("tunneling request to ", destination, " via ", server.Destination()).WriteToLog() + newError("tunneling request to ", destination, " via ", server.Destination()).WithContext(ctx).WriteToLog() defer conn.Close() From 053d03349a5e9510c36ea5d020ec645ed46d1383 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 1 Apr 2018 09:58:45 +0200 Subject: [PATCH 156/183] refactor --- proxy/shadowsocks/client.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index 9d103cc78..8cb20d6d8 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -38,7 +38,7 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { } // Process implements OutboundHandler.Process(). -func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, dialer proxy.Dialer) error { +func (c *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, dialer proxy.Dialer) error { destination, ok := proxy.TargetFromContext(ctx) if !ok { return newError("target not specified") @@ -49,7 +49,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale var conn internet.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { - server = v.serverPicker.PickServer() + server = c.serverPicker.PickServer() dest := server.Destination() dest.Network = network rawConn, err := dialer.Dial(ctx, dest) @@ -90,7 +90,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale request.Option |= RequestOptionOneTimeAuth } - sessionPolicy := v.v.PolicyManager().ForLevel(user.Level) + sessionPolicy := c.v.PolicyManager().ForLevel(user.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) From b3ecb9f7667519f1ae8424f836cdd90f5c32d3b8 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 1 Apr 2018 12:20:32 +0200 Subject: [PATCH 157/183] align large size --- common/buf/buffer_pool.go | 10 ++++++++-- common/buf/reader.go | 4 +--- common/buf/reader_test.go | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/common/buf/buffer_pool.go b/common/buf/buffer_pool.go index fac6aa304..59a4fb891 100644 --- a/common/buf/buffer_pool.go +++ b/common/buf/buffer_pool.go @@ -15,14 +15,19 @@ func createAllocFunc(size uint32) func() interface{} { } } +// The following parameters controls the size of buffer pools. +// There are numPools pools. Starting from 2k size, the size of each pool is sizeMulti of the previous one. +// Package buf is guaranteed to not use buffers larger than the largest pool. +// Other packets may use larger buffers. const ( numPools = 5 sizeMulti = 4 ) var ( - pool [numPools]sync.Pool - poolSize [numPools]uint32 + pool [numPools]sync.Pool + poolSize [numPools]uint32 + largeSize uint32 ) func init() { @@ -32,6 +37,7 @@ func init() { New: createAllocFunc(size), } poolSize[i] = size + largeSize = size size *= sizeMulti } } diff --git a/common/buf/reader.go b/common/buf/reader.go index 8ad3bba17..a9e20c903 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -19,8 +19,6 @@ func NewBytesToBufferReader(reader io.Reader) Reader { } } -const xlSize = 128 * 1024 - func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) { b := New() for i := 0; i < 64; i++ { @@ -55,7 +53,7 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) { if nBytes > 0 { mb := NewMultiBufferCap(nBytes/Size + 1) mb.Write(r.buffer[:nBytes]) - if nBytes == len(r.buffer) && nBytes < xlSize { + if nBytes == len(r.buffer) && nBytes < int(largeSize) { freeBytes(r.buffer) r.buffer = newBytes(uint32(nBytes) + 1) } else if nBytes < Size { diff --git a/common/buf/reader_test.go b/common/buf/reader_test.go index f5964ff92..36cd003bb 100644 --- a/common/buf/reader_test.go +++ b/common/buf/reader_test.go @@ -33,7 +33,7 @@ func TestAdaptiveReader(t *testing.T) { b, err = reader.ReadMultiBuffer() assert(err, IsNil) - assert(b.Len(), Equals, 128*1024) + assert(b.Len(), Equals, 512*1024) } func TestBytesReaderWriteTo(t *testing.T) { From 8d0a74b3fabbbc4ad95ea9cfa661f3ca46796b03 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 1 Apr 2018 22:41:38 +0200 Subject: [PATCH 158/183] refine auth writer --- common/crypto/auth.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 416645315..9895bed14 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -204,12 +204,20 @@ func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error { func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error { defer mb.Release() + if mb.IsEmpty() { + b := buf.New() + defer b.Release() + + eb, _ := w.seal(b) + return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(eb)) + } + mb2Write := buf.NewMultiBufferCap(len(mb) + 1) - for { + for !mb.IsEmpty() { b := mb.SplitFirst() if b == nil { - b = buf.New() + continue } eb, err := w.seal(b) b.Release() @@ -218,9 +226,6 @@ func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error { return err } mb2Write.Append(eb) - if mb.IsEmpty() { - break - } } return w.writer.WriteMultiBuffer(mb2Write) From 3d919a6a93625dc9b2d4acea31d8bb97a7eeb775 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 1 Apr 2018 23:31:53 +0200 Subject: [PATCH 159/183] optimize auth reader --- common/buf/reader.go | 5 ++++ common/crypto/auth.go | 58 +++++++++++++++++++++++++++++++++----- common/crypto/auth_test.go | 4 +-- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/common/buf/reader.go b/common/buf/reader.go index a9e20c903..0dafeec7f 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -97,6 +97,11 @@ func (r *BufferedReader) IsBuffered() bool { return r.buffered } +// BufferedBytes returns the number of bytes that is cached in this reader. +func (r *BufferedReader) BufferedBytes() int32 { + return int32(r.leftOver.Len()) +} + // ReadByte implements io.ByteReader. func (r *BufferedReader) ReadByte() (byte, error) { var b [1]byte diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 9895bed14..20258f59c 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -93,6 +93,7 @@ type AuthenticationReader struct { reader *buf.BufferedReader sizeParser ChunkSizeDecoder transferType protocol.TransferType + size int32 } func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, reader io.Reader, transferType protocol.TransferType) *AuthenticationReader { @@ -101,42 +102,85 @@ func NewAuthenticationReader(auth Authenticator, sizeParser ChunkSizeDecoder, re reader: buf.NewBufferedReader(buf.NewReader(reader)), sizeParser: sizeParser, transferType: transferType, + size: -1, } } -func (r *AuthenticationReader) readSize() (int, error) { +func (r *AuthenticationReader) readSize() (int32, error) { + if r.size != -1 { + s := r.size + r.size = -1 + return s, nil + } sizeBytes := make([]byte, r.sizeParser.SizeBytes()) _, err := io.ReadFull(r.reader, sizeBytes) if err != nil { return 0, err } size, err := r.sizeParser.Decode(sizeBytes) - return int(size), err + return int32(size), err } -func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) { +var errSoft = newError("waiting for more data") + +func (r *AuthenticationReader) readInternal(soft bool) (*buf.Buffer, error) { + if soft && r.reader.BufferedBytes() < 2 { + return nil, errSoft + } + size, err := r.readSize() if err != nil { return nil, err } - if size == r.auth.Overhead() { + if size == -2 || size == int32(r.auth.Overhead()) { + r.size = -2 return nil, io.EOF } + if soft && size > r.reader.BufferedBytes() { + r.size = size + return nil, errSoft + } + b := buf.NewSize(uint32(size)) - if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil { + if err := b.Reset(buf.ReadFullFrom(r.reader, int(size))); err != nil { b.Release() return nil, err } - rb, err := r.auth.Open(b.BytesTo(0), b.BytesTo(size)) + rb, err := r.auth.Open(b.BytesTo(0), b.BytesTo(int(size))) if err != nil { b.Release() return nil, err } b.Slice(0, len(rb)) - return buf.NewMultiBufferValue(b), nil + + return b, nil +} + +func (r *AuthenticationReader) ReadMultiBuffer() (buf.MultiBuffer, error) { + b, err := r.readInternal(false) + if err != nil { + return nil, err + } + + mb := buf.NewMultiBufferCap(32) + mb.Append(b) + + for { + b, err := r.readInternal(true) + if err == errSoft || err == io.EOF { + break + } + if err != nil { + mb.Release() + return nil, err + } + mb.Append(b) + } + + return mb, nil } type AuthenticationWriter struct { diff --git a/common/crypto/auth_test.go b/common/crypto/auth_test.go index 0986a5839..b3cbe4f6e 100644 --- a/common/crypto/auth_test.go +++ b/common/crypto/auth_test.go @@ -125,12 +125,10 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) { b1 := mb.SplitFirst() assert(b1.String(), Equals, "abcd") - assert(mb.IsEmpty(), IsTrue) - mb, err = reader.ReadMultiBuffer() - assert(err, IsNil) b2 := mb.SplitFirst() assert(b2.String(), Equals, "efgh") + assert(mb.IsEmpty(), IsTrue) _, err = reader.ReadMultiBuffer() From 5af3580bcf673c42027d5008e72c257b62aaefb5 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 2 Apr 2018 00:00:15 +0200 Subject: [PATCH 160/183] correct magic number --- common/crypto/auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 20258f59c..e7b3e5597 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -124,7 +124,7 @@ func (r *AuthenticationReader) readSize() (int32, error) { var errSoft = newError("waiting for more data") func (r *AuthenticationReader) readInternal(soft bool) (*buf.Buffer, error) { - if soft && r.reader.BufferedBytes() < 2 { + if soft && r.reader.BufferedBytes() < int32(r.sizeParser.SizeBytes()) { return nil, errSoft } From 03512a6a9b5444687fdd3007c31b39e3bbe56fb1 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 2 Apr 2018 00:25:42 +0200 Subject: [PATCH 161/183] read size to multi buffer --- common/buf/multi_buffer.go | 20 ++++++++++++++++++++ proxy/http/server.go | 10 +++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/common/buf/multi_buffer.go b/common/buf/multi_buffer.go index a245123c8..48cc185ff 100644 --- a/common/buf/multi_buffer.go +++ b/common/buf/multi_buffer.go @@ -30,6 +30,26 @@ func ReadAllToMultiBuffer(reader io.Reader) (MultiBuffer, error) { } } +// ReadSizeToMultiBuffer reads specific number of bytes from reader into a MultiBuffer. +func ReadSizeToMultiBuffer(reader io.Reader, size int32) (MultiBuffer, error) { + mb := NewMultiBufferCap(32) + + for size > 0 { + bSize := size + if bSize > Size { + bSize = Size + } + b := NewSize(uint32(bSize)) + if err := b.Reset(ReadFullFrom(reader, int(bSize))); err != nil { + mb.Release() + return nil, err + } + size -= bSize + mb.Append(b) + } + return mb, nil +} + // ReadAllToBytes reads all content from the reader into a byte array, until EOF. func ReadAllToBytes(reader io.Reader) ([]byte, error) { mb, err := ReadAllToMultiBuffer(reader) diff --git a/proxy/http/server.go b/proxy/http/server.go index 2e939803b..ec30bd115 100644 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -173,11 +173,11 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade } if reader.Buffered() > 0 { - payload := buf.NewSize(uint32(reader.Buffered())) - common.Must(payload.Reset(func(b []byte) (int, error) { - return reader.Read(b[:reader.Buffered()]) - })) - if err := ray.InboundInput().WriteMultiBuffer(buf.NewMultiBufferValue(payload)); err != nil { + payload, err := buf.ReadSizeToMultiBuffer(reader, int32(reader.Buffered())) + if err != nil { + return err + } + if err := ray.InboundInput().WriteMultiBuffer(payload); err != nil { return err } reader = nil From 4de3f1adc18d9621b89961182eed9ccc8a3407b9 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 2 Apr 2018 00:44:47 +0200 Subject: [PATCH 162/183] change ReadFullFrom to take int32 size --- app/proxyman/mux/reader.go | 4 ++-- common/buf/io.go | 2 +- common/buf/multi_buffer.go | 6 +++--- common/buf/reader.go | 2 +- common/crypto/auth.go | 2 +- common/protocol/address.go | 2 +- proxy/shadowsocks/ota.go | 2 +- proxy/shadowsocks/protocol.go | 6 +++--- proxy/socks/protocol.go | 6 +++--- proxy/vmess/encoding/client.go | 4 ++-- proxy/vmess/encoding/server.go | 4 ++-- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/proxyman/mux/reader.go b/app/proxyman/mux/reader.go index d25132571..3443dd802 100644 --- a/app/proxyman/mux/reader.go +++ b/app/proxyman/mux/reader.go @@ -20,7 +20,7 @@ func ReadMetadata(reader io.Reader) (*FrameMetadata, error) { b := buf.NewSize(uint32(metaLen)) defer b.Release() - if err := b.Reset(buf.ReadFullFrom(reader, int(metaLen))); err != nil { + if err := b.Reset(buf.ReadFullFrom(reader, int32(metaLen))); err != nil { return nil, err } return ReadFrameFrom(b) @@ -52,7 +52,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { } b := buf.NewSize(uint32(size)) - if err := b.AppendSupplier(buf.ReadFullFrom(r.reader, int(size))); err != nil { + if err := b.Reset(buf.ReadFullFrom(r.reader, int32(size))); err != nil { b.Release() return nil, err } diff --git a/common/buf/io.go b/common/buf/io.go index e3c8aa338..c3b9538ba 100644 --- a/common/buf/io.go +++ b/common/buf/io.go @@ -33,7 +33,7 @@ func ReadFrom(reader io.Reader) Supplier { } // ReadFullFrom creates a Supplier to read full buffer from a given io.Reader. -func ReadFullFrom(reader io.Reader, size int) Supplier { +func ReadFullFrom(reader io.Reader, size int32) Supplier { return func(b []byte) (int, error) { return io.ReadFull(reader, b[:size]) } diff --git a/common/buf/multi_buffer.go b/common/buf/multi_buffer.go index 48cc185ff..ade8630c8 100644 --- a/common/buf/multi_buffer.go +++ b/common/buf/multi_buffer.go @@ -40,7 +40,7 @@ func ReadSizeToMultiBuffer(reader io.Reader, size int32) (MultiBuffer, error) { bSize = Size } b := NewSize(uint32(bSize)) - if err := b.Reset(ReadFullFrom(reader, int(bSize))); err != nil { + if err := b.Reset(ReadFullFrom(reader, bSize)); err != nil { mb.Release() return nil, err } @@ -174,12 +174,12 @@ func (mb MultiBuffer) ToNetBuffers() net.Buffers { } // SliceBySize splits the beginning of this MultiBuffer into another one, for at most size bytes. -func (mb *MultiBuffer) SliceBySize(size int) MultiBuffer { +func (mb *MultiBuffer) SliceBySize(size int32) MultiBuffer { slice := NewMultiBufferCap(10) sliceSize := 0 endIndex := len(*mb) for i, b := range *mb { - if b.Len()+sliceSize > size { + if int32(b.Len()+sliceSize) > size { endIndex = i break } diff --git a/common/buf/reader.go b/common/buf/reader.go index 0dafeec7f..72d955000 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -158,7 +158,7 @@ func (r *BufferedReader) ReadAtMost(size int) (MultiBuffer, error) { r.leftOver = mb } - mb := r.leftOver.SliceBySize(size) + mb := r.leftOver.SliceBySize(int32(size)) if r.leftOver.IsEmpty() { r.leftOver = nil } diff --git a/common/crypto/auth.go b/common/crypto/auth.go index e7b3e5597..7aecea41f 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -144,7 +144,7 @@ func (r *AuthenticationReader) readInternal(soft bool) (*buf.Buffer, error) { } b := buf.NewSize(uint32(size)) - if err := b.Reset(buf.ReadFullFrom(r.reader, int(size))); err != nil { + if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil { b.Release() return nil, err } diff --git a/common/protocol/address.go b/common/protocol/address.go index f7d322209..9534c5b50 100644 --- a/common/protocol/address.go +++ b/common/protocol/address.go @@ -99,7 +99,7 @@ func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Addres return nil, err } domainLength := int(b.Byte(b.Len() - 1)) - if err := b.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { + if err := b.AppendSupplier(buf.ReadFullFrom(reader, int32(domainLength))); err != nil { return nil, err } domain := string(b.BytesFrom(-domainLength)) diff --git a/proxy/shadowsocks/ota.go b/proxy/shadowsocks/ota.go index be18be8c7..e574bcb0f 100644 --- a/proxy/shadowsocks/ota.go +++ b/proxy/shadowsocks/ota.go @@ -77,7 +77,7 @@ func (v *ChunkReader) ReadMultiBuffer() (buf.MultiBuffer, error) { size += AuthSize buffer := buf.NewSize(uint32(size)) - if err := buffer.AppendSupplier(buf.ReadFullFrom(v.reader, int(size))); err != nil { + if err := buffer.AppendSupplier(buf.ReadFullFrom(v.reader, int32(size))); err != nil { buffer.Release() return nil, err } diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index a52e310f2..d6cfd31cb 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -41,7 +41,7 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea ivLen := account.Cipher.IVSize() var iv []byte if ivLen > 0 { - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, ivLen)); err != nil { + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, int32(ivLen))); err != nil { return nil, nil, newError("failed to read IV").Base(err) } @@ -70,7 +70,7 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea // Invalid address. Continue to read some bytes to confuse client. nBytes := dice.Roll(32) + 1 buffer.Clear() - buffer.AppendSupplier(buf.ReadFullFrom(br, nBytes)) + buffer.AppendSupplier(buf.ReadFullFrom(br, int32(nBytes))) return nil, nil, newError("failed to read address").Base(err) } @@ -227,7 +227,7 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff buffer := buf.New() ivLen := account.Cipher.IVSize() if ivLen > 0 { - common.Must(buffer.Reset(buf.ReadFullFrom(rand.Reader, ivLen))) + common.Must(buffer.Reset(buf.ReadFullFrom(rand.Reader, int32(ivLen)))) } iv := buffer.Bytes() diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index 10c0f2d92..8715e30c8 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -97,7 +97,7 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol if version == socks5Version { nMethod := int(buffer.Byte(1)) - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, nMethod)); err != nil { + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, int32(nMethod))); err != nil { return nil, newError("failed to read auth methods").Base(err) } @@ -190,7 +190,7 @@ func readUsernamePassword(reader io.Reader) (string, string, error) { if err := buffer.Reset(buf.ReadFullFrom(reader, 2)); err != nil { return "", "", err } - nUsername := int(buffer.Byte(1)) + nUsername := int32(buffer.Byte(1)) if err := buffer.Reset(buf.ReadFullFrom(reader, nUsername)); err != nil { return "", "", err @@ -200,7 +200,7 @@ func readUsernamePassword(reader io.Reader) (string, string, error) { if err := buffer.Reset(buf.ReadFullFrom(reader, 1)); err != nil { return "", "", err } - nPassword := int(buffer.Byte(0)) + nPassword := int32(buffer.Byte(0)) if err := buffer.Reset(buf.ReadFullFrom(reader, nPassword)); err != nil { return "", "", err } diff --git a/proxy/vmess/encoding/client.go b/proxy/vmess/encoding/client.go index 688e0e241..c8dae88f1 100644 --- a/proxy/vmess/encoding/client.go +++ b/proxy/vmess/encoding/client.go @@ -87,7 +87,7 @@ func (c *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ } if padingLen > 0 { - common.Must(buffer.AppendSupplier(buf.ReadFullFrom(rand.Reader, padingLen))) + common.Must(buffer.AppendSupplier(buf.ReadFullFrom(rand.Reader, int32(padingLen)))) } fnv1a := fnv.New32a() @@ -191,7 +191,7 @@ func (c *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon if buffer.Byte(2) != 0 { cmdID := buffer.Byte(2) - dataLen := int(buffer.Byte(3)) + dataLen := int32(buffer.Byte(3)) if err := buffer.Reset(buf.ReadFullFrom(c.responseReader, dataLen)); err != nil { return nil, newError("failed to read response command").Base(err) diff --git a/proxy/vmess/encoding/server.go b/proxy/vmess/encoding/server.go index 438ca4600..d3f713c34 100644 --- a/proxy/vmess/encoding/server.go +++ b/proxy/vmess/encoding/server.go @@ -168,7 +168,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request if invalidRequestErr != nil { randomLen := dice.Roll(64) + 1 // Read random number of bytes for prevent detection. - buffer.AppendSupplier(buf.ReadFullFrom(decryptor, randomLen)) + buffer.AppendSupplier(buf.ReadFullFrom(decryptor, int32(randomLen))) } }() @@ -195,7 +195,7 @@ func (s *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request } if padingLen > 0 { - if err := buffer.AppendSupplier(buf.ReadFullFrom(decryptor, padingLen)); err != nil { + if err := buffer.AppendSupplier(buf.ReadFullFrom(decryptor, int32(padingLen))); err != nil { return nil, newError("failed to read padding").Base(err) } } From a58063e7ac9f160ffbe90e55b5fc86a2e4cf6559 Mon Sep 17 00:00:00 2001 From: wuxiangzhou2010 Date: Mon, 2 Apr 2018 15:17:36 +0800 Subject: [PATCH 163/183] fix typo, first sound of user is not a vowel, but the consonant /j/ --- app/proxyman/command/command.go | 4 ++-- common/protocol/account.go | 2 +- common/protocol/context.go | 4 ++-- proxy/proxy.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) mode change 100644 => 100755 app/proxyman/command/command.go mode change 100644 => 100755 common/protocol/account.go mode change 100644 => 100755 common/protocol/context.go mode change 100644 => 100755 proxy/proxy.go diff --git a/app/proxyman/command/command.go b/app/proxyman/command/command.go old mode 100644 new mode 100755 index ca0b662c4..e80f3d2c2 --- a/app/proxyman/command/command.go +++ b/app/proxyman/command/command.go @@ -37,7 +37,7 @@ func (op *AddUserOperation) ApplyInbound(ctx context.Context, handler core.Inbou } um, ok := p.(proxy.UserManager) if !ok { - return newError("proxy is not an UserManager") + return newError("proxy is not a UserManager") } return um.AddUser(ctx, op.User) } @@ -50,7 +50,7 @@ func (op *RemoveUserOperation) ApplyInbound(ctx context.Context, handler core.In } um, ok := p.(proxy.UserManager) if !ok { - return newError("proxy is not an UserManager") + return newError("proxy is not a UserManager") } return um.RemoveUser(ctx, op.Email) } diff --git a/common/protocol/account.go b/common/protocol/account.go old mode 100644 new mode 100755 index 1c6a1eeaa..7793974a7 --- a/common/protocol/account.go +++ b/common/protocol/account.go @@ -1,6 +1,6 @@ package protocol -// Account is an user identity used for authentication. +// Account is a user identity used for authentication. type Account interface { Equals(Account) bool } diff --git a/common/protocol/context.go b/common/protocol/context.go old mode 100644 new mode 100755 index 6a2d24090..17437719a --- a/common/protocol/context.go +++ b/common/protocol/context.go @@ -10,12 +10,12 @@ const ( userKey key = iota ) -// ContextWithUser returns a context combined with an User. +// ContextWithUser returns a context combined with a User. func ContextWithUser(ctx context.Context, user *User) context.Context { return context.WithValue(ctx, userKey, user) } -// UserFromContext extracts an User from the given context, if any. +// UserFromContext extracts a User from the given context, if any. func UserFromContext(ctx context.Context) *User { v := ctx.Value(userKey) if v == nil { diff --git a/proxy/proxy.go b/proxy/proxy.go old mode 100644 new mode 100755 index 0278d1185..a0d0ac422 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -41,7 +41,7 @@ type UserManager interface { // AddUser adds a new user. AddUser(context.Context, *protocol.User) error - // RemoveUser removes an user by email. + // RemoveUser removes a user by email. RemoveUser(context.Context, string) error } From 5a08411ee20b6669cb680abc3af6abd13f075049 Mon Sep 17 00:00:00 2001 From: wuxiangzhou2010 Date: Mon, 2 Apr 2018 15:52:16 +0800 Subject: [PATCH 164/183] fix typo --- common/crypto/chunk.go | 4 ++-- common/protocol/id.go | 2 +- common/serial/bytes.go | 4 ++-- common/serial/numbers.go | 2 +- common/signal/done.go | 2 +- common/signal/notifier.go | 2 +- common/uuid/uuid.go | 6 +++--- config.go | 2 +- proxy/vmess/account.pb.go | 2 +- proxy/vmess/account.proto | 2 +- transport/internet/kcp/segment.go | 2 +- v2ray.go | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) mode change 100644 => 100755 common/crypto/chunk.go mode change 100644 => 100755 common/protocol/id.go mode change 100644 => 100755 common/serial/bytes.go mode change 100644 => 100755 common/serial/numbers.go mode change 100644 => 100755 common/signal/done.go mode change 100644 => 100755 common/signal/notifier.go mode change 100644 => 100755 common/uuid/uuid.go mode change 100644 => 100755 config.go mode change 100644 => 100755 proxy/vmess/account.pb.go mode change 100644 => 100755 proxy/vmess/account.proto mode change 100644 => 100755 transport/internet/kcp/segment.go diff --git a/common/crypto/chunk.go b/common/crypto/chunk.go old mode 100644 new mode 100755 index e54119984..decfdffc9 --- a/common/crypto/chunk.go +++ b/common/crypto/chunk.go @@ -8,13 +8,13 @@ import ( "v2ray.com/core/common/serial" ) -// ChunkSizeDecoder is an utility class to decode size value from bytes. +// ChunkSizeDecoder is a utility class to decode size value from bytes. type ChunkSizeDecoder interface { SizeBytes() int Decode([]byte) (uint16, error) } -// ChunkSizeEncoder is an utility class to encode size value into bytes. +// ChunkSizeEncoder is a utility class to encode size value into bytes. type ChunkSizeEncoder interface { SizeBytes() int Encode(uint16, []byte) []byte diff --git a/common/protocol/id.go b/common/protocol/id.go old mode 100644 new mode 100755 index e6b0176ee..6deb0ccec --- a/common/protocol/id.go +++ b/common/protocol/id.go @@ -19,7 +19,7 @@ func DefaultIDHash(key []byte) hash.Hash { return hmac.New(md5.New, key) } -// The ID of en entity, in the form of an UUID. +// The ID of en entity, in the form of a UUID. type ID struct { uuid uuid.UUID cmdKey [IDBytesLen]byte diff --git a/common/serial/bytes.go b/common/serial/bytes.go old mode 100644 new mode 100755 index 14c7676cc..69475b19a --- a/common/serial/bytes.go +++ b/common/serial/bytes.go @@ -7,12 +7,12 @@ func ByteToHexString(value byte) string { return hex.EncodeToString([]byte{value}) } -// BytesToUint16 deserializes a byte array to an uint16 in big endian order. The byte array must have at least 2 elements. +// BytesToUint16 deserializes a byte array to a uint16 in big endian order. The byte array must have at least 2 elements. func BytesToUint16(value []byte) uint16 { return uint16(value[0])<<8 | uint16(value[1]) } -// BytesToUint32 deserializes a byte array to an uint32 in big endian order. The byte array must have at least 4 elements. +// BytesToUint32 deserializes a byte array to a uint32 in big endian order. The byte array must have at least 4 elements. func BytesToUint32(value []byte) uint32 { return uint32(value[0])<<24 | uint32(value[1])<<16 | diff --git a/common/serial/numbers.go b/common/serial/numbers.go old mode 100644 new mode 100755 index 556fcb345..3b15c38ff --- a/common/serial/numbers.go +++ b/common/serial/numbers.go @@ -3,7 +3,7 @@ package serial import "strconv" import "io" -// Uint16ToBytes serializes an uint16 into bytes in big endian order. +// Uint16ToBytes serializes a uint16 into bytes in big endian order. func Uint16ToBytes(value uint16, b []byte) []byte { return append(b, byte(value>>8), byte(value)) } diff --git a/common/signal/done.go b/common/signal/done.go old mode 100644 new mode 100755 index bedf50abe..351f8c630 --- a/common/signal/done.go +++ b/common/signal/done.go @@ -4,7 +4,7 @@ import ( "sync" ) -// Done is an utility for notifications of something being done. +// Done is a utility for notifications of something being done. type Done struct { access sync.Mutex c chan struct{} diff --git a/common/signal/notifier.go b/common/signal/notifier.go old mode 100644 new mode 100755 index 0a0362785..19836e54f --- a/common/signal/notifier.go +++ b/common/signal/notifier.go @@ -1,6 +1,6 @@ package signal -// Notifier is an utility for notifying changes. The change producer may notify changes multiple time, and the consumer may get notified asynchronously. +// Notifier is a utility for notifying changes. The change producer may notify changes multiple time, and the consumer may get notified asynchronously. type Notifier struct { c chan struct{} } diff --git a/common/uuid/uuid.go b/common/uuid/uuid.go old mode 100644 new mode 100755 index 0e0ca3869..06b219a0f --- a/common/uuid/uuid.go +++ b/common/uuid/uuid.go @@ -61,14 +61,14 @@ func (u *UUID) Next() UUID { } } -// New creates an UUID with random value. +// New creates a UUID with random value. func New() UUID { var uuid UUID common.Must2(rand.Read(uuid.Bytes())) return uuid } -// ParseBytes converts an UUID in byte form to object. +// ParseBytes converts a UUID in byte form to object. func ParseBytes(b []byte) (UUID, error) { var uuid UUID if len(b) != 16 { @@ -78,7 +78,7 @@ func ParseBytes(b []byte) (UUID, error) { return uuid, nil } -// ParseString converts an UUID in string form to object. +// ParseString converts a UUID in string form to object. func ParseString(str string) (UUID, error) { var uuid UUID diff --git a/config.go b/config.go old mode 100644 new mode 100755 index 345af0c5e..d03f0673e --- a/config.go +++ b/config.go @@ -16,7 +16,7 @@ type ConfigFormat struct { Loader ConfigLoader } -// ConfigLoader is an utility to load V2Ray config from external source. +// ConfigLoader is a utility to load V2Ray config from external source. type ConfigLoader func(input io.Reader) (*Config, error) var ( diff --git a/proxy/vmess/account.pb.go b/proxy/vmess/account.pb.go old mode 100644 new mode 100755 index d78ba2960..05607fa07 --- a/proxy/vmess/account.pb.go +++ b/proxy/vmess/account.pb.go @@ -17,7 +17,7 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type Account struct { - // ID of the account, in the form of an UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57". + // ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57". Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` // Number of alternative IDs. Client and server must share the same number. AlterId uint32 `protobuf:"varint,2,opt,name=alter_id,json=alterId" json:"alter_id,omitempty"` diff --git a/proxy/vmess/account.proto b/proxy/vmess/account.proto old mode 100644 new mode 100755 index c69317d97..343749144 --- a/proxy/vmess/account.proto +++ b/proxy/vmess/account.proto @@ -9,7 +9,7 @@ option java_multiple_files = true; import "v2ray.com/core/common/protocol/headers.proto"; message Account { - // ID of the account, in the form of an UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57". + // ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57". string id = 1; // Number of alternative IDs. Client and server must share the same number. uint32 alter_id = 2; diff --git a/transport/internet/kcp/segment.go b/transport/internet/kcp/segment.go old mode 100644 new mode 100755 index 9af1629dd..25b475900 --- a/transport/internet/kcp/segment.go +++ b/transport/internet/kcp/segment.go @@ -9,7 +9,7 @@ import ( type Command byte const ( - // CommandACK indicates a AckSegment. + // CommandACK indicates an AckSegment. CommandACK Command = 0 // CommandData indicates a DataSegment. CommandData Command = 1 diff --git a/v2ray.go b/v2ray.go index 8bd4dc718..28af89a63 100755 --- a/v2ray.go +++ b/v2ray.go @@ -94,7 +94,7 @@ func (s *Instance) CreateObject(config interface{}) (interface{}, error) { return common.CreateObject(ctx, config) } -// ID returns an unique ID for this V2Ray instance. +// ID returns a unique ID for this V2Ray instance. func (s *Instance) ID() uuid.UUID { return s.id } From f0862df9a76f7e4c70889b8ca5bab92101bba24c Mon Sep 17 00:00:00 2001 From: wuxiangzhou2010 Date: Mon, 2 Apr 2018 16:12:35 +0800 Subject: [PATCH 165/183] fix typo --- core.go | 2 +- proxy/http/server.go | 2 +- transport/internet/websocket/ws.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) mode change 100644 => 100755 core.go mode change 100644 => 100755 proxy/http/server.go mode change 100644 => 100755 transport/internet/websocket/ws.go diff --git a/core.go b/core.go old mode 100644 new mode 100755 index bcc3fc686..5ad599ba1 --- a/core.go +++ b/core.go @@ -19,7 +19,7 @@ var ( version = "3.16" build = "Custom" codename = "die Commanderin" - intro = "An unified platform for anti-censorship." + intro = "A unified platform for anti-censorship." ) // Version returns V2Ray's version as a string, in the form of "x.y.z" where x, y and z are numbers. diff --git a/proxy/http/server.go b/proxy/http/server.go old mode 100644 new mode 100755 index ec30bd115..ffc7a1bc5 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -21,7 +21,7 @@ import ( "v2ray.com/core/transport/internet" ) -// Server is a HTTP proxy server. +// Server is an HTTP proxy server. type Server struct { config *ServerConfig v *core.Instance diff --git a/transport/internet/websocket/ws.go b/transport/internet/websocket/ws.go old mode 100644 new mode 100755 index 7e5f1c7af..e9863b031 --- a/transport/internet/websocket/ws.go +++ b/transport/internet/websocket/ws.go @@ -1,6 +1,6 @@ /*Package websocket implements Websocket transport -Websocket transport implements a HTTP(S) compliable, surveillance proof transport method with plausible deniability. +Websocket transport implements an HTTP(S) compliable, surveillance proof transport method with plausible deniability. */ package websocket From 08dab81eb2d9fe9a867e13cf92c40a1d953df24d Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 2 Apr 2018 20:00:50 +0200 Subject: [PATCH 166/183] migrate int to int32 --- app/proxyman/mux/reader.go | 4 +-- app/proxyman/mux/writer.go | 2 +- common/buf/buffer.go | 36 ++++++++++---------- common/buf/buffer_test.go | 4 +-- common/buf/multi_buffer.go | 12 +++---- common/buf/multi_buffer_test.go | 4 +-- common/buf/reader.go | 8 ++--- common/buf/reader_test.go | 10 +++--- common/buf/writer_test.go | 2 +- common/crypto/auth.go | 10 +++--- common/crypto/auth_test.go | 8 ++--- common/crypto/chunk.go | 4 +-- common/crypto/chunk_test.go | 6 ++-- common/protocol/address.go | 2 +- proxy/shadowsocks/config.go | 32 ++++++++--------- proxy/socks/protocol.go | 8 ++--- testing/scenarios/shadowsocks_test.go | 2 +- transport/internet/header.go | 2 +- transport/internet/headers/http/http.go | 10 +++--- transport/internet/headers/http/http_test.go | 2 +- transport/internet/headers/noop/noop.go | 2 +- transport/internet/headers/srtp/srtp.go | 2 +- transport/internet/headers/srtp/srtp_test.go | 2 +- transport/internet/headers/utp/utp.go | 2 +- transport/internet/headers/utp/utp_test.go | 2 +- transport/internet/headers/wechat/wechat.go | 2 +- transport/internet/kcp/io.go | 2 +- transport/internet/kcp/segment.go | 12 +++---- transport/internet/kcp/segment_test.go | 8 ++--- transport/ray/connection.go | 2 +- 30 files changed, 100 insertions(+), 104 deletions(-) diff --git a/app/proxyman/mux/reader.go b/app/proxyman/mux/reader.go index 3443dd802..641f074cb 100644 --- a/app/proxyman/mux/reader.go +++ b/app/proxyman/mux/reader.go @@ -89,7 +89,7 @@ func (r *StreamReader) ReadMultiBuffer() (buf.MultiBuffer, error) { r.leftOver = int32(size) } - mb, err := r.reader.ReadAtMost(int(r.leftOver)) - r.leftOver -= int32(mb.Len()) + mb, err := r.reader.ReadAtMost(r.leftOver) + r.leftOver -= mb.Len() return mb, err } diff --git a/app/proxyman/mux/writer.go b/app/proxyman/mux/writer.go index b7ecfa4e0..b043909f3 100644 --- a/app/proxyman/mux/writer.go +++ b/app/proxyman/mux/writer.go @@ -71,7 +71,7 @@ func (w *Writer) writeData(mb buf.MultiBuffer) error { return err } - mb2 := buf.NewMultiBufferCap(len(mb) + 1) + mb2 := buf.NewMultiBufferCap(int32(len(mb)) + 1) mb2.Append(frame) mb2.AppendMulti(mb) return w.writer.WriteMultiBuffer(mb2) diff --git a/common/buf/buffer.go b/common/buf/buffer.go index d2ca828e6..340cdf76e 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -56,13 +56,13 @@ func (b *Buffer) AppendSupplier(writer Supplier) error { } // Byte returns the bytes at index. -func (b *Buffer) Byte(index int) byte { - return b.v[b.start+int32(index)] +func (b *Buffer) Byte(index int32) byte { + return b.v[b.start+index] } // SetByte sets the byte value at index. -func (b *Buffer) SetByte(index int, value byte) { - b.v[b.start+int32(index)] = value +func (b *Buffer) SetByte(index int32, value byte) { + b.v[b.start+index] = value } // Bytes returns the content bytes of this Buffer. @@ -79,34 +79,34 @@ func (b *Buffer) Reset(writer Supplier) error { } // BytesRange returns a slice of this buffer with given from and to boundary. -func (b *Buffer) BytesRange(from, to int) []byte { +func (b *Buffer) BytesRange(from, to int32) []byte { if from < 0 { from += b.Len() } if to < 0 { to += b.Len() } - return b.v[b.start+int32(from) : b.start+int32(to)] + return b.v[b.start+from : b.start+to] } // BytesFrom returns a slice of this Buffer starting from the given position. -func (b *Buffer) BytesFrom(from int) []byte { +func (b *Buffer) BytesFrom(from int32) []byte { if from < 0 { from += b.Len() } - return b.v[b.start+int32(from) : b.end] + return b.v[b.start+from : b.end] } // BytesTo returns a slice of this Buffer from start to the given position. -func (b *Buffer) BytesTo(to int) []byte { +func (b *Buffer) BytesTo(to int32) []byte { if to < 0 { to += b.Len() } - return b.v[b.start : b.start+int32(to)] + return b.v[b.start : b.start+to] } // Slice cuts the buffer at the given position. -func (b *Buffer) Slice(from, to int) { +func (b *Buffer) Slice(from, to int32) { if from < 0 { from += b.Len() } @@ -116,24 +116,24 @@ func (b *Buffer) Slice(from, to int) { if to < from { panic("Invalid slice") } - b.end = b.start + int32(to) - b.start += int32(from) + b.end = b.start + to + b.start += from } // SliceFrom cuts the buffer at the given position. -func (b *Buffer) SliceFrom(from int) { +func (b *Buffer) SliceFrom(from int32) { if from < 0 { from += b.Len() } - b.start += int32(from) + b.start += from } // Len returns the length of the buffer content. -func (b *Buffer) Len() int { +func (b *Buffer) Len() int32 { if b == nil { return 0 } - return int(b.end - b.start) + return b.end - b.start } // IsEmpty returns true if the buffer is empty. @@ -159,7 +159,7 @@ func (b *Buffer) Read(data []byte) (int, error) { return 0, io.EOF } nBytes := copy(data, b.v[b.start:b.end]) - if nBytes == b.Len() { + if int32(nBytes) == b.Len() { b.Clear() } else { b.start += int32(nBytes) diff --git a/common/buf/buffer_test.go b/common/buf/buffer_test.go index a51687d64..e00ba28f2 100644 --- a/common/buf/buffer_test.go +++ b/common/buf/buffer_test.go @@ -16,10 +16,10 @@ func TestBufferClear(t *testing.T) { payload := "Bytes" buffer.Append([]byte(payload)) - assert(buffer.Len(), Equals, len(payload)) + assert(buffer.Len(), Equals, int32(len(payload))) buffer.Clear() - assert(buffer.Len(), Equals, 0) + assert(buffer.Len(), Equals, int32(0)) } func TestBufferIsEmpty(t *testing.T) { diff --git a/common/buf/multi_buffer.go b/common/buf/multi_buffer.go index ade8630c8..60b7ce8e9 100644 --- a/common/buf/multi_buffer.go +++ b/common/buf/multi_buffer.go @@ -66,7 +66,7 @@ func ReadAllToBytes(reader io.Reader) ([]byte, error) { type MultiBuffer []*Buffer // NewMultiBufferCap creates a new MultiBuffer instance. -func NewMultiBufferCap(capacity int) MultiBuffer { +func NewMultiBufferCap(capacity int32) MultiBuffer { return MultiBuffer(make([]*Buffer, 0, capacity)) } @@ -93,7 +93,7 @@ func (mb MultiBuffer) Copy(b []byte) int { for _, bb := range mb { nBytes := copy(b[total:], bb.Bytes()) total += nBytes - if nBytes < bb.Len() { + if int32(nBytes) < bb.Len() { break } } @@ -137,8 +137,8 @@ func (mb *MultiBuffer) Write(b []byte) { } // Len returns the total number of bytes in the MultiBuffer. -func (mb MultiBuffer) Len() int { - size := 0 +func (mb MultiBuffer) Len() int32 { + size := int32(0) for _, b := range mb { size += b.Len() } @@ -176,10 +176,10 @@ func (mb MultiBuffer) ToNetBuffers() net.Buffers { // SliceBySize splits the beginning of this MultiBuffer into another one, for at most size bytes. func (mb *MultiBuffer) SliceBySize(size int32) MultiBuffer { slice := NewMultiBufferCap(10) - sliceSize := 0 + sliceSize := int32(0) endIndex := len(*mb) for i, b := range *mb { - if int32(b.Len()+sliceSize) > size { + if b.Len()+sliceSize > size { endIndex = i break } diff --git a/common/buf/multi_buffer_test.go b/common/buf/multi_buffer_test.go index e78918f44..656d0656e 100644 --- a/common/buf/multi_buffer_test.go +++ b/common/buf/multi_buffer_test.go @@ -33,7 +33,7 @@ func TestMultiBufferAppend(t *testing.T) { b := New() b.AppendBytes('a', 'b') mb.Append(b) - assert(mb.Len(), Equals, 2) + assert(mb.Len(), Equals, int32(2)) } func TestMultiBufferSliceBySizeLarge(t *testing.T) { @@ -46,5 +46,5 @@ func TestMultiBufferSliceBySizeLarge(t *testing.T) { mb.Append(lb) mb2 := mb.SliceBySize(4 * 1024) - assert(mb2.Len(), Equals, 4*1024) + assert(mb2.Len(), Equals, int32(4*1024)) } diff --git a/common/buf/reader.go b/common/buf/reader.go index 72d955000..df7af923d 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -51,7 +51,7 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) { nBytes, err := r.Reader.Read(r.buffer) if nBytes > 0 { - mb := NewMultiBufferCap(nBytes/Size + 1) + mb := NewMultiBufferCap(int32(nBytes/Size) + 1) mb.Write(r.buffer[:nBytes]) if nBytes == len(r.buffer) && nBytes < int(largeSize) { freeBytes(r.buffer) @@ -99,7 +99,7 @@ func (r *BufferedReader) IsBuffered() bool { // BufferedBytes returns the number of bytes that is cached in this reader. func (r *BufferedReader) BufferedBytes() int32 { - return int32(r.leftOver.Len()) + return r.leftOver.Len() } // ReadByte implements io.ByteReader. @@ -149,7 +149,7 @@ func (r *BufferedReader) ReadMultiBuffer() (MultiBuffer, error) { } // ReadAtMost returns a MultiBuffer with at most size. -func (r *BufferedReader) ReadAtMost(size int) (MultiBuffer, error) { +func (r *BufferedReader) ReadAtMost(size int32) (MultiBuffer, error) { if r.leftOver == nil { mb, err := r.stream.ReadMultiBuffer() if mb.IsEmpty() && err != nil { @@ -158,7 +158,7 @@ func (r *BufferedReader) ReadAtMost(size int) (MultiBuffer, error) { r.leftOver = mb } - mb := r.leftOver.SliceBySize(int32(size)) + mb := r.leftOver.SliceBySize(size) if r.leftOver.IsEmpty() { r.leftOver = nil } diff --git a/common/buf/reader_test.go b/common/buf/reader_test.go index 36cd003bb..6086a7301 100644 --- a/common/buf/reader_test.go +++ b/common/buf/reader_test.go @@ -17,23 +17,23 @@ func TestAdaptiveReader(t *testing.T) { reader := NewReader(bytes.NewReader(make([]byte, 1024*1024))) b, err := reader.ReadMultiBuffer() assert(err, IsNil) - assert(b.Len(), Equals, 2*1024) + assert(b.Len(), Equals, int32(2*1024)) b, err = reader.ReadMultiBuffer() assert(err, IsNil) - assert(b.Len(), Equals, 8*1024) + assert(b.Len(), Equals, int32(8*1024)) b, err = reader.ReadMultiBuffer() assert(err, IsNil) - assert(b.Len(), Equals, 32*1024) + assert(b.Len(), Equals, int32(32*1024)) b, err = reader.ReadMultiBuffer() assert(err, IsNil) - assert(b.Len(), Equals, 128*1024) + assert(b.Len(), Equals, int32(128*1024)) b, err = reader.ReadMultiBuffer() assert(err, IsNil) - assert(b.Len(), Equals, 512*1024) + assert(b.Len(), Equals, int32(512*1024)) } func TestBytesReaderWriteTo(t *testing.T) { diff --git a/common/buf/writer_test.go b/common/buf/writer_test.go index 1dbe2f224..b90f84c9c 100644 --- a/common/buf/writer_test.go +++ b/common/buf/writer_test.go @@ -47,7 +47,7 @@ func TestBytesWriterReadFrom(t *testing.T) { mb, err := cache.ReadMultiBuffer() assert(err, IsNil) - assert(mb.Len(), Equals, size) + assert(mb.Len(), Equals, int32(size)) } func TestDiscardBytes(t *testing.T) { diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 7aecea41f..7ea682d92 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -149,12 +149,12 @@ func (r *AuthenticationReader) readInternal(soft bool) (*buf.Buffer, error) { return nil, err } - rb, err := r.auth.Open(b.BytesTo(0), b.BytesTo(int(size))) + rb, err := r.auth.Open(b.BytesTo(0), b.BytesTo(size)) if err != nil { b.Release() return nil, err } - b.Slice(0, len(rb)) + b.Slice(0, int32(len(rb))) return b, nil } @@ -200,7 +200,7 @@ func NewAuthenticationWriter(auth Authenticator, sizeParser ChunkSizeEncoder, wr } func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) { - encryptedSize := b.Len() + w.auth.Overhead() + encryptedSize := int(b.Len()) + w.auth.Overhead() eb := buf.New() common.Must(eb.Reset(func(bb []byte) (int, error) { @@ -222,7 +222,7 @@ func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error { defer mb.Release() payloadSize := buf.Size - w.auth.Overhead() - w.sizeParser.SizeBytes() - mb2Write := buf.NewMultiBufferCap(len(mb) + 10) + mb2Write := buf.NewMultiBufferCap(int32(len(mb) + 10)) for { b := buf.New() @@ -256,7 +256,7 @@ func (w *AuthenticationWriter) writePacket(mb buf.MultiBuffer) error { return w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(eb)) } - mb2Write := buf.NewMultiBufferCap(len(mb) + 1) + mb2Write := buf.NewMultiBufferCap(int32(len(mb)) + 1) for !mb.IsEmpty() { b := mb.SplitFirst() diff --git a/common/crypto/auth_test.go b/common/crypto/auth_test.go index b3cbe4f6e..702ddbb33 100644 --- a/common/crypto/auth_test.go +++ b/common/crypto/auth_test.go @@ -30,7 +30,7 @@ func TestAuthenticationReaderWriter(t *testing.T) { payload := buf.NewSize(payloadSize) payload.Append(rawPayload) - assert(payload.Len(), Equals, payloadSize) + assert(payload.Len(), Equals, int32(payloadSize)) cache := buf.NewSize(160 * 1024) iv := make([]byte, 12) @@ -45,7 +45,7 @@ func TestAuthenticationReaderWriter(t *testing.T) { }, PlainChunkSizeParser{}, cache, protocol.TransferTypeStream) assert(writer.WriteMultiBuffer(buf.NewMultiBufferValue(payload)), IsNil) - assert(cache.Len(), Equals, 82658) + assert(cache.Len(), Equals, int32(82658)) assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil) reader := NewAuthenticationReader(&AEADAuthenticator{ @@ -65,7 +65,7 @@ func TestAuthenticationReaderWriter(t *testing.T) { mb.AppendMulti(mb2) } - assert(mb.Len(), Equals, payloadSize) + assert(mb.Len(), Equals, int32(payloadSize)) mbContent := make([]byte, payloadSize) mb.Read(mbContent) @@ -108,7 +108,7 @@ func TestAuthenticationReaderWriterPacket(t *testing.T) { payload.Append(pb2) assert(writer.WriteMultiBuffer(payload), IsNil) - assert(cache.Len(), GreaterThan, 0) + assert(cache.Len(), GreaterThan, int32(0)) assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil) assert(err, IsNil) diff --git a/common/crypto/chunk.go b/common/crypto/chunk.go index e54119984..e58078d47 100644 --- a/common/crypto/chunk.go +++ b/common/crypto/chunk.go @@ -62,7 +62,7 @@ type ChunkStreamReader struct { reader *buf.BufferedReader buffer []byte - leftOverSize int + leftOverSize int32 } func NewChunkStreamReader(sizeDecoder ChunkSizeDecoder, reader io.Reader) *ChunkStreamReader { @@ -90,7 +90,7 @@ func (r *ChunkStreamReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if nextSize == 0 { return nil, io.EOF } - size = int(nextSize) + size = int32(nextSize) } r.leftOverSize = size diff --git a/common/crypto/chunk_test.go b/common/crypto/chunk_test.go index d5d8c9e5b..f1ef5d285 100644 --- a/common/crypto/chunk_test.go +++ b/common/crypto/chunk_test.go @@ -27,16 +27,16 @@ func TestChunkStreamIO(t *testing.T) { assert(writer.WriteMultiBuffer(buf.MultiBuffer{}), IsNil) - assert(cache.Len(), Equals, 13) + assert(cache.Len(), Equals, int32(13)) mb, err := reader.ReadMultiBuffer() assert(err, IsNil) - assert(mb.Len(), Equals, 4) + assert(mb.Len(), Equals, int32(4)) assert(mb[0].Bytes(), Equals, []byte("abcd")) mb, err = reader.ReadMultiBuffer() assert(err, IsNil) - assert(mb.Len(), Equals, 3) + assert(mb.Len(), Equals, int32(3)) assert(mb[0].Bytes(), Equals, []byte("efg")) _, err = reader.ReadMultiBuffer() diff --git a/common/protocol/address.go b/common/protocol/address.go index 9534c5b50..48daf7614 100644 --- a/common/protocol/address.go +++ b/common/protocol/address.go @@ -98,7 +98,7 @@ func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Addres if err := b.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil { return nil, err } - domainLength := int(b.Byte(b.Len() - 1)) + domainLength := int32(b.Byte(b.Len() - 1)) if err := b.AppendSupplier(buf.ReadFullFrom(reader, int32(domainLength))); err != nil { return nil, err } diff --git a/proxy/shadowsocks/config.go b/proxy/shadowsocks/config.go index 4d67b02bf..3c30a1c09 100644 --- a/proxy/shadowsocks/config.go +++ b/proxy/shadowsocks/config.go @@ -96,8 +96,8 @@ func (a *Account) AsAccount() (protocol.Account, error) { // Cipher is an interface for all Shadowsocks ciphers. type Cipher interface { - KeySize() int - IVSize() int + KeySize() int32 + IVSize() int32 NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) IsAEAD() bool @@ -107,18 +107,18 @@ type Cipher interface { // AesCfb represents all AES-CFB ciphers. type AesCfb struct { - KeyBytes int + KeyBytes int32 } func (*AesCfb) IsAEAD() bool { return false } -func (v *AesCfb) KeySize() int { +func (v *AesCfb) KeySize() int32 { return v.KeyBytes } -func (v *AesCfb) IVSize() int { +func (v *AesCfb) IVSize() int32 { return 16 } @@ -151,8 +151,8 @@ func (v *AesCfb) DecodePacket(key []byte, b *buf.Buffer) error { } type AEADCipher struct { - KeyBytes int - IVBytes int + KeyBytes int32 + IVBytes int32 AEADAuthCreator func(key []byte) cipher.AEAD } @@ -160,11 +160,11 @@ func (*AEADCipher) IsAEAD() bool { return true } -func (c *AEADCipher) KeySize() int { +func (c *AEADCipher) KeySize() int32 { return c.KeyBytes } -func (c *AEADCipher) IVSize() int { +func (c *AEADCipher) IVSize() int32 { return c.IVBytes } @@ -226,18 +226,18 @@ func (c *AEADCipher) DecodePacket(key []byte, b *buf.Buffer) error { } type ChaCha20 struct { - IVBytes int + IVBytes int32 } func (*ChaCha20) IsAEAD() bool { return false } -func (v *ChaCha20) KeySize() int { +func (v *ChaCha20) KeySize() int32 { return 32 } -func (v *ChaCha20) IVSize() int { +func (v *ChaCha20) IVSize() int32 { return v.IVBytes } @@ -271,8 +271,8 @@ func (v *ChaCha20) DecodePacket(key []byte, b *buf.Buffer) error { type NoneCipher struct{} -func (NoneCipher) KeySize() int { return 0 } -func (NoneCipher) IVSize() int { return 0 } +func (NoneCipher) KeySize() int32 { return 0 } +func (NoneCipher) IVSize() int32 { return 0 } func (NoneCipher) IsAEAD() bool { return true // to avoid OTA } @@ -293,13 +293,13 @@ func (NoneCipher) DecodePacket(key []byte, b *buf.Buffer) error { return nil } -func passwordToCipherKey(password []byte, keySize int) []byte { +func passwordToCipherKey(password []byte, keySize int32) []byte { key := make([]byte, 0, keySize) md5Sum := md5.Sum(password) key = append(key, md5Sum[:]...) - for len(key) < keySize { + for int32(len(key)) < keySize { md5Hash := md5.New() common.Must2(md5Hash.Write(md5Sum[:])) common.Must2(md5Hash.Write(password)) diff --git a/proxy/socks/protocol.go b/proxy/socks/protocol.go index 8715e30c8..bd3a11823 100644 --- a/proxy/socks/protocol.go +++ b/proxy/socks/protocol.go @@ -26,10 +26,6 @@ const ( authPassword = 0x02 authNoMatchingMethod = 0xFF - addrTypeIPv4 = 0x01 - addrTypeIPv6 = 0x04 - addrTypeDomain = 0x03 - statusSuccess = 0x00 statusCmdNotSupport = 0x07 ) @@ -96,8 +92,8 @@ func (s *ServerSession) Handshake(reader io.Reader, writer io.Writer) (*protocol } if version == socks5Version { - nMethod := int(buffer.Byte(1)) - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, int32(nMethod))); err != nil { + nMethod := int32(buffer.Byte(1)) + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, nMethod)); err != nil { return nil, newError("failed to read auth methods").Base(err) } diff --git a/testing/scenarios/shadowsocks_test.go b/testing/scenarios/shadowsocks_test.go index c93d3923a..6a803157f 100644 --- a/testing/scenarios/shadowsocks_test.go +++ b/testing/scenarios/shadowsocks_test.go @@ -883,7 +883,7 @@ func TestShadowsocksChacha20Poly1305UDPConformance(t *testing.T) { Port: int(serverPort), }) assert(err, IsNil) - assert(nBytes, Equals, payload.Len()) + assert(int32(nBytes), Equals, payload.Len()) conn.SetReadDeadline(time.Now().Add(time.Second * 10)) response := make([]byte, 10240) diff --git a/transport/internet/header.go b/transport/internet/header.go index 20c86d882..c6bde9258 100644 --- a/transport/internet/header.go +++ b/transport/internet/header.go @@ -8,7 +8,7 @@ import ( ) type PacketHeader interface { - Size() int + Size() int32 Write([]byte) (int, error) } diff --git a/transport/internet/headers/http/http.go b/transport/internet/headers/http/http.go index 397b5497a..0a2f9e277 100644 --- a/transport/internet/headers/http/http.go +++ b/transport/internet/headers/http/http.go @@ -57,7 +57,7 @@ type HeaderReader struct { func (*HeaderReader) Read(reader io.Reader) (*buf.Buffer, error) { buffer := buf.New() - totalBytes := 0 + totalBytes := int32(0) endingDetected := false for totalBytes < maxHeaderLength { err := buffer.AppendSupplier(buf.ReadFrom(reader)) @@ -66,13 +66,13 @@ func (*HeaderReader) Read(reader io.Reader) (*buf.Buffer, error) { return nil, err } if n := bytes.Index(buffer.Bytes(), []byte(ENDING)); n != -1 { - buffer.SliceFrom(n + len(ENDING)) + buffer.SliceFrom(int32(n + len(ENDING))) endingDetected = true break } - if buffer.Len() >= len(ENDING) { - totalBytes += buffer.Len() - len(ENDING) - leftover := buffer.BytesFrom(-len(ENDING)) + if buffer.Len() >= int32(len(ENDING)) { + totalBytes += buffer.Len() - int32(len(ENDING)) + leftover := buffer.BytesFrom(-int32(len(ENDING))) buffer.Reset(func(b []byte) (int, error) { return copy(b, leftover), nil }) diff --git a/transport/internet/headers/http/http_test.go b/transport/internet/headers/http/http_test.go index 315cad762..ff8b9c540 100644 --- a/transport/internet/headers/http/http_test.go +++ b/transport/internet/headers/http/http_test.go @@ -21,7 +21,7 @@ func TestReaderWriter(t *testing.T) { writer := NewHeaderWriter(b) err := writer.Write(cache) assert(err, IsNil) - assert(cache.Len(), Equals, 8) + assert(cache.Len(), Equals, int32(8)) _, err = cache.Write([]byte{'e', 'f', 'g'}) assert(err, IsNil) diff --git a/transport/internet/headers/noop/noop.go b/transport/internet/headers/noop/noop.go index 9d1dd7113..a9c87385c 100644 --- a/transport/internet/headers/noop/noop.go +++ b/transport/internet/headers/noop/noop.go @@ -9,7 +9,7 @@ import ( type NoOpHeader struct{} -func (NoOpHeader) Size() int { +func (NoOpHeader) Size() int32 { return 0 } diff --git a/transport/internet/headers/srtp/srtp.go b/transport/internet/headers/srtp/srtp.go index 69e08b4b6..4d5cacf4c 100644 --- a/transport/internet/headers/srtp/srtp.go +++ b/transport/internet/headers/srtp/srtp.go @@ -13,7 +13,7 @@ type SRTP struct { number uint16 } -func (*SRTP) Size() int { +func (*SRTP) Size() int32 { return 4 } diff --git a/transport/internet/headers/srtp/srtp_test.go b/transport/internet/headers/srtp/srtp_test.go index 1b08dae4e..8856df80a 100644 --- a/transport/internet/headers/srtp/srtp_test.go +++ b/transport/internet/headers/srtp/srtp_test.go @@ -22,5 +22,5 @@ func TestSRTPWrite(t *testing.T) { payload.AppendSupplier(srtp.Write) payload.Append(content) - assert(payload.Len(), Equals, len(content)+srtp.Size()) + assert(payload.Len(), Equals, int32(len(content))+srtp.Size()) } diff --git a/transport/internet/headers/utp/utp.go b/transport/internet/headers/utp/utp.go index c76c5fe02..e556bf52f 100644 --- a/transport/internet/headers/utp/utp.go +++ b/transport/internet/headers/utp/utp.go @@ -14,7 +14,7 @@ type UTP struct { connectionId uint16 } -func (*UTP) Size() int { +func (*UTP) Size() int32 { return 4 } diff --git a/transport/internet/headers/utp/utp_test.go b/transport/internet/headers/utp/utp_test.go index a41480e2f..845f8b1fb 100644 --- a/transport/internet/headers/utp/utp_test.go +++ b/transport/internet/headers/utp/utp_test.go @@ -22,5 +22,5 @@ func TestUTPWrite(t *testing.T) { payload.AppendSupplier(utp.Write) payload.Append(content) - assert(payload.Len(), Equals, len(content)+utp.Size()) + assert(payload.Len(), Equals, int32(len(content))+utp.Size()) } diff --git a/transport/internet/headers/wechat/wechat.go b/transport/internet/headers/wechat/wechat.go index 2d0473c96..5baa3e853 100644 --- a/transport/internet/headers/wechat/wechat.go +++ b/transport/internet/headers/wechat/wechat.go @@ -12,7 +12,7 @@ type VideoChat struct { sn int } -func (vc *VideoChat) Size() int { +func (vc *VideoChat) Size() int32 { return 13 } diff --git a/transport/internet/kcp/io.go b/transport/internet/kcp/io.go index 8548b7e2e..27e85bc93 100644 --- a/transport/internet/kcp/io.go +++ b/transport/internet/kcp/io.go @@ -57,7 +57,7 @@ type KCPPacketWriter struct { func (w *KCPPacketWriter) Overhead() int { overhead := 0 if w.Header != nil { - overhead += w.Header.Size() + overhead += int(w.Header.Size()) } if w.Security != nil { overhead += w.Security.Overhead() diff --git a/transport/internet/kcp/segment.go b/transport/internet/kcp/segment.go index 9af1629dd..c689f939a 100644 --- a/transport/internet/kcp/segment.go +++ b/transport/internet/kcp/segment.go @@ -29,7 +29,7 @@ type Segment interface { Release() Conversation() uint16 Command() Command - ByteSize() int + ByteSize() int32 Bytes() buf.Supplier parse(conv uint16, cmd Command, opt SegmentOption, buf []byte) (bool, []byte) } @@ -116,7 +116,7 @@ func (s *DataSegment) Bytes() buf.Supplier { } } -func (s *DataSegment) ByteSize() int { +func (s *DataSegment) ByteSize() int32 { return 2 + 1 + 1 + 4 + 4 + 4 + 2 + s.payload.Len() } @@ -198,8 +198,8 @@ func (s *AckSegment) IsEmpty() bool { return len(s.NumberList) == 0 } -func (s *AckSegment) ByteSize() int { - return 2 + 1 + 1 + 4 + 4 + 4 + 1 + len(s.NumberList)*4 +func (s *AckSegment) ByteSize() int32 { + return 2 + 1 + 1 + 4 + 4 + 4 + 1 + int32(len(s.NumberList)*4) } func (s *AckSegment) Bytes() buf.Supplier { @@ -214,7 +214,7 @@ func (s *AckSegment) Bytes() buf.Supplier { for _, number := range s.NumberList { b = serial.Uint32ToBytes(number, b) } - return s.ByteSize(), nil + return int(s.ByteSize()), nil } } @@ -264,7 +264,7 @@ func (s *CmdOnlySegment) Command() Command { return s.Cmd } -func (*CmdOnlySegment) ByteSize() int { +func (*CmdOnlySegment) ByteSize() int32 { return 2 + 1 + 1 + 4 + 4 + 4 } diff --git a/transport/internet/kcp/segment_test.go b/transport/internet/kcp/segment_test.go index f12d488e1..2f1ba70a8 100644 --- a/transport/internet/kcp/segment_test.go +++ b/transport/internet/kcp/segment_test.go @@ -30,7 +30,7 @@ func TestDataSegment(t *testing.T) { bytes := make([]byte, nBytes) seg.Bytes()(bytes) - assert(len(bytes), Equals, nBytes) + assert(int32(len(bytes)), Equals, nBytes) iseg, _ := ReadSegment(bytes) seg2 := iseg.(*DataSegment) @@ -56,7 +56,7 @@ func Test1ByteDataSegment(t *testing.T) { bytes := make([]byte, nBytes) seg.Bytes()(bytes) - assert(len(bytes), Equals, nBytes) + assert(int32(len(bytes)), Equals, nBytes) iseg, _ := ReadSegment(bytes) seg2 := iseg.(*DataSegment) @@ -82,7 +82,7 @@ func TestACKSegment(t *testing.T) { bytes := make([]byte, nBytes) seg.Bytes()(bytes) - assert(len(bytes), Equals, nBytes) + assert(int32(len(bytes)), Equals, nBytes) iseg, _ := ReadSegment(bytes) seg2 := iseg.(*AckSegment) @@ -112,7 +112,7 @@ func TestCmdSegment(t *testing.T) { bytes := make([]byte, nBytes) seg.Bytes()(bytes) - assert(len(bytes), Equals, nBytes) + assert(int32(len(bytes)), Equals, nBytes) iseg, _ := ReadSegment(bytes) seg2 := iseg.(*CmdOnlySegment) diff --git a/transport/ray/connection.go b/transport/ray/connection.go index 2bc4d39b9..dbf36d5fd 100644 --- a/transport/ray/connection.go +++ b/transport/ray/connection.go @@ -79,7 +79,7 @@ func (c *connection) Write(b []byte) (int, error) { } l := len(b) - mb := buf.NewMultiBufferCap(l/buf.Size + 1) + mb := buf.NewMultiBufferCap(int32(l)/buf.Size + 1) mb.Write(b) return l, c.output.WriteMultiBuffer(mb) } From c5bd23105ef2cb79d271645fc372bc47caaeb5db Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 2 Apr 2018 22:01:55 +0200 Subject: [PATCH 167/183] change uint32 to int32 --- app/proxyman/mux/reader.go | 4 ++-- common/buf/buffer.go | 6 +++--- common/buf/buffer_pool.go | 12 ++++++------ common/buf/multi_buffer.go | 4 ++-- common/buf/reader.go | 2 +- common/crypto/auth.go | 2 +- proxy/shadowsocks/ota.go | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/proxyman/mux/reader.go b/app/proxyman/mux/reader.go index 641f074cb..4c5432972 100644 --- a/app/proxyman/mux/reader.go +++ b/app/proxyman/mux/reader.go @@ -17,7 +17,7 @@ func ReadMetadata(reader io.Reader) (*FrameMetadata, error) { return nil, newError("invalid metalen ", metaLen).AtError() } - b := buf.NewSize(uint32(metaLen)) + b := buf.NewSize(int32(metaLen)) defer b.Release() if err := b.Reset(buf.ReadFullFrom(reader, int32(metaLen))); err != nil { @@ -51,7 +51,7 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) { return nil, err } - b := buf.NewSize(uint32(size)) + b := buf.NewSize(int32(size)) if err := b.Reset(buf.ReadFullFrom(r.reader, int32(size))); err != nil { b.Release() return nil, err diff --git a/common/buf/buffer.go b/common/buf/buffer.go index 340cdf76e..01a4f0ebd 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -179,9 +179,9 @@ func New() *Buffer { } } -// NewSize creates and returns a buffer with 0 length and at least the given capacity. -func NewSize(size uint32) *Buffer { +// NewSize creates and returns a buffer with 0 length and at least the given capacity. Capacity must be positive. +func NewSize(capacity int32) *Buffer { return &Buffer{ - v: newBytes(size), + v: newBytes(capacity), } } diff --git a/common/buf/buffer_pool.go b/common/buf/buffer_pool.go index 59a4fb891..e73881332 100644 --- a/common/buf/buffer_pool.go +++ b/common/buf/buffer_pool.go @@ -9,7 +9,7 @@ const ( Size = 2 * 1024 ) -func createAllocFunc(size uint32) func() interface{} { +func createAllocFunc(size int32) func() interface{} { return func() interface{} { return make([]byte, size) } @@ -26,12 +26,12 @@ const ( var ( pool [numPools]sync.Pool - poolSize [numPools]uint32 - largeSize uint32 + poolSize [numPools]int32 + largeSize int32 ) func init() { - size := uint32(Size) + size := int32(Size) for i := 0; i < numPools; i++ { pool[i] = sync.Pool{ New: createAllocFunc(size), @@ -42,7 +42,7 @@ func init() { } } -func newBytes(size uint32) []byte { +func newBytes(size int32) []byte { for idx, ps := range poolSize { if size <= ps { return pool[idx].Get().([]byte) @@ -52,7 +52,7 @@ func newBytes(size uint32) []byte { } func freeBytes(b []byte) { - size := uint32(cap(b)) + size := int32(cap(b)) b = b[0:cap(b)] for i := numPools - 1; i >= 0; i-- { if size >= poolSize[i] { diff --git a/common/buf/multi_buffer.go b/common/buf/multi_buffer.go index 60b7ce8e9..b4e6d4cf7 100644 --- a/common/buf/multi_buffer.go +++ b/common/buf/multi_buffer.go @@ -39,7 +39,7 @@ func ReadSizeToMultiBuffer(reader io.Reader, size int32) (MultiBuffer, error) { if bSize > Size { bSize = Size } - b := NewSize(uint32(bSize)) + b := NewSize(bSize) if err := b.Reset(ReadFullFrom(reader, bSize)); err != nil { mb.Release() return nil, err @@ -189,7 +189,7 @@ func (mb *MultiBuffer) SliceBySize(size int32) MultiBuffer { } *mb = (*mb)[endIndex:] if endIndex == 0 && len(*mb) > 0 { - b := NewSize(uint32(size)) + b := NewSize(size) common.Must(b.Reset(ReadFullFrom((*mb)[0], size))) return NewMultiBufferValue(b) } diff --git a/common/buf/reader.go b/common/buf/reader.go index df7af923d..d652e86a8 100644 --- a/common/buf/reader.go +++ b/common/buf/reader.go @@ -55,7 +55,7 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) { mb.Write(r.buffer[:nBytes]) if nBytes == len(r.buffer) && nBytes < int(largeSize) { freeBytes(r.buffer) - r.buffer = newBytes(uint32(nBytes) + 1) + r.buffer = newBytes(int32(nBytes) + 1) } else if nBytes < Size { r.freeBuffer() } diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 7ea682d92..8ac9e4789 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -143,7 +143,7 @@ func (r *AuthenticationReader) readInternal(soft bool) (*buf.Buffer, error) { return nil, errSoft } - b := buf.NewSize(uint32(size)) + b := buf.NewSize(size) if err := b.Reset(buf.ReadFullFrom(r.reader, size)); err != nil { b.Release() return nil, err diff --git a/proxy/shadowsocks/ota.go b/proxy/shadowsocks/ota.go index e574bcb0f..500ccec38 100644 --- a/proxy/shadowsocks/ota.go +++ b/proxy/shadowsocks/ota.go @@ -76,7 +76,7 @@ func (v *ChunkReader) ReadMultiBuffer() (buf.MultiBuffer, error) { } size += AuthSize - buffer := buf.NewSize(uint32(size)) + buffer := buf.NewSize(int32(size)) if err := buffer.AppendSupplier(buf.ReadFullFrom(v.reader, int32(size))); err != nil { buffer.Release() return nil, err From 009d58dd6c7d80f1db72861b750f6de890d292a1 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 2 Apr 2018 22:12:51 +0200 Subject: [PATCH 168/183] remove static bytes array --- transport/internet/kcp/io.go | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/transport/internet/kcp/io.go b/transport/internet/kcp/io.go index 27e85bc93..a8e5f67b3 100644 --- a/transport/internet/kcp/io.go +++ b/transport/internet/kcp/io.go @@ -5,6 +5,8 @@ import ( "crypto/rand" "io" + "v2ray.com/core/common" + "v2ray.com/core/common/buf" "v2ray.com/core/transport/internet" ) @@ -50,8 +52,6 @@ type KCPPacketWriter struct { Header internet.PacketHeader Security cipher.AEAD Writer io.Writer - - buffer [2048]byte } func (w *KCPPacketWriter) Overhead() int { @@ -66,27 +66,28 @@ func (w *KCPPacketWriter) Overhead() int { } func (w *KCPPacketWriter) Write(b []byte) (int, error) { - x := w.buffer[:] - size := 0 + bb := buf.NewSize(int32(len(b) + w.Overhead())) + defer bb.Release() + if w.Header != nil { - nBytes, _ := w.Header.Write(x) - size += nBytes - x = x[nBytes:] + common.Must(bb.AppendSupplier(func(x []byte) (int, error) { + return w.Header.Write(x) + })) } if w.Security != nil { nonceSize := w.Security.NonceSize() - var nonce []byte - if nonceSize > 0 { - nonce = x[:nonceSize] - rand.Read(nonce) - x = x[nonceSize:] - } - x = w.Security.Seal(x[:0], nonce, b, nil) - size += nonceSize + len(x) + common.Must(bb.AppendSupplier(func(x []byte) (int, error) { + return rand.Read(x[:nonceSize]) + })) + nonce := bb.BytesFrom(int32(-nonceSize)) + common.Must(bb.AppendSupplier(func(x []byte) (int, error) { + eb := w.Security.Seal(x[:0], nonce, b, nil) + return len(eb), nil + })) } else { - size += copy(x, b) + bb.Append(b) } - _, err := w.Writer.Write(w.buffer[:size]) + _, err := w.Writer.Write(bb.Bytes()) return len(b), err } From 4c2edeb18a54b794f9df53a393c33ef201492de8 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Mon, 2 Apr 2018 22:17:06 +0200 Subject: [PATCH 169/183] fix test break --- transport/internet/header_test.go | 6 +++--- transport/internet/http/http_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/transport/internet/header_test.go b/transport/internet/header_test.go index 65a7afa19..980b62a90 100644 --- a/transport/internet/header_test.go +++ b/transport/internet/header_test.go @@ -15,13 +15,13 @@ func TestAllHeadersLoadable(t *testing.T) { noopAuth, err := CreatePacketHeader((*noop.Config)(nil)) assert(err, IsNil) - assert(noopAuth.Size(), Equals, 0) + assert(noopAuth.Size(), Equals, int32(0)) srtp, err := CreatePacketHeader((*srtp.Config)(nil)) assert(err, IsNil) - assert(srtp.Size(), Equals, 4) + assert(srtp.Size(), Equals, int32(4)) utp, err := CreatePacketHeader((*utp.Config)(nil)) assert(err, IsNil) - assert(utp.Size(), Equals, 4) + assert(utp.Size(), Equals, int32(4)) } diff --git a/transport/internet/http/http_test.go b/transport/internet/http/http_test.go index acbe62128..c0809fffa 100644 --- a/transport/internet/http/http_test.go +++ b/transport/internet/http/http_test.go @@ -41,7 +41,7 @@ func TestHTTPConnection(t *testing.T) { } nBytes, err := conn.Write(b.Bytes()) assert(err, IsNil) - assert(nBytes, Equals, b.Len()) + assert(int32(nBytes), Equals, b.Len()) } }() }) From 6287285c8a2ab75d8fa59170af46422f916056e8 Mon Sep 17 00:00:00 2001 From: wuxiang Date: Tue, 3 Apr 2018 17:03:03 +0800 Subject: [PATCH 170/183] add nil check as other applications --- stats.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stats.go b/stats.go index c44943350..3a8f33cc6 100644 --- a/stats.go +++ b/stats.go @@ -64,8 +64,12 @@ func (s *syncStatManager) GetCounter(name string) StatCounter { } func (s *syncStatManager) Set(m StatManager) { + if m == nil { + return + } s.Lock() defer s.Unlock() + s.StatManager.Close() s.StatManager = m } From 3f19d09878ccb660eb93140729c85422be659293 Mon Sep 17 00:00:00 2001 From: wuxiang Date: Tue, 3 Apr 2018 17:11:54 +0800 Subject: [PATCH 171/183] update some comments --- app/dispatcher/default.go | 4 ++-- app/dns/server.go | 2 +- app/log/log.go | 6 +++--- app/policy/manager.go | 4 ++-- app/proxyman/outbound/handler.go | 2 +- common/signal/task.go | 2 +- network.go | 4 ++-- transport/ray/connection.go | 2 +- v2ray.go | 4 ++-- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 865a643b4..6354ce3f1 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -44,12 +44,12 @@ func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatch return d, nil } -// Start implements app.Application. +// Start implements common.Runnable. func (*DefaultDispatcher) Start() error { return nil } -// Close implements app.Application. +// Close implements common.Closable. func (*DefaultDispatcher) Close() error { return nil } func getStatsName(u *protocol.User) string { diff --git a/app/dns/server.go b/app/dns/server.go index 8f5c3cd54..5fbec9b91 100644 --- a/app/dns/server.go +++ b/app/dns/server.go @@ -80,7 +80,7 @@ func (s *Server) Start() error { return s.task.Start() } -// Close implements common.Runnable. +// Close implements common.Closable. func (s *Server) Close() error { return s.task.Close() } diff --git a/app/log/log.go b/app/log/log.go index d5205d104..5471fb35f 100644 --- a/app/log/log.go +++ b/app/log/log.go @@ -11,7 +11,7 @@ import ( "v2ray.com/core/common/log" ) -// Instance is an app.Application that handles logs. +// Instance is a log.Handler that handles logs. type Instance struct { sync.RWMutex config *Config @@ -91,7 +91,7 @@ func (g *Instance) startInternal() error { return nil } -// Start implements app.Application.Start(). +// Start implements common.Runnable.Start(). func (g *Instance) Start() error { if err := g.startInternal(); err != nil { return err @@ -125,7 +125,7 @@ func (g *Instance) Handle(msg log.Message) { } } -// Close implement app.Application.Close(). +// Close implements common.Closable.Close(). func (g *Instance) Close() error { newError("Logger closing").AtDebug().WriteToLog() diff --git a/app/policy/manager.go b/app/policy/manager.go index 20e785154..68ed4e9d7 100644 --- a/app/policy/manager.go +++ b/app/policy/manager.go @@ -43,12 +43,12 @@ func (m *Instance) ForLevel(level uint32) core.Policy { return core.DefaultPolicy() } -// Start implements app.Application.Start(). +// Start implements common.Runnable.Start(). func (m *Instance) Start() error { return nil } -// Close implements app.Application.Close(). +// Close implements common.Closable.Close(). func (m *Instance) Close() error { return nil } diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index 3bc57ea08..f511b590f 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -133,7 +133,7 @@ func (h *Handler) Start() error { return nil } -// Close implements common.Runnable. +// Close implements common.Closable. func (h *Handler) Close() error { common.Close(h.mux) return nil diff --git a/common/signal/task.go b/common/signal/task.go index a9a886fb9..36e0a1f4e 100644 --- a/common/signal/task.go +++ b/common/signal/task.go @@ -50,7 +50,7 @@ func (t *PeriodicTask) Start() error { return nil } -// Close implements common.Runnable. +// Close implements common.Closable. func (t *PeriodicTask) Close() error { t.access.Lock() defer t.access.Unlock() diff --git a/network.go b/network.go index 3e36624b0..e6a0046cb 100644 --- a/network.go +++ b/network.go @@ -26,7 +26,7 @@ type OutboundHandler interface { Dispatch(ctx context.Context, outboundRay ray.OutboundRay) } -// InboundHandlerManager is a feature that managers InboundHandlers. +// InboundHandlerManager is a feature that manages InboundHandlers. type InboundHandlerManager interface { Feature // GetHandlers returns an InboundHandler for the given tag. @@ -98,7 +98,7 @@ func (m *syncInboundHandlerManager) Set(manager InboundHandlerManager) { // OutboundHandlerManager is a feature that manages OutboundHandlers. type OutboundHandlerManager interface { Feature - // GetHandler returns an OutboundHandler will given tag. + // GetHandler returns an OutboundHandler for the given tag. GetHandler(tag string) OutboundHandler // GetDefaultHandler returns the default OutboundHandler. It is usually the first OutboundHandler specified in the configuration. GetDefaultHandler() OutboundHandler diff --git a/transport/ray/connection.go b/transport/ray/connection.go index 2bc4d39b9..18d98a52d 100644 --- a/transport/ray/connection.go +++ b/transport/ray/connection.go @@ -123,7 +123,7 @@ func (c *connection) SetReadDeadline(t time.Time) error { return nil } -// SetWriteDeadline implement net.Conn.SetWriteDeadline(). +// SetWriteDeadline implements net.Conn.SetWriteDeadline(). func (c *connection) SetWriteDeadline(t time.Time) error { return nil } diff --git a/v2ray.go b/v2ray.go index 28af89a63..33bc831ab 100755 --- a/v2ray.go +++ b/v2ray.go @@ -38,7 +38,7 @@ type Instance struct { // New returns a new V2Ray instance based on given configuration. // The instance is not started at this point. -// To make sure V2Ray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional. +// To ensure V2Ray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional. func New(config *Config) (*Instance, error) { var server = &Instance{ id: uuid.New(), @@ -169,7 +169,7 @@ func (s *Instance) allFeatures() []Feature { } // GetFeature returns a feature that was registered in this Instance. Nil if not found. -// The returned Feature must implement common.HasType and whose type equals the given feature type. +// The returned Feature must implement common.HasType and whose type equals to the given feature type. func (s *Instance) GetFeature(featureType interface{}) Feature { for _, f := range s.features { if hasType, ok := f.(common.HasType); ok { From 7bafd7a1abc23c03df98242a736164eb28dfef31 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 3 Apr 2018 11:32:03 +0200 Subject: [PATCH 172/183] migrate int to int32 --- common/crypto/auth.go | 4 ++-- common/crypto/chunk.go | 12 ++++++------ proxy/vmess/encoding/auth.go | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 8ac9e4789..85a84a196 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -205,7 +205,7 @@ func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) { eb := buf.New() common.Must(eb.Reset(func(bb []byte) (int, error) { w.sizeParser.Encode(uint16(encryptedSize), bb[:0]) - return w.sizeParser.SizeBytes(), nil + return int(w.sizeParser.SizeBytes()), nil })) if err := eb.AppendSupplier(func(bb []byte) (int, error) { _, err := w.auth.Seal(bb[:0], b.Bytes()) @@ -221,7 +221,7 @@ func (w *AuthenticationWriter) seal(b *buf.Buffer) (*buf.Buffer, error) { func (w *AuthenticationWriter) writeStream(mb buf.MultiBuffer) error { defer mb.Release() - payloadSize := buf.Size - w.auth.Overhead() - w.sizeParser.SizeBytes() + payloadSize := buf.Size - int32(w.auth.Overhead()) - w.sizeParser.SizeBytes() mb2Write := buf.NewMultiBufferCap(int32(len(mb) + 10)) for { diff --git a/common/crypto/chunk.go b/common/crypto/chunk.go index 82e8a4ff0..e6ea63d11 100755 --- a/common/crypto/chunk.go +++ b/common/crypto/chunk.go @@ -10,19 +10,19 @@ import ( // ChunkSizeDecoder is a utility class to decode size value from bytes. type ChunkSizeDecoder interface { - SizeBytes() int + SizeBytes() int32 Decode([]byte) (uint16, error) } // ChunkSizeEncoder is a utility class to encode size value into bytes. type ChunkSizeEncoder interface { - SizeBytes() int + SizeBytes() int32 Encode(uint16, []byte) []byte } type PlainChunkSizeParser struct{} -func (PlainChunkSizeParser) SizeBytes() int { +func (PlainChunkSizeParser) SizeBytes() int32 { return 2 } @@ -38,8 +38,8 @@ type AEADChunkSizeParser struct { Auth *AEADAuthenticator } -func (p *AEADChunkSizeParser) SizeBytes() int { - return 2 + p.Auth.Overhead() +func (p *AEADChunkSizeParser) SizeBytes() int32 { + return 2 + int32(p.Auth.Overhead()) } func (p *AEADChunkSizeParser) Encode(size uint16, b []byte) []byte { @@ -125,7 +125,7 @@ func (w *ChunkStreamWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { b := buf.New() common.Must(b.Reset(func(buffer []byte) (int, error) { w.sizeEncoder.Encode(uint16(slice.Len()), buffer[:0]) - return w.sizeEncoder.SizeBytes(), nil + return int(w.sizeEncoder.SizeBytes()), nil })) mb2Write.Append(b) mb2Write.AppendMulti(slice) diff --git a/proxy/vmess/encoding/auth.go b/proxy/vmess/encoding/auth.go index 05477661c..2f23fa673 100644 --- a/proxy/vmess/encoding/auth.go +++ b/proxy/vmess/encoding/auth.go @@ -88,7 +88,7 @@ func NewShakeSizeParser(nonce []byte) *ShakeSizeParser { } } -func (*ShakeSizeParser) SizeBytes() int { +func (*ShakeSizeParser) SizeBytes() int32 { return 2 } From 4bed69a9b956c5e09d0819bf253b51e4713a6c31 Mon Sep 17 00:00:00 2001 From: wuxiang Date: Tue, 3 Apr 2018 17:40:21 +0800 Subject: [PATCH 173/183] add nil check --- stats.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stats.go b/stats.go index 3a8f33cc6..17bab8757 100644 --- a/stats.go +++ b/stats.go @@ -70,6 +70,8 @@ func (s *syncStatManager) Set(m StatManager) { s.Lock() defer s.Unlock() - s.StatManager.Close() + if s.StatManager != nil { + s.StatManager.Close() + } s.StatManager = m } From 074dfbb78ccf649991efa366df87214ba35fcfec Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 3 Apr 2018 17:51:01 +0200 Subject: [PATCH 174/183] add network list to shadowsocks server --- common/net/network.go | 9 ++++ proxy/shadowsocks/config.pb.go | 76 ++++++++++++++++----------- proxy/shadowsocks/config.proto | 6 ++- proxy/shadowsocks/server.go | 9 ++-- testing/scenarios/shadowsocks_test.go | 13 +++-- 5 files changed, 74 insertions(+), 39 deletions(-) diff --git a/common/net/network.go b/common/net/network.go index f232de0ef..33d53bc72 100644 --- a/common/net/network.go +++ b/common/net/network.go @@ -46,6 +46,15 @@ func (n Network) URLPrefix() string { } } +func HasNetwork(list []Network, network Network) bool { + for _, value := range list { + if string(value) == string(network) { + return true + } + } + return false +} + // HasNetwork returns true if the given network is in v NetworkList. func (l NetworkList) HasNetwork(network Network) bool { for _, value := range l.Network { diff --git a/proxy/shadowsocks/config.pb.go b/proxy/shadowsocks/config.pb.go index bd7cd3b64..c345cf222 100644 --- a/proxy/shadowsocks/config.pb.go +++ b/proxy/shadowsocks/config.pb.go @@ -3,6 +3,7 @@ package shadowsocks import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" +import v2ray_core_common_net "v2ray.com/core/common/net" import v2ray_core_common_protocol "v2ray.com/core/common/protocol" import v2ray_core_common_protocol1 "v2ray.com/core/common/protocol" @@ -116,8 +117,11 @@ func (m *Account) GetOta() Account_OneTimeAuth { } type ServerConfig struct { + // UdpEnabled specified whether or not to enable UDP for Shadowsocks. + // Deprecated. Use 'network' field. UdpEnabled bool `protobuf:"varint,1,opt,name=udp_enabled,json=udpEnabled" json:"udp_enabled,omitempty"` User *v2ray_core_common_protocol.User `protobuf:"bytes,2,opt,name=user" json:"user,omitempty"` + Network []v2ray_core_common_net.Network `protobuf:"varint,3,rep,packed,name=network,enum=v2ray.core.common.net.Network" json:"network,omitempty"` } func (m *ServerConfig) Reset() { *m = ServerConfig{} } @@ -139,6 +143,13 @@ func (m *ServerConfig) GetUser() *v2ray_core_common_protocol.User { return nil } +func (m *ServerConfig) GetNetwork() []v2ray_core_common_net.Network { + if m != nil { + return m.Network + } + return nil +} + type ClientConfig struct { Server []*v2ray_core_common_protocol1.ServerEndpoint `protobuf:"bytes,1,rep,name=server" json:"server,omitempty"` } @@ -166,35 +177,38 @@ func init() { func init() { proto.RegisterFile("v2ray.com/core/proxy/shadowsocks/config.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 477 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0x51, 0x8f, 0x93, 0x40, - 0x14, 0x85, 0x97, 0xb6, 0xb6, 0xf5, 0x52, 0x95, 0x9d, 0xc4, 0xa4, 0x69, 0x36, 0xb1, 0xe9, 0x53, - 0xdd, 0xc4, 0xa1, 0x65, 0x5d, 0xe3, 0x2b, 0x45, 0xd6, 0xdd, 0xa8, 0xb4, 0xa1, 0x5d, 0x8d, 0xbe, - 0x10, 0x76, 0x18, 0x2d, 0xb1, 0x30, 0x93, 0x19, 0xd8, 0x95, 0x5f, 0xe3, 0xbb, 0xff, 0xcc, 0x7f, - 0x61, 0x18, 0xda, 0x2e, 0xf1, 0xa1, 0xfb, 0xc6, 0xbd, 0x9c, 0x73, 0x38, 0xf7, 0x03, 0x5e, 0xdd, - 0x5a, 0x22, 0x2c, 0x30, 0x61, 0x89, 0x49, 0x98, 0xa0, 0x26, 0x17, 0xec, 0x57, 0x61, 0xca, 0x75, - 0x18, 0xb1, 0x3b, 0xc9, 0xc8, 0x4f, 0x69, 0x12, 0x96, 0x7e, 0x8f, 0x7f, 0x60, 0x2e, 0x58, 0xc6, - 0xd0, 0xc9, 0x4e, 0x2e, 0x28, 0x56, 0x52, 0x5c, 0x93, 0x0e, 0x5e, 0xfe, 0x17, 0x46, 0x58, 0x92, - 0xb0, 0xd4, 0x54, 0x56, 0xc2, 0x36, 0x66, 0x2e, 0xa9, 0xa8, 0x82, 0x06, 0x93, 0x07, 0xa4, 0x92, - 0x8a, 0x5b, 0x2a, 0x02, 0xc9, 0x29, 0xa9, 0x1c, 0xa3, 0xbf, 0x1a, 0x74, 0x6c, 0x42, 0x58, 0x9e, - 0x66, 0x68, 0x00, 0x5d, 0x1e, 0x4a, 0x79, 0xc7, 0x44, 0xd4, 0xd7, 0x86, 0xda, 0xf8, 0xb1, 0xbf, - 0x9f, 0xd1, 0x15, 0xe8, 0x24, 0xe6, 0x6b, 0x2a, 0x82, 0xac, 0xe0, 0xb4, 0xdf, 0x18, 0x6a, 0xe3, - 0xa7, 0xd6, 0x18, 0x1f, 0x2a, 0x8e, 0x1d, 0x65, 0x58, 0x15, 0x9c, 0xfa, 0x40, 0xf6, 0xcf, 0xc8, - 0x81, 0x26, 0xcb, 0xc2, 0x7e, 0x53, 0x45, 0x4c, 0x0f, 0x47, 0x6c, 0xab, 0xe1, 0x79, 0x4a, 0x57, - 0x71, 0x42, 0xed, 0x3c, 0x5b, 0xfb, 0xa5, 0x7b, 0x64, 0x81, 0x5e, 0xdb, 0xa1, 0x2e, 0xb4, 0xec, - 0x3c, 0x63, 0xc6, 0x11, 0xea, 0x41, 0xf7, 0x5d, 0x2c, 0xc3, 0x9b, 0x0d, 0x8d, 0x0c, 0x0d, 0xe9, - 0xd0, 0x71, 0xd3, 0x6a, 0x68, 0x8c, 0x28, 0xf4, 0x96, 0x0a, 0x80, 0xa3, 0xe0, 0xa3, 0x17, 0xa0, - 0xe7, 0x11, 0x0f, 0x68, 0x25, 0x50, 0x27, 0x77, 0x7d, 0xc8, 0x23, 0xbe, 0xb5, 0xa0, 0xd7, 0xd0, - 0x2a, 0xe1, 0xaa, 0x6b, 0x75, 0x6b, 0x58, 0xaf, 0x5a, 0x91, 0xc5, 0x3b, 0xb2, 0xf8, 0x5a, 0x52, - 0xe1, 0x2b, 0xf5, 0xc8, 0x87, 0x9e, 0xb3, 0x89, 0x69, 0x9a, 0x6d, 0x3f, 0x33, 0x83, 0x76, 0xc5, - 0xbd, 0xaf, 0x0d, 0x9b, 0x63, 0xdd, 0x3a, 0x3d, 0x94, 0x53, 0x15, 0x74, 0xd3, 0x88, 0xb3, 0x38, - 0xcd, 0xfc, 0xad, 0xf3, 0xf4, 0xb7, 0x06, 0x70, 0x8f, 0xb3, 0x3c, 0xeb, 0xda, 0xfb, 0xe0, 0xcd, - 0xbf, 0x78, 0xc6, 0x11, 0x7a, 0x06, 0xba, 0xed, 0x2e, 0x83, 0xa9, 0xf5, 0x36, 0x70, 0x2e, 0x66, - 0x86, 0xb6, 0x5b, 0x58, 0xe7, 0x6f, 0xd4, 0xa2, 0x51, 0x32, 0x71, 0x2e, 0x6d, 0xe7, 0xd2, 0xb6, - 0x26, 0x46, 0x13, 0x1d, 0xc3, 0x93, 0xdd, 0x14, 0x5c, 0xb9, 0xab, 0x0b, 0xa3, 0x55, 0x8f, 0x78, - 0xef, 0x7c, 0x32, 0x1e, 0xd5, 0x23, 0xca, 0x45, 0x1b, 0x3d, 0x87, 0xe3, 0xbd, 0x69, 0x31, 0xff, - 0xf8, 0x75, 0x7a, 0x36, 0x39, 0x37, 0x3a, 0x25, 0x77, 0x6f, 0xee, 0xb9, 0x46, 0x77, 0xb6, 0x80, - 0x21, 0x61, 0xc9, 0xc1, 0xbf, 0xb9, 0xd0, 0xbe, 0xe9, 0xb5, 0xf1, 0x4f, 0xe3, 0xe4, 0xb3, 0xe5, - 0x87, 0x05, 0x76, 0x4a, 0xf5, 0x42, 0xa9, 0x97, 0xf7, 0xaf, 0x6f, 0xda, 0x0a, 0xca, 0xd9, 0xbf, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0xd5, 0x9f, 0x8c, 0x4d, 0x03, 0x00, 0x00, + // 522 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xc1, 0x6e, 0xd3, 0x4e, + 0x10, 0xc6, 0xbb, 0x71, 0xff, 0x4d, 0xfe, 0xe3, 0x50, 0xdc, 0x95, 0x90, 0xac, 0xa8, 0x42, 0x56, + 0x38, 0x10, 0x2a, 0xb1, 0x4e, 0x5c, 0x8a, 0x7a, 0x75, 0x4c, 0x4a, 0x2b, 0xc0, 0x89, 0x9c, 0x14, + 0x04, 0x17, 0xcb, 0x5d, 0x2f, 0xc4, 0x6a, 0xe2, 0xb5, 0xd6, 0x76, 0x43, 0x9e, 0x86, 0x03, 0x37, + 0xde, 0x8c, 0xb7, 0x40, 0x5e, 0x3b, 0xa9, 0x85, 0xaa, 0x70, 0x88, 0x94, 0x99, 0xfd, 0x7d, 0x9f, + 0x66, 0xbe, 0x31, 0xbc, 0xbc, 0xb3, 0x44, 0xb0, 0x26, 0x94, 0x2f, 0x4d, 0xca, 0x05, 0x33, 0x13, + 0xc1, 0xbf, 0xaf, 0xcd, 0x74, 0x1e, 0x84, 0x7c, 0x95, 0x72, 0x7a, 0x9b, 0x9a, 0x94, 0xc7, 0x5f, + 0xa3, 0x6f, 0x24, 0x11, 0x3c, 0xe3, 0xf8, 0x78, 0x83, 0x0b, 0x46, 0x24, 0x4a, 0x6a, 0x68, 0xe7, + 0xf9, 0x5f, 0x66, 0x94, 0x2f, 0x97, 0x3c, 0x36, 0x63, 0x96, 0x15, 0xbf, 0x15, 0x17, 0xb7, 0xa5, + 0x4d, 0xe7, 0xc5, 0xc3, 0xa0, 0x7c, 0xa4, 0x7c, 0x61, 0xe6, 0x29, 0x13, 0x15, 0xda, 0xff, 0x07, + 0x9a, 0x32, 0x71, 0xc7, 0x84, 0x9f, 0x26, 0x8c, 0x96, 0x8a, 0xee, 0x6f, 0x04, 0x4d, 0x9b, 0x52, + 0x9e, 0xc7, 0x19, 0xee, 0x40, 0x2b, 0x09, 0xd2, 0x74, 0xc5, 0x45, 0xa8, 0x23, 0x03, 0xf5, 0xfe, + 0xf7, 0xb6, 0x35, 0xbe, 0x02, 0x95, 0x46, 0xc9, 0x9c, 0x09, 0x3f, 0x5b, 0x27, 0x4c, 0x6f, 0x18, + 0xa8, 0x77, 0x68, 0xf5, 0xc8, 0xae, 0x0d, 0x89, 0x23, 0x05, 0xb3, 0x75, 0xc2, 0x3c, 0xa0, 0xdb, + 0xff, 0xd8, 0x01, 0x85, 0x67, 0x81, 0xae, 0x48, 0x8b, 0xc1, 0x6e, 0x8b, 0x6a, 0x34, 0x32, 0x8e, + 0xd9, 0x2c, 0x5a, 0x32, 0x3b, 0xcf, 0xe6, 0x5e, 0xa1, 0xee, 0x5a, 0xa0, 0xd6, 0x7a, 0xb8, 0x05, + 0xfb, 0x76, 0x9e, 0x71, 0x6d, 0x0f, 0xb7, 0xa1, 0xf5, 0x26, 0x4a, 0x83, 0x9b, 0x05, 0x0b, 0x35, + 0x84, 0x55, 0x68, 0x8e, 0xe2, 0xb2, 0x68, 0x74, 0x7f, 0x22, 0x68, 0x4f, 0x65, 0x02, 0x8e, 0x3c, + 0x13, 0x7e, 0x06, 0x6a, 0x1e, 0x26, 0x3e, 0x2b, 0x09, 0xb9, 0x73, 0x6b, 0xd8, 0xd0, 0x91, 0x07, + 0x79, 0x98, 0x54, 0x3a, 0xfc, 0x0a, 0xf6, 0x8b, 0x84, 0xe5, 0xca, 0xaa, 0x65, 0xd4, 0xe7, 0x2d, + 0xe3, 0x25, 0x9b, 0x78, 0xc9, 0x75, 0xca, 0x84, 0x27, 0x69, 0x7c, 0x0e, 0xcd, 0xea, 0x8a, 0xba, + 0x62, 0x28, 0xbd, 0x43, 0xeb, 0xe9, 0x03, 0xc2, 0x98, 0x65, 0xc4, 0x2d, 0x29, 0x6f, 0x83, 0x77, + 0x3d, 0x68, 0x3b, 0x8b, 0x88, 0xc5, 0x59, 0x35, 0xe4, 0x10, 0x0e, 0xca, 0xb3, 0xe9, 0xc8, 0x50, + 0x7a, 0xaa, 0x75, 0xb2, 0x6b, 0x82, 0x72, 0xbd, 0x51, 0x1c, 0x26, 0x3c, 0x8a, 0x33, 0xaf, 0x52, + 0x9e, 0xfc, 0x40, 0x00, 0xf7, 0xd7, 0x28, 0x52, 0xb9, 0x76, 0xdf, 0xb9, 0xe3, 0x4f, 0xae, 0xb6, + 0x87, 0x1f, 0x83, 0x6a, 0x8f, 0xa6, 0xfe, 0xc0, 0x3a, 0xf7, 0x9d, 0x8b, 0xa1, 0x86, 0x36, 0x0d, + 0xeb, 0xec, 0xb5, 0x6c, 0x34, 0x8a, 0x48, 0x9d, 0x4b, 0xdb, 0xb9, 0xb4, 0xad, 0xbe, 0xa6, 0xe0, + 0x23, 0x78, 0xb4, 0xa9, 0xfc, 0xab, 0xd1, 0xec, 0x42, 0xdb, 0xaf, 0x5b, 0xbc, 0x75, 0x3e, 0x68, + 0xff, 0xd5, 0x2d, 0x8a, 0xc6, 0x01, 0x7e, 0x02, 0x47, 0x5b, 0xd1, 0x64, 0xfc, 0xfe, 0xf3, 0xe0, + 0xb4, 0x7f, 0xa6, 0x35, 0x8b, 0xb3, 0xb9, 0x63, 0x77, 0xa4, 0xb5, 0x86, 0x13, 0x30, 0x28, 0x5f, + 0xee, 0xfc, 0x18, 0x26, 0xe8, 0x8b, 0x5a, 0x2b, 0x7f, 0x35, 0x8e, 0x3f, 0x5a, 0x5e, 0xb0, 0x26, + 0x4e, 0x41, 0x4f, 0x24, 0x3d, 0xbd, 0x7f, 0xbe, 0x39, 0x90, 0xa1, 0x9c, 0xfe, 0x09, 0x00, 0x00, + 0xff, 0xff, 0xdc, 0x7e, 0x6b, 0x61, 0xb5, 0x03, 0x00, 0x00, } diff --git a/proxy/shadowsocks/config.proto b/proxy/shadowsocks/config.proto index a9a9fb5a1..9e77a7efd 100644 --- a/proxy/shadowsocks/config.proto +++ b/proxy/shadowsocks/config.proto @@ -6,6 +6,7 @@ option go_package = "shadowsocks"; option java_package = "com.v2ray.core.proxy.shadowsocks"; option java_multiple_files = true; +import "v2ray.com/core/common/net/network.proto"; import "v2ray.com/core/common/protocol/user.proto"; import "v2ray.com/core/common/protocol/server_spec.proto"; @@ -33,8 +34,11 @@ enum CipherType { } message ServerConfig { - bool udp_enabled = 1; + // UdpEnabled specified whether or not to enable UDP for Shadowsocks. + // Deprecated. Use 'network' field. + bool udp_enabled = 1 [deprecated = true]; v2ray.core.common.protocol.User user = 2; + repeated v2ray.core.common.net.Network network = 3; } message ClientConfig { diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 5b75b9e05..a58d19720 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -17,7 +17,7 @@ import ( ) type Server struct { - config *ServerConfig + config ServerConfig user *protocol.User account *MemoryAccount v *core.Instance @@ -36,7 +36,7 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { account := rawAccount.(*MemoryAccount) s := &Server{ - config: config, + config: *config, user: config.GetUser(), account: account, v: core.MustFromContext(ctx), @@ -47,7 +47,10 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { func (s *Server) Network() net.NetworkList { list := net.NetworkList{ - Network: []net.Network{net.Network_TCP}, + Network: s.config.Network, + } + if len(list.Network) == 0 { + list.Network = append(list.Network, net.Network_TCP) } if s.config.UdpEnabled { list.Network = append(list.Network, net.Network_UDP) diff --git a/testing/scenarios/shadowsocks_test.go b/testing/scenarios/shadowsocks_test.go index 6a803157f..0db921b77 100644 --- a/testing/scenarios/shadowsocks_test.go +++ b/testing/scenarios/shadowsocks_test.go @@ -60,6 +60,7 @@ func TestShadowsocksAES256TCP(t *testing.T) { Account: account, Level: 1, }, + Network: []net.Network{net.Network_TCP}, }), }, }, @@ -174,11 +175,11 @@ func TestShadowsocksAES128UDP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{ - UdpEnabled: true, User: &protocol.User{ Account: account, Level: 1, }, + Network: []net.Network{net.Network_UDP}, }), }, }, @@ -297,6 +298,7 @@ func TestShadowsocksChacha20TCP(t *testing.T) { Account: account, Level: 1, }, + Network: []net.Network{net.Network_TCP}, }), }, }, @@ -414,6 +416,7 @@ func TestShadowsocksAES256GCMTCP(t *testing.T) { Account: account, Level: 1, }, + Network: []net.Network{net.Network_TCP}, }), }, }, @@ -527,11 +530,11 @@ func TestShadowsocksAES128GCMUDP(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{ - UdpEnabled: true, User: &protocol.User{ Account: account, Level: 1, }, + Network: []net.Network{net.Network_UDP}, }), }, }, @@ -645,11 +648,11 @@ func TestShadowsocksAES128GCMUDPMux(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{ - UdpEnabled: false, User: &protocol.User{ Account: account, Level: 1, }, + Network: []net.Network{net.Network_TCP}, }), }, }, @@ -773,6 +776,7 @@ func TestShadowsocksAES256GCMConformance(t *testing.T) { Account: account, Level: 1, }, + Network: []net.Network{net.Network_TCP}, }), }, }, @@ -847,11 +851,11 @@ func TestShadowsocksChacha20Poly1305UDPConformance(t *testing.T) { Listen: net.NewIPOrDomain(net.LocalHostIP), }), ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{ - UdpEnabled: true, User: &protocol.User{ Account: account, Level: 1, }, + Network: []net.Network{net.Network_UDP}, }), }, }, @@ -934,6 +938,7 @@ func TestShadowsocksChacha20Conformance(t *testing.T) { Account: account, Level: 1, }, + Network: []net.Network{net.Network_TCP}, }), }, }, From 27ccc9d726b21a900c3de41b9fcc5f6faf6e9c50 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Tue, 3 Apr 2018 22:34:59 +0200 Subject: [PATCH 175/183] comments --- common/buf/buf.go | 3 ++- common/buf/buffer.go | 1 - common/crypto/crypto.go | 2 +- common/dice/dice.go | 2 +- common/errors/errors.go | 2 +- common/log/log.go | 2 +- common/net/net.go | 2 +- common/platform/platform.go | 2 +- common/predicate/predicate.go | 2 +- common/protocol/protocol.go | 2 +- common/retry/retry.go | 2 +- common/session/session.go | 8 +++++++- common/uuid/uuid.go | 2 +- 13 files changed, 19 insertions(+), 13 deletions(-) diff --git a/common/buf/buf.go b/common/buf/buf.go index 949ec6456..584815f4a 100644 --- a/common/buf/buf.go +++ b/common/buf/buf.go @@ -1,3 +1,4 @@ -package buf +// Package buf provides a light-weight memory allocation mechanism. +package buf // import "v2ray.com/core/common/buf" //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg buf -path Buf diff --git a/common/buf/buffer.go b/common/buf/buffer.go index 01a4f0ebd..e7583fccb 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -1,4 +1,3 @@ -// Package buf provides a light-weight memory allocation mechanism. package buf import ( diff --git a/common/crypto/crypto.go b/common/crypto/crypto.go index 8483b9540..45797a8ad 100644 --- a/common/crypto/crypto.go +++ b/common/crypto/crypto.go @@ -1,4 +1,4 @@ // Package crypto provides common crypto libraries for V2Ray. -package crypto +package crypto // import "v2ray.com/core/common/crypto" //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg crypto -path Crypto diff --git a/common/dice/dice.go b/common/dice/dice.go index 014f4c0de..ac03fe9e5 100644 --- a/common/dice/dice.go +++ b/common/dice/dice.go @@ -1,6 +1,6 @@ // Package dice contains common functions to generate random number. // It also initialize math/rand with the time in seconds at launch time. -package dice +package dice // import "v2ray.com/core/common/dice" import ( "math/rand" diff --git a/common/errors/errors.go b/common/errors/errors.go index a3c2758d9..3e5e09425 100644 --- a/common/errors/errors.go +++ b/common/errors/errors.go @@ -1,5 +1,5 @@ // Package errors is a drop-in replacement for Golang lib 'errors'. -package errors +package errors // import "v2ray.com/core/common/errors" import ( "context" diff --git a/common/log/log.go b/common/log/log.go index e739a1f0c..423901b73 100644 --- a/common/log/log.go +++ b/common/log/log.go @@ -1,4 +1,4 @@ -package log +package log // import "v2ray.com/core/common/log" import ( "sync" diff --git a/common/net/net.go b/common/net/net.go index 9cddfe530..b44beedbf 100644 --- a/common/net/net.go +++ b/common/net/net.go @@ -1,4 +1,4 @@ // Package net is a drop-in replacement to Golang's net package, with some more functionalities. -package net +package net // import "v2ray.com/core/common/net" //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg net -path Net diff --git a/common/platform/platform.go b/common/platform/platform.go index 96fa7103a..3b86ec9dc 100644 --- a/common/platform/platform.go +++ b/common/platform/platform.go @@ -1,4 +1,4 @@ -package platform +package platform // import "v2ray.com/core/common/platform" import ( "os" diff --git a/common/predicate/predicate.go b/common/predicate/predicate.go index 326ceba1f..fbeb703e4 100644 --- a/common/predicate/predicate.go +++ b/common/predicate/predicate.go @@ -1,4 +1,4 @@ -package predicate +package predicate // import "v2ray.com/core/common/predicate" type Predicate func() bool diff --git a/common/protocol/protocol.go b/common/protocol/protocol.go index 6c89418d4..edf4e5ca9 100644 --- a/common/protocol/protocol.go +++ b/common/protocol/protocol.go @@ -1,3 +1,3 @@ -package protocol +package protocol // import "v2ray.com/core/common/protocol" //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg protocol -path Protocol diff --git a/common/retry/retry.go b/common/retry/retry.go index c2f94ce3f..b2c785997 100644 --- a/common/retry/retry.go +++ b/common/retry/retry.go @@ -1,4 +1,4 @@ -package retry +package retry // import "v2ray.com/core/common/retry" //go:generate go run $GOPATH/src/v2ray.com/core/common/errors/errorgen/main.go -pkg retry -path Retry diff --git a/common/session/session.go b/common/session/session.go index af7aa6558..e1be95766 100644 --- a/common/session/session.go +++ b/common/session/session.go @@ -1,12 +1,16 @@ -package session +// Package session provides functions for sessions of incoming requests. +package session // import "v2ray.com/core/common/session" import ( "context" "math/rand" ) +// ID of a session. type ID uint32 +// NewID generates a new ID. The generated ID is high likely to be unique, but not cryptographically secure. +// The generated ID will never be 0. func NewID() ID { for { id := ID(rand.Uint32()) @@ -22,10 +26,12 @@ const ( idSessionKey sessionKey = iota ) +// ContextWithID returns a new context with the given ID. func ContextWithID(ctx context.Context, id ID) context.Context { return context.WithValue(ctx, idSessionKey, id) } +// IDFromContext returns ID in this context, or 0 if not contained. func IDFromContext(ctx context.Context) ID { if id, ok := ctx.Value(idSessionKey).(ID); ok { return id diff --git a/common/uuid/uuid.go b/common/uuid/uuid.go index 06b219a0f..fa90f9159 100755 --- a/common/uuid/uuid.go +++ b/common/uuid/uuid.go @@ -1,4 +1,4 @@ -package uuid +package uuid // import "v2ray.com/core/common/uuid" import ( "bytes" From 9f198d7e3d40337cea6b814d090a71f65f119c2a Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 4 Apr 2018 00:29:30 +0200 Subject: [PATCH 176/183] fix lint errors --- app/commander/commander.go | 3 +++ app/dispatcher/default.go | 4 ---- app/dns/nameserver.go | 3 ++- app/stats/stats.go | 5 +++++ common/crypto/auth.go | 2 +- common/protocol/address.go | 2 +- common/uuid/uuid.go | 6 +++--- proxy/dokodemo/dokodemo.go | 1 - proxy/shadowsocks/protocol.go | 6 +++--- transport/config.go | 5 +---- 10 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/commander/commander.go b/app/commander/commander.go index 19991fce4..072d38cd9 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -34,10 +34,12 @@ func NewCommander(ctx context.Context, config *Config) (*Commander, error) { return c, nil } +// Type implements common.HasType. func (c *Commander) Type() interface{} { return (*Commander)(nil) } +// Start implements common.Runnable. func (c *Commander) Start() error { c.Lock() c.server = grpc.NewServer() @@ -77,6 +79,7 @@ func (c *Commander) Start() error { return nil } +// Close implements common.Closable. func (c *Commander) Close() error { c.Lock() defer c.Unlock() diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 6354ce3f1..3a9fb82d2 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -52,10 +52,6 @@ func (*DefaultDispatcher) Start() error { // Close implements common.Closable. func (*DefaultDispatcher) Close() error { return nil } -func getStatsName(u *protocol.User) string { - return "user>traffic>" + u.Email -} - func (d *DefaultDispatcher) getStatCounter(name string) core.StatCounter { c := d.stats.GetCounter(name) if c != nil { diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index 88e9c26ab..1d7667124 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -7,6 +7,7 @@ import ( "github.com/miekg/dns" "v2ray.com/core" + "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/dice" "v2ray.com/core/common/net" @@ -54,7 +55,7 @@ func NewUDPNameServer(address net.Destination, dispatcher core.Dispatcher) *UDPN Interval: time.Minute, Execute: s.Cleanup, } - s.cleanup.Start() + common.Must(s.cleanup.Start()) return s } diff --git a/app/stats/stats.go b/app/stats/stats.go index 27fffc1bd..03ee2982c 100644 --- a/app/stats/stats.go +++ b/app/stats/stats.go @@ -10,22 +10,27 @@ import ( "v2ray.com/core" ) +// Counter is an implementation of core.StatCounter. type Counter struct { value int64 } +// Value implements core.StatCounter. func (c *Counter) Value() int64 { return atomic.LoadInt64(&c.value) } +// Set implements core.StatCounter. func (c *Counter) Set(newValue int64) int64 { return atomic.SwapInt64(&c.value, newValue) } +// Add implements core.StatCounter. func (c *Counter) Add(delta int64) int64 { return atomic.AddInt64(&c.value, delta) } +// Manager is an implementation of core.StatManager. type Manager struct { access sync.RWMutex counters map[string]*Counter diff --git a/common/crypto/auth.go b/common/crypto/auth.go index 85a84a196..2cf0eb15e 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -124,7 +124,7 @@ func (r *AuthenticationReader) readSize() (int32, error) { var errSoft = newError("waiting for more data") func (r *AuthenticationReader) readInternal(soft bool) (*buf.Buffer, error) { - if soft && r.reader.BufferedBytes() < int32(r.sizeParser.SizeBytes()) { + if soft && r.reader.BufferedBytes() < r.sizeParser.SizeBytes() { return nil, errSoft } diff --git a/common/protocol/address.go b/common/protocol/address.go index 48daf7614..83437e0ca 100644 --- a/common/protocol/address.go +++ b/common/protocol/address.go @@ -99,7 +99,7 @@ func (p *AddressParser) readAddress(b *buf.Buffer, reader io.Reader) (net.Addres return nil, err } domainLength := int32(b.Byte(b.Len() - 1)) - if err := b.AppendSupplier(buf.ReadFullFrom(reader, int32(domainLength))); err != nil { + if err := b.AppendSupplier(buf.ReadFullFrom(reader, domainLength)); err != nil { return nil, err } domain := string(b.BytesFrom(-domainLength)) diff --git a/common/uuid/uuid.go b/common/uuid/uuid.go index fa90f9159..1f3a8342f 100755 --- a/common/uuid/uuid.go +++ b/common/uuid/uuid.go @@ -49,15 +49,15 @@ func (u *UUID) Equals(another *UUID) bool { // Next generates a deterministic random UUID based on this UUID. func (u *UUID) Next() UUID { md5hash := md5.New() - md5hash.Write(u.Bytes()) - md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81")) + common.Must2(md5hash.Write(u.Bytes())) + common.Must2(md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81"))) var newid UUID for { md5hash.Sum(newid[:0]) if !newid.Equals(u) { return newid } - md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2")) + common.Must2(md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2"))) } } diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 1445f864f..7626911dc 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -21,7 +21,6 @@ type DokodemoDoor struct { config *Config address net.Address port net.Port - v *core.Instance } func New(ctx context.Context, config *Config) (*DokodemoDoor, error) { diff --git a/proxy/shadowsocks/protocol.go b/proxy/shadowsocks/protocol.go index d6cfd31cb..9a86c958f 100644 --- a/proxy/shadowsocks/protocol.go +++ b/proxy/shadowsocks/protocol.go @@ -41,7 +41,7 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea ivLen := account.Cipher.IVSize() var iv []byte if ivLen > 0 { - if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, int32(ivLen))); err != nil { + if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, ivLen)); err != nil { return nil, nil, newError("failed to read IV").Base(err) } @@ -227,7 +227,7 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff buffer := buf.New() ivLen := account.Cipher.IVSize() if ivLen > 0 { - common.Must(buffer.Reset(buf.ReadFullFrom(rand.Reader, int32(ivLen)))) + common.Must(buffer.Reset(buf.ReadFullFrom(rand.Reader, ivLen))) } iv := buffer.Bytes() @@ -293,7 +293,7 @@ func DecodeUDPPacket(user *protocol.User, payload *buf.Buffer) (*protocol.Reques authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv)) actualAuth := make([]byte, AuthSize) - authenticator.Authenticate(payload.BytesTo(payloadLen))(actualAuth) + common.Must2(authenticator.Authenticate(payload.BytesTo(payloadLen))(actualAuth)) if !bytes.Equal(actualAuth, authBytes) { return nil, nil, newError("invalid OTA") } diff --git a/transport/config.go b/transport/config.go index de51c0e37..486ea45a6 100644 --- a/transport/config.go +++ b/transport/config.go @@ -9,8 +9,5 @@ func (c *Config) Apply() error { if c == nil { return nil } - if err := internet.ApplyGlobalTransportSettings(c.TransportSettings); err != nil { - return err - } - return nil + return internet.ApplyGlobalTransportSettings(c.TransportSettings) } From a3f47f4fa284dec2eee22814cc7c02e6deb91601 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 4 Apr 2018 00:57:44 +0200 Subject: [PATCH 177/183] comments --- app/commander/commander.go | 2 ++ app/commander/outbound.go | 5 +++++ app/commander/service.go | 2 ++ app/policy/config.go | 1 + app/proxyman/inbound/inbound.go | 6 +++++- policy.go | 5 ++++- v2ray.go | 1 + 7 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/commander/commander.go b/app/commander/commander.go index 072d38cd9..4a8c80ba7 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -13,6 +13,7 @@ import ( "v2ray.com/core/common/signal" ) +// Commander is a V2Ray feature that provides gRPC methods to external clients. type Commander struct { sync.Mutex server *grpc.Server @@ -21,6 +22,7 @@ type Commander struct { ohm core.OutboundHandlerManager } +// NewCommander creates a new Commander based on the given config. func NewCommander(ctx context.Context, config *Config) (*Commander, error) { v := core.MustFromContext(ctx) c := &Commander{ diff --git a/app/commander/outbound.go b/app/commander/outbound.go index f82ae6f28..c55db7478 100644 --- a/app/commander/outbound.go +++ b/app/commander/outbound.go @@ -54,6 +54,7 @@ func (l *OutboundListener) Addr() net.Addr { } } +// CommanderOutbound is a core.OutboundHandler that handles gRPC connections. type CommanderOutbound struct { tag string listener *OutboundListener @@ -61,6 +62,7 @@ type CommanderOutbound struct { closed bool } +// Dispatch implements core.OutboundHandler. func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) { co.access.RLock() @@ -78,10 +80,12 @@ func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) { <-closeSignal.Wait() } +// Tag implements core.OutboundHandler. func (co *CommanderOutbound) Tag() string { return co.tag } +// Start implements common.Runnable. func (co *CommanderOutbound) Start() error { co.access.Lock() co.closed = false @@ -89,6 +93,7 @@ func (co *CommanderOutbound) Start() error { return nil } +// Close implements common.Closable. func (co *CommanderOutbound) Close() error { co.access.Lock() co.closed = true diff --git a/app/commander/service.go b/app/commander/service.go index fb9340db4..4e227c342 100644 --- a/app/commander/service.go +++ b/app/commander/service.go @@ -4,6 +4,8 @@ import ( "google.golang.org/grpc" ) +// Service is a Commander service. type Service interface { + // Register registers the service itself to a gRPC server. Register(*grpc.Server) } diff --git a/app/policy/config.go b/app/policy/config.go index 49cb55303..db5aaa9fa 100644 --- a/app/policy/config.go +++ b/app/policy/config.go @@ -52,6 +52,7 @@ func (p *Policy) overrideWith(another *Policy) { } } +// ToCorePolicy converts this Policy to core.Policy. func (p *Policy) ToCorePolicy() core.Policy { var cp core.Policy if p.Timeout != nil { diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index c752216f9..39d339608 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -50,7 +50,7 @@ func (m *Manager) AddHandler(ctx context.Context, handler core.InboundHandler) e return nil } -// GetHandler returns core.InboundHandlerManager. +// GetHandler implements core.InboundHandlerManager. func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandler, error) { m.access.RLock() defer m.access.RUnlock() @@ -62,6 +62,7 @@ func (m *Manager) GetHandler(ctx context.Context, tag string) (core.InboundHandl return handler, nil } +// RemoveHandler implements core.InboundHandlerManager. func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { if len(tag) == 0 { return core.ErrNoClue @@ -79,6 +80,7 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { return core.ErrNoClue } +// Start implements common.Runnable. func (m *Manager) Start() error { m.access.Lock() defer m.access.Unlock() @@ -99,6 +101,7 @@ func (m *Manager) Start() error { return nil } +// Close implements common.Closable. func (m *Manager) Close() error { m.access.Lock() defer m.access.Unlock() @@ -115,6 +118,7 @@ func (m *Manager) Close() error { return nil } +// NewHandler creates a new core.InboundHandler based on the given config. func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (core.InboundHandler, error) { rawReceiverSettings, err := config.ReceiverSettings.GetInstance() if err != nil { diff --git a/policy.go b/policy.go index d9e0682b9..f8dab11cd 100644 --- a/policy.go +++ b/policy.go @@ -19,8 +19,11 @@ type TimeoutPolicy struct { DownlinkOnly time.Duration } +// StatsPolicy contains settings for stats counters. type StatsPolicy struct { - UserUplink bool + // Whether or not to enable stat counter for user uplink traffic. + UserUplink bool + // Whether or not to enable stat counter for user downlink traffic. UserDownlink bool } diff --git a/v2ray.go b/v2ray.go index 33bc831ab..72c32628c 100755 --- a/v2ray.go +++ b/v2ray.go @@ -211,6 +211,7 @@ func (s *Instance) OutboundHandlerManager() OutboundHandlerManager { return &(s.ohm) } +// Stats returns the StatManager used by this Instance. If StatManager was not registered before, the returned value doesn't work. func (s *Instance) Stats() StatManager { return &(s.stats) } From 90c6113dfc5c7b5f91f07002f1a5aaa0801559b6 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 4 Apr 2018 17:20:45 +0200 Subject: [PATCH 178/183] handle transport errors in mux session --- app/proxyman/mux/frame.go | 1 + app/proxyman/mux/mux.go | 36 ++++++++++++++++++++++++++++++++++-- app/proxyman/mux/writer.go | 13 +++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/app/proxyman/mux/frame.go b/app/proxyman/mux/frame.go index 3c56d6044..ac32d3cad 100644 --- a/app/proxyman/mux/frame.go +++ b/app/proxyman/mux/frame.go @@ -15,6 +15,7 @@ const ( SessionStatusKeep SessionStatus = 0x02 SessionStatusEnd SessionStatus = 0x03 SessionStatusKeepAlive SessionStatus = 0x04 + SessionStatusError SessionStatus = 0x05 ) const ( diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index a94c416de..93fb1b1c2 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -146,12 +146,14 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) { } s.transferType = transferType writer := NewWriter(s.ID, dest, output, transferType) - defer writer.Close() defer s.Close() newError("dispatching request to ", dest).WithContext(ctx).WriteToLog() if err := buf.Copy(s.input, writer); err != nil { newError("failed to fetch all input").Base(err).WithContext(ctx).WriteToLog() + writer.Error() + } else { + writer.Close() } } @@ -214,6 +216,18 @@ func (m *Client) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader return nil } +func (m *Client) handleStatusError(meta *FrameMetadata, reader *buf.BufferedReader) error { + if s, found := m.sessionManager.Get(meta.SessionID); found { + s.output.CloseError() + s.input.CloseError() + s.Close() + } + if meta.Option.Has(OptionData) { + return drain(reader) + } + return nil +} + func (m *Client) fetchOutput() { defer m.done.Close() @@ -237,6 +251,8 @@ func (m *Client) fetchOutput() { err = m.handleStatusNew(meta, reader) case SessionStatusKeep: err = m.handleStatusKeep(meta, reader) + case SessionStatusError: + err = m.handleStatusError(meta, reader) default: newError("unknown status: ", meta.SessionStatus).AtError().WriteToLog() return @@ -294,8 +310,10 @@ func handle(ctx context.Context, s *Session, output buf.Writer) { writer := NewResponseWriter(s.ID, output, s.transferType) if err := buf.Copy(s.input, writer); err != nil { newError("session ", s.ID, " ends.").Base(err).WithContext(ctx).WriteToLog() + writer.Error() + } else { + writer.Close() } - writer.Close() s.Close() } @@ -353,6 +371,18 @@ func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.Buffered return nil } +func (w *ServerWorker) handleStatusError(meta *FrameMetadata, reader *buf.BufferedReader) error { + if s, found := w.sessionManager.Get(meta.SessionID); found { + s.input.CloseError() + s.output.CloseError() + s.Close() + } + if meta.Option.Has(OptionData) { + return drain(reader) + } + return nil +} + func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedReader) error { meta, err := ReadMetadata(reader) if err != nil { @@ -368,6 +398,8 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead err = w.handleStatusNew(ctx, meta, reader) case SessionStatusKeep: err = w.handleStatusKeep(meta, reader) + case SessionStatusError: + err = w.handleStatusError(meta, reader) default: return newError("unknown status: ", meta.SessionStatus).AtError() } diff --git a/app/proxyman/mux/writer.go b/app/proxyman/mux/writer.go index b043909f3..be2e2d90c 100644 --- a/app/proxyman/mux/writer.go +++ b/app/proxyman/mux/writer.go @@ -112,3 +112,16 @@ func (w *Writer) Close() error { w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(frame)) return nil } + +func (w *Writer) Error() error { + meta := FrameMetadata{ + SessionID: w.id, + SessionStatus: SessionStatusError, + } + + frame := buf.New() + common.Must(meta.WriteTo(frame)) + + w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(frame)) + return nil +} From 75a7e9c7f5210a9c6e828e2b66b28b4d654f07f7 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 4 Apr 2018 21:32:40 +0200 Subject: [PATCH 179/183] comments --- app/commander/commander.go | 2 +- app/commander/outbound.go | 25 ++++++++++++++----------- app/proxyman/inbound/inbound.go | 4 +++- app/router/router.go | 5 +++++ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/app/commander/commander.go b/app/commander/commander.go index 4a8c80ba7..fef554953 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -74,7 +74,7 @@ func (c *Commander) Start() error { }() c.ohm.RemoveHandler(context.Background(), c.config.Tag) - c.ohm.AddHandler(context.Background(), &CommanderOutbound{ + c.ohm.AddHandler(context.Background(), &Outbound{ tag: c.config.Tag, listener: listener, }) diff --git a/app/commander/outbound.go b/app/commander/outbound.go index c55db7478..25e476db1 100644 --- a/app/commander/outbound.go +++ b/app/commander/outbound.go @@ -5,6 +5,7 @@ import ( "net" "sync" + "v2ray.com/core/common" "v2ray.com/core/common/signal" "v2ray.com/core/transport/ray" ) @@ -24,6 +25,7 @@ func (l *OutboundListener) add(conn net.Conn) { } } +// Accept implements net.Listener. func (l *OutboundListener) Accept() (net.Conn, error) { select { case <-l.done.C(): @@ -33,8 +35,9 @@ func (l *OutboundListener) Accept() (net.Conn, error) { } } +// Close implement net.Listener. func (l *OutboundListener) Close() error { - l.done.Close() + common.Must(l.done.Close()) L: for { select { @@ -47,6 +50,7 @@ L: return nil } +// Addr implements net.Listener. func (l *OutboundListener) Addr() net.Addr { return &net.TCPAddr{ IP: net.IP{0, 0, 0, 0}, @@ -54,8 +58,8 @@ func (l *OutboundListener) Addr() net.Addr { } } -// CommanderOutbound is a core.OutboundHandler that handles gRPC connections. -type CommanderOutbound struct { +// Outbound is a core.OutboundHandler that handles gRPC connections. +type Outbound struct { tag string listener *OutboundListener access sync.RWMutex @@ -63,7 +67,7 @@ type CommanderOutbound struct { } // Dispatch implements core.OutboundHandler. -func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) { +func (co *Outbound) Dispatch(ctx context.Context, r ray.OutboundRay) { co.access.RLock() if co.closed { @@ -81,12 +85,12 @@ func (co *CommanderOutbound) Dispatch(ctx context.Context, r ray.OutboundRay) { } // Tag implements core.OutboundHandler. -func (co *CommanderOutbound) Tag() string { +func (co *Outbound) Tag() string { return co.tag } // Start implements common.Runnable. -func (co *CommanderOutbound) Start() error { +func (co *Outbound) Start() error { co.access.Lock() co.closed = false co.access.Unlock() @@ -94,11 +98,10 @@ func (co *CommanderOutbound) Start() error { } // Close implements common.Closable. -func (co *CommanderOutbound) Close() error { +func (co *Outbound) Close() error { co.access.Lock() - co.closed = true - co.listener.Close() - co.access.Unlock() + defer co.access.Unlock() - return nil + co.closed = true + return co.listener.Close() } diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index 39d339608..084d25b5b 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -72,7 +72,9 @@ func (m *Manager) RemoveHandler(ctx context.Context, tag string) error { defer m.access.Unlock() if handler, found := m.taggedHandlers[tag]; found { - handler.Close() + if err := handler.Close(); err != nil { + newError("failed to close handler ", tag).Base(err).AtWarning().WithContext(ctx).WriteToLog() + } delete(m.taggedHandlers, tag) return nil } diff --git a/app/router/router.go b/app/router/router.go index 4267064c5..fb30b419c 100644 --- a/app/router/router.go +++ b/app/router/router.go @@ -11,12 +11,14 @@ import ( "v2ray.com/core/proxy" ) +// Router is an implementation of core.Router. type Router struct { domainStrategy Config_DomainStrategy rules []Rule dns core.DNSClient } +// NewRouter creates a new Router based on the given config. func NewRouter(ctx context.Context, config *Config) (*Router, error) { v := core.MustFromContext(ctx) r := &Router{ @@ -68,6 +70,7 @@ func (r *ipResolver) Resolve() []net.Address { return r.ip } +// PickRoute implements core.Router. func (r *Router) PickRoute(ctx context.Context) (string, error) { resolver := &ipResolver{ dns: r.dns, @@ -106,10 +109,12 @@ func (r *Router) PickRoute(ctx context.Context) (string, error) { return "", core.ErrNoClue } +// Start implements common.Runnable. func (*Router) Start() error { return nil } +// Close implements common.Closable. func (*Router) Close() error { return nil } From 076f385e4bdc0a8083a49b2681f39a26d58d95fb Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 4 Apr 2018 21:32:54 +0200 Subject: [PATCH 180/183] stop recording mux access log --- proxy/vmess/inbound/inbound.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index df9988536..dafe2bb43 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -252,12 +252,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection i return newError("client is using insecure encryption: ", request.Security) } - log.Record(&log.AccessMessage{ - From: connection.RemoteAddr(), - To: request.Destination(), - Status: log.AccessAccepted, - Reason: "", - }) + if request.Command != protocol.RequestCommandMux { + log.Record(&log.AccessMessage{ + From: connection.RemoteAddr(), + To: request.Destination(), + Status: log.AccessAccepted, + Reason: "", + }) + } newError("received request for ", request.Destination()).WithContext(ctx).WriteToLog() From 79c1087311cd27da5e9b355bc1800d8458fb4dd9 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 4 Apr 2018 21:33:33 +0200 Subject: [PATCH 181/183] change status to option --- app/proxyman/mux/frame.go | 4 +-- app/proxyman/mux/mux.go | 63 +++++++++++++++++--------------------- app/proxyman/mux/writer.go | 16 +++------- 3 files changed, 34 insertions(+), 49 deletions(-) diff --git a/app/proxyman/mux/frame.go b/app/proxyman/mux/frame.go index ac32d3cad..958aa740b 100644 --- a/app/proxyman/mux/frame.go +++ b/app/proxyman/mux/frame.go @@ -15,11 +15,11 @@ const ( SessionStatusKeep SessionStatus = 0x02 SessionStatusEnd SessionStatus = 0x03 SessionStatusKeepAlive SessionStatus = 0x04 - SessionStatusError SessionStatus = 0x05 ) const ( - OptionData bitmask.Byte = 0x01 + OptionData bitmask.Byte = 0x01 + OptionError bitmask.Byte = 0x02 ) type TargetNetwork byte diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index 93fb1b1c2..802eadb5b 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -10,8 +10,10 @@ import ( "v2ray.com/core" "v2ray.com/core/app/proxyman" + "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/errors" + "v2ray.com/core/common/log" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/signal" @@ -131,7 +133,7 @@ func (m *Client) monitor() { case <-timer.C: size := m.sessionManager.Size() if size == 0 && m.sessionManager.CloseIfNoSession() { - m.done.Close() + common.Must(m.done.Close()) return } } @@ -151,10 +153,10 @@ func fetchInput(ctx context.Context, s *Session, output buf.Writer) { newError("dispatching request to ", dest).WithContext(ctx).WriteToLog() if err := buf.Copy(s.input, writer); err != nil { newError("failed to fetch all input").Base(err).WithContext(ctx).WriteToLog() - writer.Error() - } else { - writer.Close() + writer.hasError = true } + + writer.Close() } func (m *Client) Dispatch(ctx context.Context, outboundRay ray.OutboundRay) bool { @@ -208,18 +210,10 @@ func (m *Client) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReade func (m *Client) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error { if s, found := m.sessionManager.Get(meta.SessionID); found { - s.Close() - } - if meta.Option.Has(OptionData) { - return drain(reader) - } - return nil -} - -func (m *Client) handleStatusError(meta *FrameMetadata, reader *buf.BufferedReader) error { - if s, found := m.sessionManager.Get(meta.SessionID); found { - s.output.CloseError() - s.input.CloseError() + if meta.Option.Has(OptionError) { + s.input.CloseError() + s.output.CloseError() + } s.Close() } if meta.Option.Has(OptionData) { @@ -251,8 +245,6 @@ func (m *Client) fetchOutput() { err = m.handleStatusNew(meta, reader) case SessionStatusKeep: err = m.handleStatusKeep(meta, reader) - case SessionStatusError: - err = m.handleStatusError(meta, reader) default: newError("unknown status: ", meta.SessionStatus).AtError().WriteToLog() return @@ -310,10 +302,10 @@ func handle(ctx context.Context, s *Session, output buf.Writer) { writer := NewResponseWriter(s.ID, output, s.transferType) if err := buf.Copy(s.input, writer); err != nil { newError("session ", s.ID, " ends.").Base(err).WithContext(ctx).WriteToLog() - writer.Error() - } else { - writer.Close() + writer.hasError = true } + + writer.Close() s.Close() } @@ -326,6 +318,17 @@ func (w *ServerWorker) handleStatusKeepAlive(meta *FrameMetadata, reader *buf.Bu func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata, reader *buf.BufferedReader) error { newError("received request for ", meta.Target).WithContext(ctx).WriteToLog() + { + msg := &log.AccessMessage{ + To: meta.Target, + Status: log.AccessAccepted, + Reason: "", + } + if src, f := proxy.SourceFromContext(ctx); f { + msg.From = src + } + log.Record(msg) + } inboundRay, err := w.dispatcher.Dispatch(ctx, meta.Target) if err != nil { if meta.Option.Has(OptionData) { @@ -363,18 +366,10 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere func (w *ServerWorker) handleStatusEnd(meta *FrameMetadata, reader *buf.BufferedReader) error { if s, found := w.sessionManager.Get(meta.SessionID); found { - s.Close() - } - if meta.Option.Has(OptionData) { - return drain(reader) - } - return nil -} - -func (w *ServerWorker) handleStatusError(meta *FrameMetadata, reader *buf.BufferedReader) error { - if s, found := w.sessionManager.Get(meta.SessionID); found { - s.input.CloseError() - s.output.CloseError() + if meta.Option.Has(OptionError) { + s.input.CloseError() + s.output.CloseError() + } s.Close() } if meta.Option.Has(OptionData) { @@ -398,8 +393,6 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead err = w.handleStatusNew(ctx, meta, reader) case SessionStatusKeep: err = w.handleStatusKeep(meta, reader) - case SessionStatusError: - err = w.handleStatusError(meta, reader) default: return newError("unknown status: ", meta.SessionStatus).AtError() } diff --git a/app/proxyman/mux/writer.go b/app/proxyman/mux/writer.go index be2e2d90c..ed0f938ff 100644 --- a/app/proxyman/mux/writer.go +++ b/app/proxyman/mux/writer.go @@ -13,6 +13,7 @@ type Writer struct { writer buf.Writer id uint16 followup bool + hasError bool transferType protocol.TransferType } @@ -40,6 +41,7 @@ func (w *Writer) getNextFrameMeta() FrameMetadata { SessionID: w.id, Target: w.dest, } + if w.followup { meta.SessionStatus = SessionStatusKeep } else { @@ -105,18 +107,8 @@ func (w *Writer) Close() error { SessionID: w.id, SessionStatus: SessionStatusEnd, } - - frame := buf.New() - common.Must(meta.WriteTo(frame)) - - w.writer.WriteMultiBuffer(buf.NewMultiBufferValue(frame)) - return nil -} - -func (w *Writer) Error() error { - meta := FrameMetadata{ - SessionID: w.id, - SessionStatus: SessionStatusError, + if w.hasError { + meta.Option.Set(OptionError) } frame := buf.New() From 61a1cce97265940bf24b9ecce4d9924e052868e3 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Wed, 4 Apr 2018 21:46:36 +0200 Subject: [PATCH 182/183] reduce size of TransferType --- common/protocol/payload.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/protocol/payload.go b/common/protocol/payload.go index 03eced677..2f3c72904 100644 --- a/common/protocol/payload.go +++ b/common/protocol/payload.go @@ -1,6 +1,6 @@ package protocol -type TransferType int +type TransferType byte const ( TransferTypeStream TransferType = 0 From 2b675335bd0e3845df43d0037d90bddafd588d60 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Thu, 5 Apr 2018 12:19:57 +0200 Subject: [PATCH 183/183] properly handle error case --- app/proxyman/mux/mux.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index 802eadb5b..1a35929cb 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -203,7 +203,11 @@ func (m *Client) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReade } if s, found := m.sessionManager.Get(meta.SessionID); found { - return buf.Copy(s.NewReader(reader), s.output, buf.IgnoreWriterError()) + if err := buf.Copy(s.NewReader(reader), s.output); err != nil { + drain(reader) + s.input.CloseError() + return s.Close() + } } return drain(reader) } @@ -359,7 +363,11 @@ func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.Buffere return nil } if s, found := w.sessionManager.Get(meta.SessionID); found { - return buf.Copy(s.NewReader(reader), s.output, buf.IgnoreWriterError()) + if err := buf.Copy(s.NewReader(reader), s.output); err != nil { + drain(reader) + s.input.CloseError() + return s.Close() + } } return drain(reader) }