1package core_test
2
3import (
4	"context"
5	"crypto/rand"
6	"io"
7	"testing"
8	"time"
9
10	"github.com/google/go-cmp/cmp"
11	"google.golang.org/protobuf/proto"
12
13	core "github.com/v2fly/v2ray-core/v4"
14	"github.com/v2fly/v2ray-core/v4/app/dispatcher"
15	"github.com/v2fly/v2ray-core/v4/app/proxyman"
16	"github.com/v2fly/v2ray-core/v4/common"
17	"github.com/v2fly/v2ray-core/v4/common/net"
18	"github.com/v2fly/v2ray-core/v4/common/serial"
19	"github.com/v2fly/v2ray-core/v4/proxy/freedom"
20	"github.com/v2fly/v2ray-core/v4/testing/servers/tcp"
21	"github.com/v2fly/v2ray-core/v4/testing/servers/udp"
22)
23
24func xor(b []byte) []byte {
25	r := make([]byte, len(b))
26	for i, v := range b {
27		r[i] = v ^ 'c'
28	}
29	return r
30}
31
32func xor2(b []byte) []byte {
33	r := make([]byte, len(b))
34	for i, v := range b {
35		r[i] = v ^ 'd'
36	}
37	return r
38}
39
40func TestV2RayDial(t *testing.T) {
41	tcpServer := tcp.Server{
42		MsgProcessor: xor,
43	}
44	dest, err := tcpServer.Start()
45	common.Must(err)
46	defer tcpServer.Close()
47
48	config := &core.Config{
49		App: []*serial.TypedMessage{
50			serial.ToTypedMessage(&dispatcher.Config{}),
51			serial.ToTypedMessage(&proxyman.InboundConfig{}),
52			serial.ToTypedMessage(&proxyman.OutboundConfig{}),
53		},
54		Outbound: []*core.OutboundHandlerConfig{
55			{
56				ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
57			},
58		},
59	}
60
61	cfgBytes, err := proto.Marshal(config)
62	common.Must(err)
63
64	server, err := core.StartInstance("protobuf", cfgBytes)
65	common.Must(err)
66	defer server.Close()
67
68	conn, err := core.Dial(context.Background(), server, dest)
69	common.Must(err)
70	defer conn.Close()
71
72	const size = 10240 * 1024
73	payload := make([]byte, size)
74	common.Must2(rand.Read(payload))
75
76	if _, err := conn.Write(payload); err != nil {
77		t.Fatal(err)
78	}
79
80	receive := make([]byte, size)
81	if _, err := io.ReadFull(conn, receive); err != nil {
82		t.Fatal("failed to read all response: ", err)
83	}
84
85	if r := cmp.Diff(xor(receive), payload); r != "" {
86		t.Error(r)
87	}
88}
89
90func TestV2RayDialUDPConn(t *testing.T) {
91	udpServer := udp.Server{
92		MsgProcessor: xor,
93	}
94	dest, err := udpServer.Start()
95	common.Must(err)
96	defer udpServer.Close()
97
98	config := &core.Config{
99		App: []*serial.TypedMessage{
100			serial.ToTypedMessage(&dispatcher.Config{}),
101			serial.ToTypedMessage(&proxyman.InboundConfig{}),
102			serial.ToTypedMessage(&proxyman.OutboundConfig{}),
103		},
104		Outbound: []*core.OutboundHandlerConfig{
105			{
106				ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
107			},
108		},
109	}
110
111	cfgBytes, err := proto.Marshal(config)
112	common.Must(err)
113
114	server, err := core.StartInstance("protobuf", cfgBytes)
115	common.Must(err)
116	defer server.Close()
117
118	conn, err := core.Dial(context.Background(), server, dest)
119	common.Must(err)
120	defer conn.Close()
121
122	const size = 1024
123	payload := make([]byte, size)
124	common.Must2(rand.Read(payload))
125
126	for i := 0; i < 2; i++ {
127		if _, err := conn.Write(payload); err != nil {
128			t.Fatal(err)
129		}
130	}
131
132	time.Sleep(time.Millisecond * 500)
133
134	receive := make([]byte, size*2)
135	for i := 0; i < 2; i++ {
136		n, err := conn.Read(receive)
137		if err != nil {
138			t.Fatal("expect no error, but got ", err)
139		}
140		if n != size {
141			t.Fatal("expect read size ", size, " but got ", n)
142		}
143
144		if r := cmp.Diff(xor(receive[:n]), payload); r != "" {
145			t.Fatal(r)
146		}
147	}
148}
149
150func TestV2RayDialUDP(t *testing.T) {
151	udpServer1 := udp.Server{
152		MsgProcessor: xor,
153	}
154	dest1, err := udpServer1.Start()
155	common.Must(err)
156	defer udpServer1.Close()
157
158	udpServer2 := udp.Server{
159		MsgProcessor: xor2,
160	}
161	dest2, err := udpServer2.Start()
162	common.Must(err)
163	defer udpServer2.Close()
164
165	config := &core.Config{
166		App: []*serial.TypedMessage{
167			serial.ToTypedMessage(&dispatcher.Config{}),
168			serial.ToTypedMessage(&proxyman.InboundConfig{}),
169			serial.ToTypedMessage(&proxyman.OutboundConfig{}),
170		},
171		Outbound: []*core.OutboundHandlerConfig{
172			{
173				ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
174			},
175		},
176	}
177
178	cfgBytes, err := proto.Marshal(config)
179	common.Must(err)
180
181	server, err := core.StartInstance("protobuf", cfgBytes)
182	common.Must(err)
183	defer server.Close()
184
185	conn, err := core.DialUDP(context.Background(), server)
186	common.Must(err)
187	defer conn.Close()
188
189	const size = 1024
190	{
191		payload := make([]byte, size)
192		common.Must2(rand.Read(payload))
193
194		if _, err := conn.WriteTo(payload, &net.UDPAddr{
195			IP:   dest1.Address.IP(),
196			Port: int(dest1.Port),
197		}); err != nil {
198			t.Fatal(err)
199		}
200
201		receive := make([]byte, size)
202		if _, _, err := conn.ReadFrom(receive); err != nil {
203			t.Fatal(err)
204		}
205
206		if r := cmp.Diff(xor(receive), payload); r != "" {
207			t.Error(r)
208		}
209	}
210
211	{
212		payload := make([]byte, size)
213		common.Must2(rand.Read(payload))
214
215		if _, err := conn.WriteTo(payload, &net.UDPAddr{
216			IP:   dest2.Address.IP(),
217			Port: int(dest2.Port),
218		}); err != nil {
219			t.Fatal(err)
220		}
221
222		receive := make([]byte, size)
223		if _, _, err := conn.ReadFrom(receive); err != nil {
224			t.Fatal(err)
225		}
226
227		if r := cmp.Diff(xor2(receive), payload); r != "" {
228			t.Error(r)
229		}
230	}
231}
232