├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── common.go
├── dns
├── dns.go
└── dns_test.go
├── features
├── feature.go
├── shadowsocks.go
├── trojan.go
├── vless.go
└── vmess.go
├── go.mod
├── go.sum
├── lite
├── tcp.go
└── udp.go
├── log.go
├── ping
├── download.go
├── ping.go
├── tcp.go
└── tcp_test.go
├── pool
├── alloc.go
└── pool.go
├── runner
└── runner.go
├── settings
├── shadowsocks.go
└── trojan.go
├── tun2socks.go
├── tunnel
└── tun.go
├── v2ray
├── features.go
├── features_other.go
├── hosts.go
├── tcp.go
├── udp.go
└── vmess.go
└── xray
├── config.go
├── features.go
├── tcp.go
├── udp.go
└── xray.go
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches:
6 | - "master"
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-18.04
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Setup golang
15 | uses: actions/setup-go@v2
16 | with:
17 | go-version: "1.18"
18 | - name: Clone v2ray-core
19 | run: |
20 | git clone https://github.com/v2fly/v2ray-core
21 | cd ./v2ray-core
22 | git checkout v4.36.2 && cd ..
23 | - name: Setup gomobile
24 | run: |
25 | go get golang.org/x/mobile/cmd/gomobile
26 | gomobile init -v
27 | - name: Set up JDK 1.8
28 | uses: actions/setup-java@v1
29 | with:
30 | java-version: 1.8
31 | - name: Cache Data
32 | uses: actions/cache@v1
33 | env:
34 | ndk: android-ndk-r21e
35 | sdk-tools: 4333796
36 | with:
37 | path: ~/.android-cache
38 | key: ${{ runner.os }}-android-${{ env.ndk-version }}-${{ env.sdk-tools }}
39 | restore-keys: |
40 | ${{ runner.os }}-android-${{ env.ndk-version }}-${{ env.sdk-tools }}
41 | - name: Setup Android
42 | run: |
43 | wget "https://raw.githubusercontent.com/xxf098/shadowsocksr-v2ray-android/efbb4f034ac95eec2affc38f97681a23ec4a42e6/travis-ci/setup.sh" -O setup.sh
44 | sed -i 's/r12b/r21e/g' setup.sh && sed -i '41,48d' setup.sh && chmod +x setup.sh
45 | ./setup.sh
46 | - name: Build AAR
47 | run: |
48 | go get -d ./...
49 | make android
50 | - name: Upload AAR
51 | uses: actions/upload-artifact@v1
52 | with:
53 | name: v2raylib
54 | path: build/tun2socks.aar
55 | - name: Check Build
56 | run: |
57 | go version
58 | java -version
59 | ls "$ANDROID_HOME"
60 | ls -thla ./build
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.exe
2 | *.exe~
3 | *.dll
4 | *.so
5 | *.dylib
6 |
7 | # Test binary, built with `go test -c`
8 | *.test
9 |
10 | # Output of the go coverage tool, specifically when used with LiteIDE
11 | *.png
12 |
13 | # Dependency directories (remove the comment below to include it)
14 | build/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | GOMOBILE=gomobile
2 | GOBIND=$(GOMOBILE) bind
3 | BUILDDIR=$(shell pwd)/build
4 | IOS_ARTIFACT=$(BUILDDIR)/Tun2socks.framework
5 | ANDROID_ARTIFACT=$(BUILDDIR)/tun2socks.aar
6 | IOS_TARGET=ios
7 | ANDROID_TARGET=android
8 | # LDFLAGS='-s -w -X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn'
9 | LDFLAGS='-s -w'
10 | IMPORT_PATH=github.com/xxf098/go-tun2socks-build
11 |
12 | BUILD_IOS="cd $(BUILDDIR) && $(GOBIND) -a -ldflags $(LDFLAGS) -target=$(IOS_TARGET) -o $(IOS_ARTIFACT) $(IMPORT_PATH)"
13 | BUILD_ANDROID="cd $(BUILDDIR) && $(GOBIND) -a -ldflags $(LDFLAGS) -target=$(ANDROID_TARGET) -tags=gomobile -o $(ANDROID_ARTIFACT) $(IMPORT_PATH)"
14 |
15 | all: ios android
16 |
17 | ios:
18 | mkdir -p $(BUILDDIR)
19 | eval $(BUILD_IOS)
20 |
21 | android:
22 | rm -rf $(BUILDDIR) 2>/dev/null
23 | mkdir -p $(BUILDDIR)
24 | eval $(BUILD_ANDROID)
25 |
26 | clean:
27 | rm -rf $(BUILDDIR)
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-tun2socks-build
2 |
3 | Building and using `go-tun2socks` for V2Ray on Android. This library is used in [shadowsocksr-v2ray-trojan-android](https://github.com/xxf098/shadowsocksr-v2ray-trojan-android) for support V2Ray.
4 |
5 | 
6 |
7 | ## Setup
8 |
9 | * install [go](https://golang.org/doc/install#download) (test with version 1.21.0)
10 | * install [gomobile](https://godoc.org/golang.org/x/mobile/cmd/gomobile) with`go install golang.org/x/mobile/cmd/gomobile@latest`, then init with `gomobile init -v`
11 | * install [JDK 8](https://openjdk.java.net/install/) (not jre)
12 | * Download Android SDK and [NDK](https://developer.android.com/ndk/downloads) (test with SDK 30 and NDK r21e)
13 |
14 |
15 | ## Build
16 | ```bash
17 | # china only
18 | export GOPROXY=https://goproxy.cn
19 | # setup go env
20 | export GOPATH="/home/xxx/go"
21 | export PATH=$PATH:/usr/local/go/bin:~/go/bin
22 | # setup android env
23 | export ANDROID_HOME=/path/to/Android/Sdk
24 | export ANDROID_NDK_HOME=/path/to/Android/android-ndk-r21d
25 |
26 | go get -d ./...
27 |
28 | # Build an AAR
29 | make android
30 |
31 | ```
32 |
33 | ## Useage
34 |
35 |
--------------------------------------------------------------------------------
/common.go:
--------------------------------------------------------------------------------
1 | package tun2socks
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "encoding/json"
7 | "errors"
8 | "fmt"
9 | "io"
10 | "net"
11 | "net/http"
12 | "strconv"
13 | "strings"
14 | "time"
15 |
16 | vproxyman "github.com/xtls/xray-core/app/proxyman"
17 | verrors "github.com/xtls/xray-core/common/errors"
18 | vnet "github.com/xtls/xray-core/common/net"
19 | vsession "github.com/xtls/xray-core/common/session"
20 | vcore "github.com/xtls/xray-core/core"
21 | vinbound "github.com/xtls/xray-core/features/inbound"
22 | "github.com/xtls/xray-core/infra/conf"
23 |
24 | // "github.com/xtls/xray-core/infra/conf/cfgcommon"
25 | json_reader "github.com/xtls/xray-core/infra/conf/json"
26 | "github.com/xxf098/go-tun2socks-build/features"
27 | "github.com/xxf098/go-tun2socks-build/ping"
28 | "github.com/xxf098/go-tun2socks-build/pool"
29 | "github.com/xxf098/go-tun2socks-build/settings"
30 | "github.com/xxf098/go-tun2socks-build/v2ray"
31 | lconfig "github.com/xxf098/lite-proxy/config"
32 | loutbound "github.com/xxf098/lite-proxy/outbound"
33 | lutils "github.com/xxf098/lite-proxy/utils"
34 | )
35 |
36 | const (
37 | testProxyPort = uint32(8899)
38 | testUrl = "http://www.gstatic.com/generate_204"
39 | Version5 = 0x05
40 | AuthMethodNotRequired = 0x00
41 | SocksCmdConnect = 0x01
42 | AddrTypeIPv4 = 0x01
43 | AddrTypeFQDN = 0x03
44 | AddrTypeIPv6 = 0x04
45 | StatusSucceeded = 0x00
46 | defaultReadBufferSize = 4096
47 | remoteHost = "clients3.google.com"
48 | )
49 |
50 | var (
51 | strHTTP11 = []byte("HTTP/1.1")
52 | )
53 |
54 | func testLatency(url string) (int64, error) {
55 | // socksProxyURL, err := url.Parse(proxy)
56 | // if err != nil {
57 | // return 0, err
58 | // }
59 | // socksTransport := &http.Transport{Proxy: http.ProxyURL(socksProxyURL)}
60 | // client := &http.Client{Transport: socksTransport, Timeout: time.Second * 3}
61 | client := &http.Client{Timeout: time.Second * 3}
62 | resp, err := client.Get(url)
63 | if err != nil {
64 | return 0, err
65 | }
66 | defer resp.Body.Close()
67 | if err != nil {
68 | return 0, err
69 | }
70 | // fmt.Println(resp.Status)
71 | // fmt.Println(resp.StatusCode)
72 | // if resp.StatusCode == 204 {
73 | start := time.Now()
74 | resp1, err := client.Get(url)
75 | elapsed := time.Since(start)
76 | if err != nil {
77 | return 0, err
78 | }
79 | defer resp1.Body.Close()
80 | if resp1.StatusCode == 204 || resp1.StatusCode == 200 {
81 | return elapsed.Milliseconds(), nil
82 | }
83 | // }
84 | return 0, newError(resp.Status)
85 | }
86 |
87 | // https://github.com/golang/net/blob/master/internal/socks/client.go
88 | // scoks5 test vmess test
89 | // TODO: error message
90 | func testLatencyWithSocks5(ip string, port uint32) (int64, error) {
91 | addr := fmt.Sprintf("%s:%d", ip, port)
92 | conn, err := net.DialTimeout("tcp", addr, 1*time.Second)
93 | if err != nil {
94 | return 0, err
95 | }
96 | defer conn.Close()
97 | err = conn.SetDeadline(time.Now().Add(2 * time.Second))
98 | if err != nil {
99 | return 0, err
100 | }
101 | // remoteHost := "clients3.google.com"
102 | remotePort := 80
103 | b := make([]byte, 0, 6+len(remoteHost))
104 | b = append(b, Version5)
105 | b = append(b, 1, byte(AuthMethodNotRequired))
106 | if _, err = conn.Write(b); err != nil {
107 | return 0, err
108 | }
109 | if _, err = io.ReadFull(conn, b[:2]); err != nil {
110 | return 0, err
111 | }
112 | if b[0] != Version5 {
113 | return 0, errors.New("unexpected protocol version")
114 | }
115 | b = b[:0]
116 | b = append(b, Version5, SocksCmdConnect, 0)
117 | b = append(b, AddrTypeFQDN)
118 | b = append(b, byte(len(remoteHost)))
119 | b = append(b, remoteHost...)
120 | b = append(b, byte(remotePort>>8), byte(remotePort))
121 | if _, err = conn.Write(b); err != nil {
122 | return 0, err
123 | }
124 | if _, err = io.ReadFull(conn, b[:10]); err != nil {
125 | return 0, err
126 | }
127 | if b[0] != Version5 {
128 | return 0, errors.New("unexpected protocol version")
129 | }
130 | if b[1] != StatusSucceeded {
131 | return 0, errors.New("unknown error")
132 | }
133 | if b[2] != 0 {
134 | return 0, errors.New("non-zero reserved field")
135 | }
136 | if err = send204Request(&conn, 2*time.Second); err != nil {
137 | return 0, err
138 | }
139 | // timeout then retry
140 | start := time.Now()
141 | if err = send204Request(&conn, 1200*time.Millisecond); err != nil {
142 | return 0, err
143 | }
144 | elapsed := time.Since(start)
145 | return elapsed.Milliseconds(), nil
146 | }
147 |
148 | // TODO: refactor
149 | func send204Request(conn *net.Conn, timeout time.Duration) error {
150 | err = (*conn).SetDeadline(time.Now().Add(timeout))
151 | if err != nil {
152 | return err
153 | }
154 | // remoteHost := "clients3.google.com"
155 | httpRequest := fmt.Sprintf("GET /generate_204 HTTP/1.1\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36\r\n\r\n", remoteHost)
156 | if _, err = fmt.Fprintf(*conn, httpRequest); err != nil {
157 | return err
158 | }
159 | buf := make([]byte, 128)
160 | n, err := (*conn).Read(buf)
161 | if err != nil && err != io.EOF {
162 | return err
163 | }
164 | httpResponse := lutils.B2s(buf[:n])
165 | if !strings.HasPrefix(httpResponse, "HTTP/1.1 204 No Content") {
166 | return fmt.Errorf("error response: %s", httpResponse)
167 | }
168 | return nil
169 | }
170 |
171 | func sendCode204Request(conn *net.Conn, timeout time.Duration) error {
172 | err = (*conn).SetReadDeadline(time.Now().Add(timeout))
173 | if err != nil {
174 | return err
175 | }
176 | // remoteHost := "clients3.google.com"
177 | // httpRequest := fmt.Sprintf("GET /generate_204 HTTP/1.1\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36\r\n\r\n", remoteHost)
178 | httpRequest := "GET /generate_204 HTTP/1.1\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36\r\n\r\n"
179 | if _, err = fmt.Fprintf(*conn, httpRequest, remoteHost); err != nil {
180 | return err
181 | }
182 | buf := make([]byte, 128)
183 | _, err := (*conn).Read(buf)
184 | if err != nil && err != io.EOF {
185 | return err
186 | }
187 | _, err = parseFirstLine(buf)
188 | return err
189 | }
190 |
191 | func parseFirstLine(buf []byte) (int, error) {
192 | bNext := buf
193 | var b []byte
194 | var err error
195 | for len(b) == 0 {
196 | if b, bNext, err = nextLine(bNext); err != nil {
197 | return 0, err
198 | }
199 | }
200 |
201 | // parse protocol
202 | n := bytes.IndexByte(b, ' ')
203 | if n < 0 {
204 | return 0, fmt.Errorf("cannot find whitespace in the first line of response %q", buf)
205 | }
206 | b = b[n+1:]
207 |
208 | // parse status code
209 | statusCode, n, err := parseUintBuf(b)
210 | if err != nil {
211 | return 0, fmt.Errorf("cannot parse response status code: %s. Response %q", err, buf)
212 | }
213 | if len(b) > n && b[n] != ' ' {
214 | return 0, fmt.Errorf("unexpected char at the end of status code. Response %q", buf)
215 | }
216 |
217 | if statusCode == 204 || statusCode == 200 {
218 | return len(buf) - len(bNext), nil
219 | }
220 | return 0, errors.New("wrong status code")
221 | }
222 |
223 | func nextLine(b []byte) ([]byte, []byte, error) {
224 | nNext := bytes.IndexByte(b, '\n')
225 | if nNext < 0 {
226 | return nil, nil, errors.New("need more data: cannot find trailing lf")
227 | }
228 | n := nNext
229 | if n > 0 && b[n-1] == '\r' {
230 | n--
231 | }
232 | return b[:n], b[nNext+1:], nil
233 | }
234 |
235 | func parseUintBuf(b []byte) (int, int, error) {
236 | n := len(b)
237 | if n == 0 {
238 | return -1, 0, errors.New("empty integer")
239 | }
240 | v := 0
241 | for i := 0; i < n; i++ {
242 | c := b[i]
243 | k := c - '0'
244 | if k > 9 {
245 | if i == 0 {
246 | return -1, i, errors.New("unexpected first char found. Expecting 0-9")
247 | }
248 | return v, i, nil
249 | }
250 | vNew := 10*v + int(k)
251 | // Test for overflow.
252 | if vNew < v {
253 | return -1, i, errors.New("too long int")
254 | }
255 | v = vNew
256 | }
257 | return v, n, nil
258 | }
259 |
260 | func v2rayDownload(profile *Vmess, timeout time.Duration, resultChan chan<- int64) (int64, error) {
261 | var max int64 = 0
262 | config, err := loadVmessTestConfig(profile, 0)
263 | if err != nil {
264 | return max, err
265 | }
266 | instance, err := startInstance(profile, config)
267 | if err != nil {
268 | return max, err
269 | }
270 | dialer := features.VmessDialer{
271 | Instance: instance,
272 | }
273 | httpTransport := &http.Transport{
274 | DialContext: dialer.DialContext,
275 | }
276 | httpClient := &http.Client{Transport: httpTransport, Timeout: timeout}
277 | req, err := http.NewRequest("GET", "https://download.microsoft.com/download/2/0/E/20E90413-712F-438C-988E-FDAA79A8AC3D/dotnetfx35.exe", nil)
278 | if err != nil {
279 | return max, err
280 | }
281 | response, err := httpClient.Do(req)
282 | if err != nil {
283 | return max, err
284 | }
285 | defer response.Body.Close()
286 | start := time.Now()
287 | prev := start
288 | var total int64
289 | for {
290 | buf := pool.NewBytes(20 * 1024)
291 | nr, er := response.Body.Read(buf)
292 | total += int64(nr)
293 | pool.FreeBytes(buf)
294 | now := time.Now()
295 | if now.Sub(prev) >= time.Second || err != nil {
296 | prev = now
297 | if resultChan != nil {
298 | resultChan <- total
299 | }
300 | if max < total {
301 | max = total
302 | }
303 | total = 0
304 | }
305 | if er != nil {
306 | if er != io.EOF {
307 | err = er
308 | }
309 | break
310 | }
311 | }
312 | resultChan <- -1
313 | return max, nil
314 | }
315 |
316 | func testLatencyWithHTTP(v *vcore.Instance) (int64, error) {
317 | dest := vnet.Destination{
318 | Address: vnet.DomainAddress("clients3.google.com"),
319 | Network: vnet.Network_TCP,
320 | Port: vnet.Port(80),
321 | }
322 | sid := vsession.NewID()
323 | ctx := vsession.ContextWithID(context.Background(), sid)
324 | conn, err := vcore.Dial(ctx, v, dest)
325 | if err != nil {
326 | return 0, fmt.Errorf("dial V proxy connection failed: %v", err)
327 | }
328 | defer conn.Close()
329 | timeout := 1235 * time.Millisecond
330 | if err = sendCode204Request(&conn, timeout); err != nil {
331 | timeout = 2358 * time.Millisecond
332 | }
333 | // timeout then retry
334 | start := time.Now()
335 | if err = sendCode204Request(&conn, timeout); err != nil {
336 | return 0, err
337 | }
338 | elapsed := time.Since(start)
339 | return elapsed.Milliseconds(), nil
340 | }
341 |
342 | func addInboundHandler(server *vcore.Instance) (string, error) {
343 | if inboundManager := server.GetFeature(vinbound.ManagerType()).(vinbound.Manager); inboundManager != nil {
344 | if _, err := inboundManager.GetHandler(context.Background(), "socks-in"); err == nil {
345 | if err := inboundManager.RemoveHandler(context.Background(), "socks-in"); err != nil {
346 | return "", err
347 | }
348 | }
349 | }
350 | inboundDetourConfig := createInboundDetourConfig(testProxyPort)
351 | inboundConfig, err := inboundDetourConfig.Build()
352 | if err != nil {
353 | return "", err
354 | }
355 | err = vcore.AddInboundHandler(server, inboundConfig)
356 | if err != nil {
357 | return "", err
358 | }
359 | return fmt.Sprintf("socks5://127.0.0.1:%d", testProxyPort), nil
360 | }
361 |
362 | func createInboundDetourConfig(proxyPort uint32) conf.InboundDetourConfig {
363 | // inboundManager.RemoveHandler(context.Background(), "socks-in")
364 | // inboundManager.RemoveHandler(context.Background(), "http-in")
365 | inboundsSettings, _ := json.Marshal(v2ray.InboundsSettings{
366 | Auth: "noauth",
367 | IP: "127.0.0.1",
368 | UDP: true,
369 | })
370 | inboundsSettingsMsg := json.RawMessage(inboundsSettings)
371 | inboundDetourConfig := conf.InboundDetourConfig{
372 | Tag: "socks-in",
373 | Protocol: "socks",
374 | PortList: &conf.PortList{Range: []conf.PortRange{conf.PortRange{From: proxyPort, To: proxyPort}}},
375 | ListenOn: &conf.Address{vnet.IPAddress([]byte{127, 0, 0, 1})},
376 | Settings: &inboundsSettingsMsg,
377 | }
378 | return inboundDetourConfig
379 | }
380 |
381 | func createVmessOutboundDetourConfig(profile *Vmess) conf.OutboundDetourConfig {
382 | outboundsSettings1, _ := json.Marshal(v2ray.OutboundsSettings{
383 | Vnext: []v2ray.Vnext{
384 | v2ray.Vnext{
385 | Address: profile.Add,
386 | Port: profile.Port,
387 | Users: []v2ray.Users{
388 | v2ray.Users{
389 | AlterID: profile.Aid,
390 | Email: "xxf098@github.com",
391 | ID: profile.ID,
392 | Level: 8,
393 | Security: profile.Security,
394 | },
395 | },
396 | },
397 | },
398 | })
399 | outboundsSettingsMsg1 := json.RawMessage(outboundsSettings1)
400 | muxEnabled := false
401 | if profile.VmessOptions.Mux > 0 {
402 | muxEnabled = true
403 | } else {
404 | profile.VmessOptions.Mux = -1
405 | }
406 | tcp := conf.TransportProtocol("tcp")
407 | vmessOutboundDetourConfig := conf.OutboundDetourConfig{
408 | Protocol: "vmess",
409 | Tag: "proxy",
410 | MuxSettings: &conf.MuxConfig{Enabled: muxEnabled, Concurrency: int16(profile.VmessOptions.Mux)},
411 | Settings: &outboundsSettingsMsg1,
412 | StreamSetting: &conf.StreamConfig{
413 | Network: &tcp,
414 | Security: "",
415 | },
416 | }
417 | if profile.Net == "ws" {
418 | transportProtocol := conf.TransportProtocol(profile.Net)
419 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
420 | Network: &transportProtocol,
421 | WSSettings: &conf.WebSocketConfig{Path: profile.Path},
422 | }
423 | if profile.Host != "" {
424 | vmessOutboundDetourConfig.StreamSetting.WSSettings.Headers =
425 | map[string]string{"Host": profile.Host}
426 | }
427 | }
428 |
429 | if profile.Net == "h2" {
430 | transportProtocol := conf.TransportProtocol(profile.Net)
431 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
432 | Network: &transportProtocol,
433 | HTTPSettings: &conf.HTTPConfig{Path: profile.Path},
434 | }
435 | if profile.Host != "" {
436 | hosts := strings.Split(profile.Host, ",")
437 | vmessOutboundDetourConfig.StreamSetting.HTTPSettings.Host = conf.NewStringList(hosts)
438 | }
439 | }
440 |
441 | if profile.Net == "quic" {
442 | transportProtocol := conf.TransportProtocol(profile.Net)
443 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
444 | Network: &transportProtocol,
445 | QUICSettings: &conf.QUICConfig{Key: profile.Path},
446 | }
447 | if profile.Host != "" {
448 | vmessOutboundDetourConfig.StreamSetting.QUICSettings.Security = profile.Host
449 | }
450 | if profile.Type != "" {
451 | header, _ := json.Marshal(v2ray.QUICSettingsHeader{Type: profile.Type})
452 | vmessOutboundDetourConfig.StreamSetting.QUICSettings.Header = json.RawMessage(header)
453 | }
454 | }
455 |
456 | if profile.Net == "kcp" {
457 | transportProtocol := conf.TransportProtocol(profile.Net)
458 | mtu := uint32(1350)
459 | tti := uint32(50)
460 | upCap := uint32(12)
461 | downap := uint32(100)
462 | congestion := false
463 | readBufferSize := uint32(1)
464 | writeBufferSize := uint32(1)
465 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
466 | Network: &transportProtocol,
467 | KCPSettings: &conf.KCPConfig{
468 | Mtu: &mtu,
469 | Tti: &tti,
470 | UpCap: &upCap,
471 | DownCap: &downap,
472 | Congestion: &congestion,
473 | ReadBufferSize: &readBufferSize,
474 | WriteBufferSize: &writeBufferSize,
475 | },
476 | }
477 | if profile.Type != "" {
478 | header, _ := json.Marshal(v2ray.KCPSettingsHeader{Type: profile.Type})
479 | vmessOutboundDetourConfig.StreamSetting.KCPSettings.HeaderConfig = json.RawMessage(header)
480 | }
481 | }
482 |
483 | // tcp带http伪装
484 | if profile.Net == "tcp" && profile.Type == "http" {
485 | transportProtocol := conf.TransportProtocol(profile.Net)
486 | tcpSettingsHeader := v2ray.TCPSettingsHeader{
487 | Type: profile.Type,
488 | TCPSettingsRequest: v2ray.TCPSettingsRequest{
489 | Version: "1.1",
490 | Method: "GET",
491 | Path: []string{profile.Path}, // TODO: split by ","
492 | Headers: v2ray.HTTPHeaders{
493 | UserAgent: []string{"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"},
494 | AcceptEncoding: []string{"gzip, deflate"},
495 | Connection: "keep-alive",
496 | Pragma: "no-cache",
497 | Host: []string{profile.Host}, // TODO: split by ","
498 | },
499 | },
500 | }
501 | header, _ := json.Marshal(tcpSettingsHeader)
502 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
503 | Network: &transportProtocol,
504 | Security: profile.TLS,
505 | TCPSettings: &conf.TCPConfig{
506 | HeaderConfig: json.RawMessage(header),
507 | },
508 | }
509 | }
510 |
511 | if profile.TLS == "tls" {
512 | vmessOutboundDetourConfig.StreamSetting.Security = profile.TLS
513 | tlsConfig := &conf.TLSConfig{Insecure: profile.AllowInsecure}
514 | if profile.Host != "" {
515 | tlsConfig.ServerName = profile.Host
516 | }
517 | vmessOutboundDetourConfig.StreamSetting.TLSSettings = tlsConfig
518 | }
519 | return vmessOutboundDetourConfig
520 | }
521 |
522 | // TODO: refactor
523 | func configVmessTransport(profile *Vmess, outboundsSettingsMsg1 json.RawMessage) conf.OutboundDetourConfig {
524 | muxEnabled := false
525 | if profile.VmessOptions.Mux > 0 {
526 | muxEnabled = true
527 | } else {
528 | profile.VmessOptions.Mux = -1
529 | }
530 | tcp := conf.TransportProtocol("tcp")
531 | streamSetting := &conf.StreamConfig{
532 | Network: &tcp,
533 | Security: "",
534 | }
535 | if profile.Protocol == v2ray.VLESS {
536 | tcpHeader, _ := json.Marshal(v2ray.TcpHeader{Type: profile.Type})
537 | tcpHeaderMsg := json.RawMessage(tcpHeader)
538 | tcpSetting := &conf.TCPConfig{
539 | HeaderConfig: tcpHeaderMsg,
540 | }
541 | streamSetting.TCPSettings = tcpSetting
542 | }
543 | vmessOutboundDetourConfig := conf.OutboundDetourConfig{
544 | Protocol: "vmess",
545 | Tag: "proxy",
546 | MuxSettings: &conf.MuxConfig{Enabled: muxEnabled, Concurrency: int16(profile.VmessOptions.Mux)},
547 | Settings: &outboundsSettingsMsg1,
548 | StreamSetting: streamSetting,
549 | }
550 | if profile.Protocol == v2ray.VLESS {
551 | vmessOutboundDetourConfig.Protocol = "vless"
552 | }
553 |
554 | if profile.Net == "ws" {
555 | transportProtocol := conf.TransportProtocol(profile.Net)
556 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
557 | Network: &transportProtocol,
558 | WSSettings: &conf.WebSocketConfig{Path: profile.Path},
559 | }
560 | if profile.Host != "" {
561 | vmessOutboundDetourConfig.StreamSetting.WSSettings.Headers =
562 | map[string]string{"Host": profile.Host}
563 | }
564 | }
565 |
566 | if profile.Net == "h2" {
567 | transportProtocol := conf.TransportProtocol(profile.Net)
568 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
569 | Network: &transportProtocol,
570 | HTTPSettings: &conf.HTTPConfig{Path: profile.Path},
571 | }
572 | if profile.Host != "" {
573 | hosts := strings.Split(profile.Host, ",")
574 | vmessOutboundDetourConfig.StreamSetting.HTTPSettings.Host = conf.NewStringList(hosts)
575 | }
576 | }
577 |
578 | if profile.Net == "quic" {
579 | transportProtocol := conf.TransportProtocol(profile.Net)
580 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
581 | Network: &transportProtocol,
582 | QUICSettings: &conf.QUICConfig{Key: profile.Path},
583 | }
584 | if profile.Host != "" {
585 | vmessOutboundDetourConfig.StreamSetting.QUICSettings.Security = profile.Host
586 | }
587 | if profile.Type != "" {
588 | header, _ := json.Marshal(v2ray.QUICSettingsHeader{Type: profile.Type})
589 | vmessOutboundDetourConfig.StreamSetting.QUICSettings.Header = json.RawMessage(header)
590 | }
591 | }
592 |
593 | if profile.Net == "kcp" {
594 | transportProtocol := conf.TransportProtocol(profile.Net)
595 | mtu := uint32(1350)
596 | tti := uint32(50)
597 | upCap := uint32(12)
598 | downap := uint32(100)
599 | congestion := false
600 | readBufferSize := uint32(1)
601 | writeBufferSize := uint32(1)
602 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
603 | Network: &transportProtocol,
604 | KCPSettings: &conf.KCPConfig{
605 | Mtu: &mtu,
606 | Tti: &tti,
607 | UpCap: &upCap,
608 | DownCap: &downap,
609 | Congestion: &congestion,
610 | ReadBufferSize: &readBufferSize,
611 | WriteBufferSize: &writeBufferSize,
612 | },
613 | }
614 | if profile.Type != "" {
615 | header, _ := json.Marshal(v2ray.KCPSettingsHeader{Type: profile.Type})
616 | vmessOutboundDetourConfig.StreamSetting.KCPSettings.HeaderConfig = json.RawMessage(header)
617 | }
618 | }
619 |
620 | // tcp带http伪装
621 | if profile.Net == "tcp" && profile.Type == "http" {
622 | transportProtocol := conf.TransportProtocol(profile.Net)
623 | tcpSettingsHeader := v2ray.TCPSettingsHeader{
624 | Type: profile.Type,
625 | TCPSettingsRequest: v2ray.TCPSettingsRequest{
626 | Version: "1.1",
627 | Method: "GET",
628 | Path: []string{profile.Path}, // TODO: split by ","
629 | Headers: v2ray.HTTPHeaders{
630 | UserAgent: []string{"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36"},
631 | AcceptEncoding: []string{"gzip, deflate"},
632 | Connection: "keep-alive",
633 | Pragma: "no-cache",
634 | Host: []string{profile.Host}, // TODO: split by ","
635 | },
636 | },
637 | }
638 | header, _ := json.Marshal(tcpSettingsHeader)
639 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
640 | Network: &transportProtocol,
641 | Security: profile.TLS,
642 | TCPSettings: &conf.TCPConfig{
643 | HeaderConfig: json.RawMessage(header),
644 | },
645 | }
646 | }
647 |
648 | if profile.TLS == "tls" {
649 | vmessOutboundDetourConfig.StreamSetting.Security = profile.TLS
650 | tlsConfig := &conf.TLSConfig{Insecure: profile.AllowInsecure}
651 | if profile.Host != "" {
652 | tlsConfig.ServerName = profile.Host
653 | }
654 | if profile.SNI != "" {
655 | tlsConfig.ServerName = profile.SNI
656 | }
657 | vmessOutboundDetourConfig.StreamSetting.TLSSettings = tlsConfig
658 | }
659 | return vmessOutboundDetourConfig
660 | }
661 |
662 | func createVlessOutboundDetourConfig(profile *Vmess) conf.OutboundDetourConfig {
663 | security := profile.Security
664 | if len(security) < 1 {
665 | security = "auto"
666 | }
667 | encryption := profile.Encryption
668 | if len(encryption) < 1 {
669 | encryption = "none"
670 | }
671 | outboundsSettings1, _ := json.Marshal(v2ray.VlessOutboundsSettings{
672 | Vnext: []v2ray.Vlessnext{
673 | {
674 | Address: profile.Add,
675 | Port: profile.Port,
676 | Users: []v2ray.VlessUser{
677 | {
678 | Encryption: encryption,
679 | Flow: profile.Flow,
680 | ID: profile.ID,
681 | Level: 8,
682 | Security: security,
683 | },
684 | },
685 | },
686 | },
687 | })
688 | outboundsSettingsMsg := json.RawMessage(outboundsSettings1)
689 | return configVmessTransport(profile, outboundsSettingsMsg)
690 | }
691 |
692 | func createTrojanOutboundDetourConfig(profile *Vmess) conf.OutboundDetourConfig {
693 | config := profile.Trojan
694 | // outboundsSettings, _ := json.Marshal(trojan.OutboundsSettings{
695 | // Address: config.Add,
696 | // Password: config.Password,
697 | // Port: config.Port,
698 | // ServerName: config.SNI,
699 | // SkipVerify: config.SkipCertVerify,
700 | // })
701 | outboundsSettings, _ := json.Marshal(settings.TrojanOutboundsSettings{
702 | Servers: []*settings.TrojanServerTarget{
703 | &settings.TrojanServerTarget{
704 | Address: config.Add,
705 | Email: "xxf098@github.com",
706 | Level: 8,
707 | Password: config.Password,
708 | Port: uint16(config.Port),
709 | },
710 | },
711 | })
712 | outboundsSettingsMsg := json.RawMessage(outboundsSettings)
713 | transportProtocol := conf.TransportProtocol("tcp")
714 | streamSetting := &conf.StreamConfig{
715 | Security: "tls",
716 | Network: &transportProtocol,
717 | TLSSettings: &conf.TLSConfig{
718 | Insecure: config.SkipCertVerify,
719 | ServerName: config.SNI,
720 | },
721 | }
722 | if config.Net == "ws" {
723 | transportProtocol := conf.TransportProtocol(config.Net)
724 | streamSetting = &conf.StreamConfig{
725 | Security: "tls",
726 | Network: &transportProtocol,
727 | TLSSettings: &conf.TLSConfig{
728 | Insecure: config.SkipCertVerify,
729 | ServerName: config.SNI,
730 | },
731 | WSSettings: &conf.WebSocketConfig{Path: config.Path},
732 | }
733 | if len(config.Host) < 1 {
734 | config.Host = config.SNI
735 | }
736 | streamSetting.WSSettings.Headers = map[string]string{"Host": config.Host}
737 | }
738 | trojanOutboundDetourConfig := conf.OutboundDetourConfig{
739 | Protocol: "trojan",
740 | Tag: "proxy",
741 | Settings: &outboundsSettingsMsg,
742 | StreamSetting: streamSetting,
743 | }
744 | return trojanOutboundDetourConfig
745 | }
746 |
747 | func createShadowsocksOutboundDetourConfig(profile *Vmess) conf.OutboundDetourConfig {
748 | config := profile.Shadowsocks
749 | outboundsSettings, _ := json.Marshal(settings.ShadowsocksOutboundsSettings{
750 | Servers: []*settings.ShadowsocksServerTarget{
751 | &settings.ShadowsocksServerTarget{
752 | Address: config.Add,
753 | Method: config.Method,
754 | Email: "xxf098@github.com",
755 | Level: 0,
756 | OTA: false,
757 | Password: config.Password,
758 | Port: uint16(config.Port),
759 | },
760 | },
761 | })
762 | outboundsSettingsMsg := json.RawMessage(outboundsSettings)
763 | shadowsocksOutboundDetourConfig := conf.OutboundDetourConfig{
764 | Protocol: "shadowsocks",
765 | Tag: "proxy",
766 | Settings: &outboundsSettingsMsg,
767 | }
768 | return shadowsocksOutboundDetourConfig
769 | }
770 |
771 | func createFreedomOutboundDetourConfig(useIPv6 bool) conf.OutboundDetourConfig {
772 | domainStrategy := "useipv4"
773 | if useIPv6 {
774 | domainStrategy = "useip"
775 | }
776 | outboundsSettings2, _ := json.Marshal(v2ray.OutboundsSettings{DomainStrategy: domainStrategy})
777 | outboundsSettingsMsg2 := json.RawMessage(outboundsSettings2)
778 | return conf.OutboundDetourConfig{
779 | Protocol: "freedom",
780 | Tag: "direct",
781 | Settings: &outboundsSettingsMsg2,
782 | }
783 | }
784 |
785 | // 0 all
786 | // 1 bypass LAN
787 | // 2 bypass China
788 | // 3 bypass LAN & China
789 | // 4 GFWList
790 | // 5 ChinaList
791 | // >= 6 bypass LAN & China & AD block
792 | //
793 | // 0: "Plain", 1: "Regex", 2: "Domain", 3: "Full",
794 | //
795 | // https://github.com/Loyalsoldier/v2ray-rules-dat
796 | func createRouterConfig(routeMode int) *conf.RouterConfig {
797 | domainStrategy := "IPIfNonMatch"
798 | bypassLAN, _ := json.Marshal(v2ray.Rules{
799 | Type: "field",
800 | OutboundTag: "direct",
801 | IP: []string{"geoip:private"},
802 | })
803 | bypassChinaIP, _ := json.Marshal(v2ray.Rules{
804 | Type: "field",
805 | OutboundTag: "direct",
806 | IP: []string{"geoip:cn"},
807 | })
808 | bypassChinaSite, _ := json.Marshal(v2ray.Rules{
809 | Type: "field",
810 | OutboundTag: "direct",
811 | Domain: []string{"geosite:cn"},
812 | })
813 | blockDomain, _ := json.Marshal(v2ray.Rules{
814 | Type: "field",
815 | OutboundTag: "blocked",
816 | Domain: v2ray.BlockDomains,
817 | })
818 | directDomains, _ := json.Marshal(v2ray.Rules{
819 | Type: "field",
820 | OutboundTag: "direct",
821 | Domain: v2ray.DirectDomains,
822 | })
823 | // blockAd, _ := json.Marshal(v2ray.Rules{
824 | // Type: "field",
825 | // OutboundTag: "blocked",
826 | // Domain: []string{"geosite:category-ads-all"},
827 | // })
828 | gfwList, _ := json.Marshal(v2ray.Rules{
829 | Type: "field",
830 | OutboundTag: "proxy",
831 | Domain: []string{"geosite:geolocation-!cn"},
832 | })
833 | gfwListIP, _ := json.Marshal(v2ray.Rules{
834 | Type: "field",
835 | OutboundTag: "proxy",
836 | IP: []string{
837 | "8.8.8.8/32",
838 | "8.8.4.4/32",
839 | "1.1.1.1/32",
840 | "1.0.0.1/32",
841 | "149.154.160.0/22",
842 | "149.154.164.0/22",
843 | "91.108.4.0/22",
844 | "91.108.56.0/22",
845 | "91.108.8.0/22",
846 | "95.161.64.0/20",
847 | },
848 | })
849 | chinaListSite, _ := json.Marshal(v2ray.Rules{
850 | Type: "field",
851 | OutboundTag: "proxy",
852 | Domain: []string{"geosite:cn"},
853 | })
854 | chinaListIP, _ := json.Marshal(v2ray.Rules{
855 | Type: "field",
856 | OutboundTag: "proxy",
857 | IP: []string{"geoip:cn"},
858 | })
859 | googleAPI, _ := json.Marshal(v2ray.Rules{
860 | Type: "field",
861 | OutboundTag: "proxy",
862 | Domain: []string{"domain:googleapis.cn", "domain:gstatic.com", "domain:ampproject.org"},
863 | })
864 | rules := []json.RawMessage{}
865 | if routeMode == 1 {
866 | rules = []json.RawMessage{
867 | json.RawMessage(googleAPI),
868 | json.RawMessage(bypassLAN),
869 | json.RawMessage(blockDomain),
870 | }
871 | }
872 | if routeMode == 2 {
873 | rules = []json.RawMessage{
874 | json.RawMessage(googleAPI),
875 | json.RawMessage(blockDomain),
876 | json.RawMessage(bypassChinaSite),
877 | json.RawMessage(gfwList),
878 | json.RawMessage(bypassChinaIP),
879 | }
880 | }
881 | if routeMode == 3 {
882 | rules = []json.RawMessage{
883 | json.RawMessage(googleAPI),
884 | json.RawMessage(blockDomain),
885 | json.RawMessage(bypassLAN),
886 | json.RawMessage(directDomains),
887 | json.RawMessage(bypassChinaSite),
888 | json.RawMessage(gfwList), // sniff
889 | json.RawMessage(bypassChinaIP),
890 | }
891 | }
892 | if routeMode == 4 {
893 | rules = []json.RawMessage{
894 | json.RawMessage(googleAPI),
895 | json.RawMessage(blockDomain),
896 | json.RawMessage(gfwListIP),
897 | json.RawMessage(gfwList),
898 | }
899 | }
900 | if routeMode == 5 {
901 | rules = []json.RawMessage{
902 | // json.RawMessage(googleAPI),
903 | json.RawMessage(blockDomain),
904 | json.RawMessage(chinaListSite),
905 | json.RawMessage(chinaListIP),
906 | }
907 | }
908 | if routeMode >= 5 {
909 | rules = []json.RawMessage{
910 | json.RawMessage(googleAPI),
911 | json.RawMessage(bypassLAN),
912 | json.RawMessage(bypassChinaSite),
913 | json.RawMessage(bypassChinaIP),
914 | json.RawMessage(blockDomain),
915 | // json.RawMessage(blockAd),
916 | }
917 | }
918 | return &conf.RouterConfig{
919 | DomainStrategy: &domainStrategy,
920 | RuleList: rules,
921 | }
922 | }
923 |
924 | func creatPolicyConfig() *conf.PolicyConfig {
925 | handshake := uint32(4)
926 | connIdle := uint32(300)
927 | downlinkOnly := uint32(1)
928 | uplinkOnly := uint32(1)
929 | return &conf.PolicyConfig{
930 | Levels: map[uint32]*conf.Policy{
931 | 8: &conf.Policy{
932 | ConnectionIdle: &connIdle,
933 | DownlinkOnly: &downlinkOnly,
934 | Handshake: &handshake,
935 | UplinkOnly: &uplinkOnly,
936 | },
937 | },
938 | System: &conf.SystemPolicy{
939 | StatsOutboundUplink: true,
940 | StatsOutboundDownlink: true,
941 | },
942 | }
943 | }
944 |
945 | // remove https://github.com/v2ray/v2ray-core/blob/02b658cd2beb5968818c7ed37388fb348b9b9cb9/app/dns/server.go#L362
946 | func createDNSConfig(routeMode int, dnsConf string) *conf.DNSConfig {
947 | // nameServerConfig := []*conf.NameServerConfig{
948 | // &conf.NameServerConfig{
949 | // Address: &conf.Address{vnet.IPAddress([]byte{223, 5, 5, 5})},
950 | // Port: 53,
951 | // // Domains: []string{"geosite:cn"},
952 | // },
953 | // &conf.NameServerConfig{Address: &conf.Address{vnet.IPAddress([]byte{1, 1, 1, 1})}, Port: 53},
954 | // }
955 | // if routeMode == 2 || routeMode == 3 || routeMode == 4 {
956 | // nameServerConfig = []*conf.NameServerConfig{
957 | // &conf.NameServerConfig{Address: &conf.Address{vnet.IPAddress([]byte{1, 1, 1, 1})}, Port: 53},
958 | // }
959 | // }
960 | dns := strings.Split(dnsConf, ",")
961 | nameServerConfig := []*conf.NameServerConfig{}
962 | if routeMode == 2 || routeMode == 3 || routeMode == 4 {
963 | for i := len(dns) - 1; i >= 0; i-- {
964 | if newConfig := toNameServerConfig(dns[i]); newConfig != nil {
965 | if i == 1 {
966 | newConfig.Domains = []string{"geosite:cn"}
967 | }
968 | nameServerConfig = append(nameServerConfig, newConfig)
969 | }
970 | }
971 | } else {
972 | if newConfig := toNameServerConfig(dns[0]); newConfig != nil {
973 | nameServerConfig = append(nameServerConfig, newConfig)
974 | }
975 | }
976 | return &conf.DNSConfig{
977 | Hosts: &conf.HostsWrapper{Hosts: v2ray.BlockHosts},
978 | Servers: nameServerConfig,
979 | }
980 | }
981 |
982 | func toNameServerConfig(hostport string) *conf.NameServerConfig {
983 | // doh
984 | if strings.HasPrefix("https", hostport) {
985 | newConfig := &conf.NameServerConfig{Address: &conf.Address{vnet.ParseAddress(hostport)}}
986 | return newConfig
987 | }
988 | if hostport == "8.8.8.8:53" {
989 | newConfig := &conf.NameServerConfig{Address: &conf.Address{vnet.ParseAddress("https://dns.google/dns-query")}}
990 | return newConfig
991 | }
992 | if hostport == "1.1.1.1:53" {
993 | newConfig := &conf.NameServerConfig{Address: &conf.Address{vnet.ParseAddress("https://1.1.1.1/dns-query")}}
994 | return newConfig
995 | }
996 |
997 | host, port, err := net.SplitHostPort(hostport)
998 | if err != nil {
999 | return nil
1000 | }
1001 | p, err := strconv.Atoi(port)
1002 | if err != nil {
1003 | return nil
1004 | }
1005 | newConfig := &conf.NameServerConfig{Address: &conf.Address{vnet.ParseAddress(host)}, Port: uint16(p)}
1006 | return newConfig
1007 | }
1008 |
1009 | type offset struct {
1010 | line int
1011 | char int
1012 | }
1013 |
1014 | func findOffset(b []byte, o int) *offset {
1015 | if o >= len(b) || o < 0 {
1016 | return nil
1017 | }
1018 |
1019 | line := 1
1020 | char := 0
1021 | for i, x := range b {
1022 | if i == o {
1023 | break
1024 | }
1025 | if x == '\n' {
1026 | line++
1027 | char = 0
1028 | } else {
1029 | char++
1030 | }
1031 | }
1032 |
1033 | return &offset{line: line, char: char}
1034 | }
1035 |
1036 | func DecodeJSONConfig(reader io.Reader) (*conf.Config, error) {
1037 | jsonConfig := &conf.Config{}
1038 |
1039 | jsonContent := bytes.NewBuffer(make([]byte, 0, 10240))
1040 | jsonReader := io.TeeReader(&json_reader.Reader{
1041 | Reader: reader,
1042 | }, jsonContent)
1043 | decoder := json.NewDecoder(jsonReader)
1044 |
1045 | if err := decoder.Decode(jsonConfig); err != nil {
1046 | var pos *offset
1047 | cause := verrors.Cause(err)
1048 | switch tErr := cause.(type) {
1049 | case *json.SyntaxError:
1050 | pos = findOffset(jsonContent.Bytes(), int(tErr.Offset))
1051 | case *json.UnmarshalTypeError:
1052 | pos = findOffset(jsonContent.Bytes(), int(tErr.Offset))
1053 | }
1054 | if pos != nil {
1055 | return nil, newError("failed to read config file at line ", pos.line, " char ", pos.char).Base(err)
1056 | }
1057 | return nil, newError("failed to read config file").Base(err)
1058 | }
1059 |
1060 | return jsonConfig, nil
1061 | }
1062 |
1063 | // ContextWithSniffingConfig is a wrapper of session.ContextWithContent.
1064 | // Deprecated. Use session.ContextWithContent directly.
1065 | func contextWithSniffingConfig(ctx context.Context, c *vproxyman.SniffingConfig) context.Context {
1066 | content := vsession.ContentFromContext(ctx)
1067 | if content == nil {
1068 | content = new(vsession.Content)
1069 | ctx = vsession.ContextWithContent(ctx, content)
1070 | }
1071 | content.SniffingRequest.Enabled = c.Enabled
1072 | content.SniffingRequest.OverrideDestinationForProtocol = c.DestinationOverride
1073 | return ctx
1074 | }
1075 |
1076 | func runCore(index int, link string, c chan<- ping.TestResult) (bool, error) {
1077 | option, err := lconfig.VmessLinkToVmessConfigIP(link, false)
1078 | if err != nil {
1079 | return false, err
1080 | }
1081 | profile := NewVmess(option.Host,
1082 | option.Path,
1083 | option.TLS,
1084 | option.Add,
1085 | option.PortInt,
1086 | option.AidInt,
1087 | option.Net,
1088 | option.ID,
1089 | v2ray.VMESS,
1090 | option.Security,
1091 | []byte{})
1092 | elapse, err := TestVmessLatency(profile, -1)
1093 | result := ping.TestResult{
1094 | Result: elapse,
1095 | Index: index,
1096 | Err: err,
1097 | Protocol: "vmess",
1098 | }
1099 | c <- result
1100 | return false, err
1101 | }
1102 |
1103 | func vmess2Lite(profile *Vmess) (loutbound.Dialer, error) {
1104 | option, err := profile2Option(profile)
1105 | if err != nil {
1106 | return nil, err
1107 | }
1108 | vmessOption, ok := option.(*loutbound.VmessOption)
1109 | if !ok {
1110 | return nil, newError("not support protocol")
1111 | }
1112 | return loutbound.NewVmess(vmessOption)
1113 | }
1114 |
1115 | func trojan2Lite(profile *Vmess) (loutbound.Dialer, error) {
1116 | return nil, newError("not support protocol")
1117 | }
1118 |
1119 | func ss2Lite(profile *Vmess) (loutbound.Dialer, error) {
1120 | option, _ := profile2Option(profile)
1121 | ssOption, ok := option.(*loutbound.ShadowSocksOption)
1122 | if !ok {
1123 | return nil, newError("not support protocol")
1124 | }
1125 | return loutbound.NewShadowSocks(ssOption)
1126 | }
1127 |
1128 | func profile2Option(profile *Vmess) (interface{}, error) {
1129 | if profile.Protocol == v2ray.VMESS {
1130 | aidRaw, _ := json.Marshal(profile.Aid)
1131 | portRaw, _ := json.Marshal(profile.Port)
1132 | c := &lconfig.VmessConfig{
1133 | Add: profile.Add,
1134 | Aid: aidRaw,
1135 | Host: profile.Host,
1136 | ID: profile.ID,
1137 | Net: profile.Net,
1138 | Path: profile.Path,
1139 | Port: portRaw,
1140 | TLS: profile.TLS,
1141 | Type: profile.Type,
1142 | Security: profile.Security,
1143 | ServerName: profile.VmessOptions.ServerName,
1144 | }
1145 | return lconfig.VmessConfigToVmessOption(c)
1146 | }
1147 | if profile.Protocol == v2ray.SHADOWSOCKS {
1148 | opt := &loutbound.ShadowSocksOption{
1149 | Server: profile.Add,
1150 | Port: profile.Port,
1151 | Password: profile.ID,
1152 | Cipher: profile.Security,
1153 | }
1154 | return opt, nil
1155 | }
1156 | return nil, newError("not support protocol")
1157 | }
1158 |
1159 | type latencyResult struct {
1160 | elapsed int64
1161 | err error
1162 | }
1163 |
--------------------------------------------------------------------------------
/dns/dns.go:
--------------------------------------------------------------------------------
1 | package dns
2 |
3 | import (
4 | "errors"
5 | "math/rand"
6 | "net"
7 | "time"
8 |
9 | "golang.org/x/net/dns/dnsmessage"
10 | )
11 |
12 | var (
13 | errLameReferral = errors.New("lame referral")
14 | errCannotUnmarshalDNSMessage = errors.New("cannot unmarshal DNS message")
15 | errCannotMarshalDNSMessage = errors.New("cannot marshal DNS message")
16 | errServerMisbehaving = errors.New("server misbehaving")
17 | errInvalidDNSResponse = errors.New("invalid DNS response")
18 | errNoAnswerFromDNSServer = errors.New("no answer from DNS server")
19 | errServerTemporarlyMisbehaving = errors.New("server misbehaving")
20 | errNoSuchHost = errors.New("No Such Host")
21 | )
22 |
23 | func newRequest(q dnsmessage.Question) (id uint16, udpReq, tcpReq []byte, err error) {
24 | id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
25 | b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true})
26 | b.EnableCompression()
27 | if err := b.StartQuestions(); err != nil {
28 | return 0, nil, nil, err
29 | }
30 | if err := b.Question(q); err != nil {
31 | return 0, nil, nil, err
32 | }
33 | tcpReq, err = b.Finish()
34 | udpReq = tcpReq[2:]
35 | l := len(tcpReq) - 2
36 | tcpReq[0] = byte(l >> 8)
37 | tcpReq[1] = byte(l)
38 | return id, udpReq, tcpReq, err
39 | }
40 |
41 | func dnsPacketRoundTrip(c net.Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
42 | if _, err := c.Write(b); err != nil {
43 | return dnsmessage.Parser{}, dnsmessage.Header{}, err
44 | }
45 | b = make([]byte, 512) // see RFC 1035
46 | for {
47 | n, err := c.Read(b)
48 | if err != nil {
49 | return dnsmessage.Parser{}, dnsmessage.Header{}, err
50 | }
51 | var p dnsmessage.Parser
52 | // Ignore invalid responses as they may be malicious
53 | // forgery attempts. Instead continue waiting until
54 | // timeout. See golang.org/issue/13281.
55 | h, err := p.Start(b[:n])
56 | if err != nil {
57 | continue
58 | }
59 | q, err := p.Question()
60 | if err != nil || !checkResponse(id, query, h, q) {
61 | continue
62 | }
63 | return p, h, nil
64 | }
65 | }
66 |
67 | func checkResponse(reqID uint16, reqQues dnsmessage.Question, respHdr dnsmessage.Header, respQues dnsmessage.Question) bool {
68 | if !respHdr.Response {
69 | return false
70 | }
71 | if reqID != respHdr.ID {
72 | return false
73 | }
74 | if reqQues.Type != respQues.Type || reqQues.Class != respQues.Class || !equalASCIIName(reqQues.Name, respQues.Name) {
75 | return false
76 | }
77 | return true
78 | }
79 |
80 | func equalASCIIName(x, y dnsmessage.Name) bool {
81 | if x.Length != y.Length {
82 | return false
83 | }
84 | for i := 0; i < int(x.Length); i++ {
85 | a := x.Data[i]
86 | b := y.Data[i]
87 | if 'A' <= a && a <= 'Z' {
88 | a += 0x20
89 | }
90 | if 'A' <= b && b <= 'Z' {
91 | b += 0x20
92 | }
93 | if a != b {
94 | return false
95 | }
96 | }
97 | return true
98 | }
99 |
100 | func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header) error {
101 | if h.RCode == dnsmessage.RCodeNameError {
102 | return errNoSuchHost
103 | }
104 |
105 | _, err := p.AnswerHeader()
106 | if err != nil && err != dnsmessage.ErrSectionDone {
107 | return errCannotUnmarshalDNSMessage
108 | }
109 |
110 | // libresolv continues to the next server when it receives
111 | // an invalid referral response. See golang.org/issue/15434.
112 | if h.RCode == dnsmessage.RCodeSuccess && !h.Authoritative && !h.RecursionAvailable && err == dnsmessage.ErrSectionDone {
113 | return errLameReferral
114 | }
115 |
116 | if h.RCode != dnsmessage.RCodeSuccess && h.RCode != dnsmessage.RCodeNameError {
117 | // None of the error codes make sense
118 | // for the query we sent. If we didn't get
119 | // a name error and we didn't get success,
120 | // the server is behaving incorrectly or
121 | // having temporary trouble.
122 | if h.RCode == dnsmessage.RCodeServerFailure {
123 | return errServerTemporarlyMisbehaving
124 | }
125 | return errServerMisbehaving
126 | }
127 |
128 | return nil
129 | }
130 |
131 | func skipToAnswer(p *dnsmessage.Parser, qtype dnsmessage.Type) error {
132 | for {
133 | h, err := p.AnswerHeader()
134 | if err == dnsmessage.ErrSectionDone {
135 | return errNoSuchHost
136 | }
137 | if err != nil {
138 | return errCannotUnmarshalDNSMessage
139 | }
140 | if h.Type == qtype {
141 | return nil
142 | }
143 | if err := p.SkipAnswer(); err != nil {
144 | return errCannotUnmarshalDNSMessage
145 | }
146 | }
147 | }
148 |
149 | func parseMsg(p dnsmessage.Parser) ([]net.IPAddr, error) {
150 | var lastErr error
151 | var addrs []net.IPAddr
152 | var cname dnsmessage.Name
153 | loop:
154 | for {
155 | h, err := p.AnswerHeader()
156 | if err != nil && err != dnsmessage.ErrSectionDone {
157 | lastErr = errors.New("cannot marshal DNS message")
158 | }
159 | if err != nil {
160 | break
161 | }
162 | switch h.Type {
163 | case dnsmessage.TypeA:
164 | a, err := p.AResource()
165 | if err != nil {
166 | lastErr = errCannotMarshalDNSMessage
167 | break loop
168 | }
169 | addrs = append(addrs, net.IPAddr{IP: net.IP(a.A[:])})
170 |
171 | case dnsmessage.TypeAAAA:
172 | aaaa, err := p.AAAAResource()
173 | if err != nil {
174 | lastErr = errCannotMarshalDNSMessage
175 | break loop
176 | }
177 | addrs = append(addrs, net.IPAddr{IP: net.IP(aaaa.AAAA[:])})
178 |
179 | default:
180 | if err := p.SkipAnswer(); err != nil {
181 | lastErr = errCannotMarshalDNSMessage
182 | break loop
183 | }
184 | continue
185 | }
186 | if cname.Length == 0 && h.Name.Length != 0 {
187 | cname = h.Name
188 | }
189 | }
190 | return addrs, lastErr
191 | }
192 |
193 | // https://golang.org/src/net/dnsclient_unix.go
194 | // TODO: multiple server
195 | func LookupIP(name string, dnsServer string) ([]net.IPAddr, error) {
196 | // qtypes := [...]dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA}
197 | qtype := dnsmessage.TypeA
198 | n, err := dnsmessage.NewName(name)
199 | if err != nil {
200 | return nil, errCannotMarshalDNSMessage
201 | }
202 | q := dnsmessage.Question{
203 | Name: n,
204 | Type: qtype,
205 | Class: dnsmessage.ClassINET,
206 | }
207 | id, udpReq, _, err := newRequest(q)
208 | if err != nil {
209 | return nil, errCannotMarshalDNSMessage
210 | }
211 | dialer := net.Dialer{Timeout: 10 * time.Second}
212 | // dialer.DialContext()
213 | c, err := dialer.Dial("udp", dnsServer)
214 | if err != nil {
215 | return nil, err
216 | }
217 | if _, err := c.Write(udpReq); err != nil {
218 | return nil, err
219 | }
220 | p, h, err := dnsPacketRoundTrip(c, id, q, udpReq)
221 | c.Close()
222 | if err != nil {
223 | return nil, err
224 | }
225 | if err := p.SkipQuestion(); err != dnsmessage.ErrSectionDone {
226 | return nil, errInvalidDNSResponse
227 | }
228 | // if h.Truncated { // see RFC 5966
229 | // continue
230 | // }
231 | if err := checkHeader(&p, h); err != nil {
232 | return nil, err
233 | }
234 | err = skipToAnswer(&p, qtype)
235 | if err != nil {
236 | return nil, err
237 | }
238 | // parse response
239 | return parseMsg(p)
240 | }
241 |
--------------------------------------------------------------------------------
/dns/dns_test.go:
--------------------------------------------------------------------------------
1 | package dns
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestLookupIP(t *testing.T) {
9 |
10 | addrs, err := LookupIP("google.com.", "8.8.8.8:53")
11 | if err != nil {
12 | t.Error(err)
13 | }
14 | for _, addr := range addrs {
15 | t.Log(fmt.Printf("latency: %s", addr.IP.String()))
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/features/feature.go:
--------------------------------------------------------------------------------
1 | package features
2 |
--------------------------------------------------------------------------------
/features/shadowsocks.go:
--------------------------------------------------------------------------------
1 | package features
2 |
3 | type Shadowsocks struct {
4 | Add string
5 | Port int
6 | Password string
7 | Method string
8 | VmessOptions
9 | }
10 |
11 | func NewShadowsocks(Add string, Port int, Password string, Method string, opt []byte) *Shadowsocks {
12 | options := NewVmessOptions(opt)
13 | return &Shadowsocks{
14 | Add: Add,
15 | Port: Port,
16 | Password: Password,
17 | Method: Method,
18 | VmessOptions: options,
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/features/trojan.go:
--------------------------------------------------------------------------------
1 | package features
2 |
3 | type Trojan struct {
4 | Add string
5 | Port int
6 | Password string
7 | SNI string
8 | SkipCertVerify bool
9 | Net string
10 | Path string // ws path
11 | Host string // ws host / http host
12 | VmessOptions
13 | }
14 |
15 | func NewTrojan(
16 | Add string,
17 | Port int,
18 | Password string,
19 | SNI string,
20 | SkipCertVerify bool,
21 | Net string,
22 | Path string,
23 | Host string,
24 | opt []byte) *Trojan {
25 | options := NewVmessOptions(opt)
26 | return &Trojan{
27 | Add: Add,
28 | Port: Port,
29 | Password: Password,
30 | SNI: SNI,
31 | SkipCertVerify: SkipCertVerify,
32 | Net: Net,
33 | Path: Path,
34 | Host: Host,
35 | VmessOptions: options,
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/features/vless.go:
--------------------------------------------------------------------------------
1 | package features
2 |
3 | type Vless struct {
4 | TLS string
5 | Add string
6 | Port int
7 | Net string
8 | ID string
9 | Type string // headerType
10 | Security string // VlessUser.Security
11 | Encryption string // VlessUser.encryption
12 | Flow string // VlessUser.flow
13 | Protocol string
14 | Path string // ws path
15 | Host string // ws host / http host
16 | SNI string // tls sni
17 | VmessOptions
18 | }
19 |
20 | func NewVless(Add string, Port int, ID string, TLS string, HeaderType string, Encryption string, Net string, Flow string, Security string, Path string, Host string, SNI string, opt []byte) *Vless {
21 | options := NewVmessOptions(opt)
22 | return &Vless{
23 | TLS: TLS,
24 | Add: Add,
25 | Port: Port,
26 | Net: Net,
27 | ID: ID,
28 | Type: HeaderType,
29 | Encryption: Encryption,
30 | Flow: Flow,
31 | Security: Security,
32 | Path: Path,
33 | Host: Host,
34 | SNI: SNI,
35 | Protocol: "vless",
36 | VmessOptions: options,
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/features/vmess.go:
--------------------------------------------------------------------------------
1 | package features
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "net"
8 |
9 | vnet "github.com/xtls/xray-core/common/net"
10 | vcore "github.com/xtls/xray-core/core"
11 | )
12 |
13 | type VmessOptions struct {
14 | UseIPv6 bool `json:"useIPv6"`
15 | Loglevel string `json:"logLevel"`
16 | RouteMode int `json:"routeMode"` // for SSRRAY
17 | DisableDNSCache bool `json:"disableDNSCache"` // for SSRRAY
18 | EnableSniffing bool `json:"enableSniffing"`
19 | DNS string `json:"dns"` // DNS Config
20 | AllowInsecure bool `json:"allowInsecure"`
21 | Mux int `json:"mux"`
22 | LocalPort int `json:"localPort"`
23 | ServerName string `json:"serverName"`
24 | }
25 |
26 | func NewVmessOptions(opt []byte) VmessOptions {
27 | var options VmessOptions
28 | err := json.Unmarshal(opt, &options)
29 | if err != nil {
30 | options = VmessOptions{
31 | UseIPv6: false,
32 | Loglevel: "error",
33 | RouteMode: 0,
34 | EnableSniffing: true,
35 | DNS: "8.8.8.8:53,223.5.5.5:53",
36 | AllowInsecure: true,
37 | Mux: -1,
38 | }
39 | }
40 | if options.Mux < 1 {
41 | options.Mux = -1
42 | }
43 | return options
44 | }
45 |
46 | type Vmess struct {
47 | Host string
48 | Path string
49 | TLS string
50 | Add string
51 | Port int
52 | Aid int
53 | Net string
54 | ID string
55 | Type string // headerType
56 | Security string // vnext.Security
57 | Encryption string // VlessUser.encryption
58 | Flow string // VlessUser.flow
59 | SNI string // tls sni
60 | Protocol string
61 | VmessOptions
62 | Trojan *Trojan
63 | Shadowsocks *Shadowsocks
64 | }
65 |
66 | func NewVmess(Host string, Path string, TLS string, Add string, Port int, Aid int, Net string, ID string, Type string, Security string, opt []byte) *Vmess {
67 | var options VmessOptions = NewVmessOptions(opt)
68 | return &Vmess{
69 | Host: Host,
70 | Path: Path,
71 | TLS: TLS,
72 | Add: Add,
73 | Port: Port,
74 | Aid: Aid,
75 | Net: Net,
76 | ID: ID,
77 | Type: Type,
78 | Security: Security,
79 | Protocol: "vmess",
80 | VmessOptions: options,
81 | Trojan: nil,
82 | }
83 | }
84 |
85 | type VmessDialer struct {
86 | Instance *vcore.Instance
87 | }
88 |
89 | func (d *VmessDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
90 | dest, err := vnet.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
91 | if err != nil {
92 | return nil, err
93 | }
94 | return vcore.Dial(ctx, d.Instance, dest)
95 | }
96 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/xxf098/go-tun2socks-build
2 |
3 | go 1.21
4 |
5 | require (
6 | github.com/eycorsican/go-tun2socks v1.16.11
7 | github.com/kr/text v0.2.0 // indirect
8 | github.com/sagernet/sing v0.2.9
9 | // github.com/v2fly/v2ray-core/v4 v4.43.0
10 | github.com/xtls/xray-core v1.8.4
11 | github.com/xxf098/lite-proxy v0.0.0
12 | golang.org/x/mobile v0.0.0-20230818142238-7088062f872d
13 | golang.org/x/net v0.14.0
14 | )
15 |
16 | require (
17 | github.com/Dreamacro/go-shadowsocks2 v0.1.8 // indirect
18 | github.com/andybalholm/brotli v1.0.5 // indirect
19 | github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
20 | github.com/francoispqt/gojay v1.2.13 // indirect
21 | github.com/gaukas/godicttls v0.0.4 // indirect
22 | github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 // indirect
23 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
24 | github.com/gofrs/uuid v4.3.1+incompatible // indirect
25 | github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
26 | github.com/golang/mock v1.6.0 // indirect
27 | github.com/golang/protobuf v1.5.3 // indirect
28 | github.com/google/btree v1.1.2 // indirect
29 | github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
30 | github.com/gorilla/websocket v1.5.0 // indirect
31 | github.com/klauspost/compress v1.16.7 // indirect
32 | github.com/klauspost/cpuid/v2 v2.2.5 // indirect
33 | github.com/miekg/dns v1.1.55 // indirect
34 | github.com/onsi/ginkgo/v2 v2.12.0 // indirect
35 | github.com/pelletier/go-toml v1.9.5 // indirect
36 | github.com/pires/go-proxyproto v0.7.0 // indirect
37 | github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
38 | github.com/quic-go/quic-go v0.38.1 // indirect
39 | github.com/refraction-networking/utls v1.4.3 // indirect
40 | github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
41 | github.com/sagernet/sing-shadowsocks v0.2.4 // indirect
42 | github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c // indirect
43 | github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
44 | github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
45 | github.com/xtls/reality v0.0.0-20230828171259-e426190d57f6 // indirect
46 | go.uber.org/atomic v1.11.0 // indirect
47 | go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
48 | golang.org/x/crypto v0.12.0 // indirect
49 | golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect
50 | golang.org/x/image v0.2.0 // indirect
51 | golang.org/x/mod v0.12.0 // indirect
52 | golang.org/x/sync v0.3.0 // indirect
53 | golang.org/x/sys v0.11.0 // indirect
54 | golang.org/x/text v0.12.0 // indirect
55 | golang.org/x/time v0.3.0 // indirect
56 | golang.org/x/tools v0.12.1-0.20230818130535-1517d1a3ba60 // indirect
57 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
58 | google.golang.org/grpc v1.57.0 // indirect
59 | google.golang.org/protobuf v1.31.0 // indirect
60 | gopkg.in/yaml.v2 v2.4.0 // indirect
61 | gopkg.in/yaml.v3 v3.0.1 // indirect
62 | gvisor.dev/gvisor v0.0.0-20230822212503-5bf4e5f98744 // indirect
63 | lukechampine.com/blake3 v1.2.1 // indirect
64 | )
65 |
66 | // replace with v2ray-core path
67 | replace (
68 | // github.com/v2fly/v2ray-core/v4 v4.43.0 => ../v2ray-core
69 | github.com/xtls/xray-core v1.8.4 => ../xray-core
70 | // git clone https://github.com/xxf098/LiteSpeedTest.git lite-proxy
71 | github.com/xxf098/lite-proxy v0.0.0 => ../lite-proxy
72 | )
73 |
--------------------------------------------------------------------------------
/lite/tcp.go:
--------------------------------------------------------------------------------
1 | package lite
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io"
7 | "net"
8 |
9 | "github.com/eycorsican/go-tun2socks/core"
10 | "github.com/xtls/xray-core/common/bytespool"
11 | "github.com/xxf098/go-tun2socks-build/pool"
12 |
13 | N "github.com/xxf098/lite-proxy/common/net"
14 | C "github.com/xxf098/lite-proxy/constant"
15 | "github.com/xxf098/lite-proxy/outbound"
16 | "github.com/xxf098/lite-proxy/tunnel"
17 | )
18 |
19 | type tcpHandler struct {
20 | ctx context.Context
21 | client outbound.Dialer
22 | }
23 |
24 | // TODO: refactor
25 | func (h *tcpHandler) relay(lhs net.Conn, rhs net.Conn) {
26 | go func() {
27 | buf := bytespool.Alloc(pool.BufSize)
28 | _, err := io.CopyBuffer(N.WriteOnlyWriter{Writer: lhs}, N.ReadOnlyReader{Reader: rhs}, buf)
29 | if err != nil {
30 | fmt.Printf("relay: %v\n", err)
31 | }
32 | bytespool.Free(buf)
33 | lhs.Close()
34 | rhs.Close()
35 | }()
36 | buf := bytespool.Alloc(pool.BufSize)
37 | // io.CopyBuffer(lhs, rhs, buf)
38 | _, err := io.CopyBuffer(N.WriteOnlyWriter{Writer: rhs}, N.ReadOnlyReader{Reader: lhs}, buf)
39 | if err != nil {
40 | fmt.Printf("relay: %v\n", err)
41 | }
42 | bytespool.Free(buf)
43 | lhs.Close()
44 | rhs.Close()
45 | }
46 |
47 | func NewTCPHandler(ctx context.Context, client outbound.Dialer) core.TCPConnHandler {
48 | return &tcpHandler{
49 | ctx: ctx,
50 | client: client,
51 | }
52 | }
53 |
54 | func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error {
55 | addr, err := tunnel.NewAddressFromAddr(target.Network(), target.String())
56 | if err != nil {
57 | return err
58 | }
59 | meta := &C.Metadata{
60 | NetWork: C.TCP,
61 | Type: 0,
62 | SrcPort: "",
63 | DstPort: fmt.Sprintf("%d", addr.Port),
64 | DstIP: addr.IP,
65 | }
66 | c, err := h.client.DialContext(h.ctx, meta)
67 | if err != nil {
68 | return fmt.Errorf("dial V proxy connection failed: %v", err)
69 | }
70 | go h.relay(conn, c)
71 | return nil
72 | }
73 |
--------------------------------------------------------------------------------
/lite/udp.go:
--------------------------------------------------------------------------------
1 | package lite
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "net"
8 | "sync"
9 | "time"
10 |
11 | vsignal "github.com/xtls/xray-core/common/signal"
12 | vtask "github.com/xtls/xray-core/common/task"
13 |
14 | "github.com/eycorsican/go-tun2socks/common/log"
15 | "github.com/eycorsican/go-tun2socks/core"
16 | "github.com/xtls/xray-core/common/bytespool"
17 | "github.com/xxf098/go-tun2socks-build/pool"
18 |
19 | C "github.com/xxf098/lite-proxy/constant"
20 | "github.com/xxf098/lite-proxy/outbound"
21 | "github.com/xxf098/lite-proxy/tunnel"
22 | )
23 |
24 | type udpConnEntry struct {
25 | conn net.PacketConn
26 |
27 | // `ReadFrom` method of PacketConn given by V2Ray
28 | // won't return the correct remote address, we treat
29 | // all data receive from V2Ray are coming from the
30 | // same remote host, i.e. the `target` that passed
31 | // to `Connect`.
32 | target *net.UDPAddr
33 |
34 | updater vsignal.ActivityUpdater
35 | }
36 |
37 | type udpHandler struct {
38 | sync.Mutex
39 |
40 | ctx context.Context
41 | v outbound.Dialer
42 | conns map[core.UDPConn]*udpConnEntry
43 | timeout time.Duration // Maybe override by V2Ray local policies for some conns.
44 | }
45 |
46 | func (h *udpHandler) fetchInput(conn core.UDPConn) {
47 | h.Lock()
48 | c, ok := h.conns[conn]
49 | h.Unlock()
50 | if !ok {
51 | return
52 | }
53 |
54 | buf := bytespool.Alloc(pool.BufSize)
55 | defer bytespool.Free(buf)
56 |
57 | for {
58 | n, _, err := c.conn.ReadFrom(buf)
59 | if err != nil && n <= 0 {
60 | h.Close(conn)
61 | conn.Close()
62 | return
63 | }
64 | c.updater.Update()
65 | _, err = conn.WriteFrom(buf[:n], c.target)
66 | if err != nil {
67 | h.Close(conn)
68 | conn.Close()
69 | return
70 | }
71 | }
72 | }
73 |
74 | func NewUDPHandler(ctx context.Context, instance outbound.Dialer, timeout time.Duration) core.UDPConnHandler {
75 | return &udpHandler{
76 | ctx: ctx,
77 | v: instance,
78 | conns: make(map[core.UDPConn]*udpConnEntry, 16),
79 | timeout: timeout,
80 | }
81 | }
82 |
83 | func (h *udpHandler) Connect(conn core.UDPConn, target *net.UDPAddr) error {
84 | if target == nil {
85 | return errors.New("nil target is not allowed")
86 | }
87 | addr, err := tunnel.NewAddressFromAddr(target.Network(), target.String())
88 | if err != nil {
89 | return err
90 | }
91 | ctx, cancel := context.WithCancel(context.Background())
92 | meta := &C.Metadata{
93 | NetWork: C.UDP,
94 | Type: 0,
95 | SrcPort: "",
96 | DstPort: fmt.Sprintf("%d", addr.Port),
97 | DstIP: addr.IP,
98 | }
99 |
100 | pc, err := h.v.DialUDP(meta)
101 | if err != nil {
102 | cancel()
103 | return fmt.Errorf("dial V proxy connection failed: %v", err)
104 | }
105 | timer := vsignal.CancelAfterInactivity(ctx, cancel, h.timeout)
106 | h.Lock()
107 | h.conns[conn] = &udpConnEntry{
108 | conn: pc,
109 | target: target,
110 | updater: timer,
111 | }
112 | h.Unlock()
113 | fetchTask := func() error {
114 | h.fetchInput(conn)
115 | return nil
116 | }
117 | go func() {
118 | if err := vtask.Run(ctx, fetchTask); err != nil {
119 | pc.Close()
120 | }
121 | }()
122 | log.Infof("new proxy connection for target: %s:%s", target.Network(), target.String())
123 | return nil
124 | }
125 |
126 | func (h *udpHandler) ReceiveTo(conn core.UDPConn, data []byte, addr *net.UDPAddr) error {
127 | h.Lock()
128 | c, ok := h.conns[conn]
129 | h.Unlock()
130 |
131 | if ok {
132 | _, err := c.conn.WriteTo(data, addr)
133 | c.updater.Update()
134 | if err != nil {
135 | h.Close(conn)
136 | return fmt.Errorf("write remote failed: %v", err)
137 | }
138 | return nil
139 | } else {
140 | h.Close(conn)
141 | return fmt.Errorf("proxy connection %v->%v does not exists", conn.LocalAddr(), addr)
142 | }
143 | }
144 |
145 | func (h *udpHandler) Close(conn core.UDPConn) {
146 | h.Lock()
147 | defer h.Unlock()
148 |
149 | if c, found := h.conns[conn]; found {
150 | c.conn.Close()
151 | }
152 | delete(h.conns, conn)
153 | }
154 |
--------------------------------------------------------------------------------
/log.go:
--------------------------------------------------------------------------------
1 | package tun2socks
2 |
3 | import (
4 | alog "github.com/xtls/xray-core/app/log"
5 | "github.com/xtls/xray-core/common"
6 | vcommonlog "github.com/xtls/xray-core/common/log"
7 | )
8 |
9 | type LogService interface {
10 | WriteLog(s string) error
11 | }
12 |
13 | type logWriter struct {
14 | logger *LogService
15 | }
16 |
17 | func (w *logWriter) Write(s string) error {
18 | (*w.logger).WriteLog(s)
19 | return nil
20 | }
21 |
22 | func (w *logWriter) Close() error {
23 | return nil
24 | }
25 |
26 | func createLogWriter(logService LogService) vcommonlog.WriterCreator {
27 | return func() vcommonlog.Writer {
28 | return &logWriter{
29 | logger: &logService,
30 | }
31 | }
32 | }
33 |
34 | func registerLogService(logService LogService) {
35 | if logService != nil {
36 | common.Must(alog.RegisterHandlerCreator(alog.LogType_Console, func(lt alog.LogType,
37 | options alog.HandlerCreatorOptions) (vcommonlog.Handler, error) {
38 | return vcommonlog.NewLogger(createLogWriter(logService)), nil
39 | }))
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ping/download.go:
--------------------------------------------------------------------------------
1 | package ping
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "strconv"
7 | "time"
8 |
9 | "github.com/xxf098/lite-proxy/download"
10 | "github.com/xxf098/lite-proxy/web"
11 | "github.com/xxf098/lite-proxy/web/render"
12 | )
13 |
14 | func testAll(ctx context.Context, links []string, max int, trafficChan chan<- int64) (chan render.Node, error) {
15 |
16 | p := web.ProfileTest{
17 | Writer: nil,
18 | MessageType: web.ALLTEST,
19 | Links: links,
20 | Options: &web.ProfileTestOptions{
21 | GroupName: "Default",
22 | SpeedTestMode: "all",
23 | PingMethod: "googleping",
24 | SortMethod: "none",
25 | Concurrency: max,
26 | TestMode: 2,
27 | Timeout: 15 * time.Second,
28 | Language: "en",
29 | FontSize: 24,
30 | },
31 | }
32 |
33 | return p.TestAll(ctx, trafficChan)
34 | }
35 |
36 | func RenderDownloadLinksSpeed(links []string, max int, fontPath string, pngPath string, language string, urlGroup string, testInfoChan chan<- TestResult) error {
37 | defer func() {
38 | if testInfoChan != nil {
39 | close(testInfoChan)
40 | }
41 | }()
42 | ctx := context.Background()
43 | trafficChan := make(chan int64)
44 | var start = time.Now()
45 | nodeChan, err := testAll(ctx, links, max, trafficChan)
46 | if err != nil {
47 | return err
48 | }
49 | count := 0
50 | successCount := 0
51 | linkCount := len(links)
52 | var sum int64
53 | nodes := make(render.Nodes, linkCount)
54 | for count < linkCount {
55 | select {
56 | case traffic := <-trafficChan:
57 | if traffic > 0 {
58 | sum += traffic
59 | fmt.Printf("traffic: %s\n", download.ByteCountIECTrim(sum))
60 | }
61 | testResult := TestResult{
62 | Result: traffic,
63 | Protocol: PROTOCOL_TRAFFIC,
64 | }
65 | if testInfoChan != nil {
66 | testInfoChan <- testResult
67 | }
68 | case node := <-nodeChan:
69 | node.Group = urlGroup
70 | nodes[node.Id] = node
71 | if node.IsOk {
72 | successCount += 1
73 | }
74 |
75 | p, _ := strconv.Atoi(node.Ping)
76 | if testInfoChan != nil {
77 | speedResult := TestResult{
78 | Result: node.MaxSpeed,
79 | Elapse: int64(p),
80 | Index: node.Id,
81 | Protocol: PROTOCOL_SPEED,
82 | }
83 |
84 | testInfoChan <- speedResult
85 | }
86 | fmt.Printf("index: %d, elapse: %s, avg: %s, max: %s\n", node.Id, node.Ping, download.ByteCountIEC(node.AvgSpeed), download.ByteCountIEC(node.MaxSpeed))
87 | count += 1
88 | }
89 | }
90 | close(nodeChan)
91 |
92 | duration := web.FormatDuration(time.Since(start))
93 | options := render.NewTableOptions(40, 30, 0.5, 0.5, 24, 0.5, fontPath, language, "original", "Asia/Shanghai", []byte{})
94 | nodes.Sort("rspeed")
95 | table, err := render.NewTableWithOption(nodes, &options)
96 | if err != nil {
97 | return err
98 | }
99 | msg := table.FormatTraffic(download.ByteCountIECTrim(sum), duration, fmt.Sprintf("%d/%d", successCount, linkCount))
100 | table.Draw(pngPath, msg)
101 | return nil
102 | }
103 |
104 | func RenderDownloadLinksSpeedAndroid(links []string, max int, fontPath string, pngPath string, language string, urlGroup string) <-chan TestResult {
105 | testInfoChan := make(chan TestResult)
106 | go func(c chan<- TestResult) {
107 | RenderDownloadLinksSpeed(links, max, fontPath, pngPath, language, urlGroup, c)
108 | }(testInfoChan)
109 | return testInfoChan
110 | }
111 |
--------------------------------------------------------------------------------
/ping/ping.go:
--------------------------------------------------------------------------------
1 | package ping
2 |
3 | import (
4 | "sync"
5 | "time"
6 |
7 | "github.com/xxf098/lite-proxy/config"
8 | "github.com/xxf098/lite-proxy/download"
9 | "github.com/xxf098/lite-proxy/request"
10 | )
11 |
12 | const PROTOCOL_SPEED = "speed"
13 | const PROTOCOL_TRAFFIC = "traffic"
14 |
15 | type TestResult struct {
16 | Result int64
17 | Elapse int64
18 | Server string
19 | Port int
20 | Index int
21 | Err error
22 | Protocol string
23 | }
24 |
25 | type RunFunc func(int, string, chan<- TestResult) (bool, error)
26 |
27 | func BatchTestLinks(links []string, max int, runFuncs []RunFunc) <-chan TestResult {
28 | if max < 1 {
29 | max = 5
30 | }
31 | resultChan := make(chan TestResult)
32 | go func(c chan<- TestResult) {
33 | maxChan := make(chan bool, max)
34 | var wg sync.WaitGroup
35 | for i, link := range links {
36 | wg.Add(1)
37 | go func(index int, link string) {
38 | defer wg.Done()
39 | maxChan <- true
40 | for _, runFunc := range runFuncs {
41 | next, _ := runFunc(index, link, c)
42 | if !next {
43 | break
44 | }
45 | }
46 | <-maxChan
47 | }(i, link)
48 | }
49 | wg.Wait()
50 | close(c)
51 | }(resultChan)
52 | return resultChan
53 | }
54 |
55 | func runVmess(index int, link string, c chan<- TestResult) (bool, error) {
56 | option, err := config.VmessLinkToVmessConfigIP(link, false)
57 | if err != nil {
58 | return true, err
59 | }
60 | n := option.Net
61 | if n != "" && n != "tcp" && n != "ws" && n != "http" && n != "h2" {
62 | return true, nil
63 | }
64 | return runLite(index, link, "vmess", c)
65 | }
66 |
67 | func runTrojan(index int, link string, c chan<- TestResult) (bool, error) {
68 | _, err := config.TrojanLinkToTrojanOption(link)
69 | if err != nil {
70 | return true, err
71 | }
72 | return runLite(index, link, "trojan", c)
73 | }
74 |
75 | func runShadowSocks(index int, link string, c chan<- TestResult) (bool, error) {
76 | _, err := config.SSLinkToSSOption(link)
77 | if err != nil {
78 | return true, err
79 | }
80 | return runLite(index, link, "ss", c)
81 | }
82 |
83 | func runLite(index int, link string, protocol string, c chan<- TestResult) (bool, error) {
84 | opt := request.PingOption{
85 | Attempts: 1,
86 | TimeOut: 2000 * time.Millisecond,
87 | }
88 | elapse, err := request.PingLinkInternal(link, opt)
89 | result := TestResult{
90 | Result: elapse,
91 | Index: index,
92 | Err: err,
93 | Protocol: protocol,
94 | }
95 | c <- result
96 | return false, err
97 | }
98 |
99 | func PingLinksLatency(links []string, max int, runPings []RunFunc) <-chan TestResult {
100 | runs := append([]RunFunc{runVmess, runTrojan, runShadowSocks}, runPings...)
101 | return BatchTestLinks(links, max, runs)
102 | }
103 |
104 | func runDownload(index int, link string, c chan<- TestResult) (bool, error) {
105 | trafficChan := make(chan int64, 1)
106 | defer close(trafficChan)
107 | go func() {
108 | for {
109 | select {
110 | case s, ok := <-trafficChan:
111 | if !ok || s < 0 {
112 | return
113 | }
114 | r := TestResult{
115 | Result: s,
116 | Index: index,
117 | Err: nil,
118 | Protocol: PROTOCOL_TRAFFIC,
119 | }
120 | c <- r
121 | }
122 | }
123 | }()
124 | speed, err := download.Download(link, 12*time.Second, 12*time.Second, trafficChan, nil)
125 | result := TestResult{
126 | Result: speed,
127 | Index: index,
128 | Err: err,
129 | Protocol: PROTOCOL_SPEED,
130 | }
131 | c <- result
132 | return false, err
133 | }
134 |
135 | func DownloadLinksSpeed(links []string, max int) <-chan TestResult {
136 | runs := append([]RunFunc{runDownload})
137 | return BatchTestLinks(links, max, runs)
138 | }
139 |
--------------------------------------------------------------------------------
/ping/tcp.go:
--------------------------------------------------------------------------------
1 | package ping
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "time"
7 | )
8 |
9 | type PingResult struct {
10 | latency int64
11 | err error
12 | }
13 |
14 | func (pingResult PingResult) Get() (int64, error) {
15 | return pingResult.latency, pingResult.err
16 | }
17 |
18 | type TCPPing struct {
19 | host string
20 | port int
21 | done chan PingResult
22 | }
23 |
24 | func NewTCPPing(host string, port int) *TCPPing {
25 | tcpping := TCPPing{
26 | host: host,
27 | port: port,
28 | done: make(chan PingResult),
29 | }
30 | return &tcpping
31 | }
32 | func (tcpping TCPPing) Start() <-chan PingResult {
33 | go func() {
34 | totalCount, successCount := 0, 0
35 | latency := int64(0)
36 | result := PingResult{latency: 0, err: nil}
37 | for {
38 | if totalCount >= 2 || successCount >= 2 {
39 | tcpping.done <- result
40 | return
41 | }
42 | start := time.Now()
43 | timeout := 1 * time.Second
44 | if totalCount == 0 {
45 | timeout = 2 * time.Second
46 | }
47 | conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", tcpping.host, tcpping.port), timeout)
48 | if err != nil {
49 | totalCount++
50 | result = PingResult{latency: 0, err: err}
51 | continue
52 | }
53 | elapsed := time.Since(start)
54 | latency = elapsed.Milliseconds()
55 | conn.Close()
56 | successCount++
57 | totalCount++
58 | result = PingResult{latency: latency, err: nil}
59 | }
60 | }()
61 | return tcpping.done
62 | }
63 |
--------------------------------------------------------------------------------
/ping/tcp_test.go:
--------------------------------------------------------------------------------
1 | package ping
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func TestGenerateVmessConfig(t *testing.T) {
9 | tcpping := NewTCPPing("167.179.109.28", 80)
10 | latency := <-tcpping.Start()
11 | t.Log(fmt.Printf("latency: %d", latency))
12 | }
13 |
--------------------------------------------------------------------------------
/pool/alloc.go:
--------------------------------------------------------------------------------
1 | package pool
2 |
3 | import (
4 | "errors"
5 | "math/bits"
6 | "sync"
7 | )
8 |
9 | var defaultAllocator *Allocator
10 |
11 | func init() {
12 | defaultAllocator = NewAllocator()
13 | }
14 |
15 | // Allocator for incoming frames, optimized to prevent overwriting after zeroing
16 | type Allocator struct {
17 | buffers []sync.Pool
18 | }
19 |
20 | // NewAllocator initiates a []byte allocator for frames less than 65536 bytes,
21 | // the waste(memory fragmentation) of space allocation is guaranteed to be
22 | // no more than 50%.
23 | func NewAllocator() *Allocator {
24 | alloc := new(Allocator)
25 | alloc.buffers = make([]sync.Pool, 16) // 1B -> 64K
26 | for k := range alloc.buffers {
27 | i := k
28 | alloc.buffers[k].New = func() interface{} {
29 | return make([]byte, 1< 65536 {
38 | return nil
39 | }
40 |
41 | bits := msb(size)
42 | if size == 1< 65536 || cap(buf) != 1< 0 {
65 | nw, ew := dst.Write(buf[0:nr])
66 | if ew != nil {
67 | err = ew
68 | }
69 | if nr != nw {
70 | err = io.ErrShortWrite
71 | }
72 | }
73 | if err != nil && err != io.EOF {
74 | break
75 | }
76 | }
77 | }
78 |
79 | func (tunDev *Interface) Stop() {
80 | err := tunDev.Close()
81 | if err != nil {
82 | log.Printf("close tun: %v", err)
83 | }
84 | tunDev.StopCh <- true
85 | }
86 |
87 | func NewBytes(size int) []byte {
88 | // if size <= BufSize {
89 | // return pool.Get().([]byte)
90 | // } else {
91 | // return make([]byte, size)
92 | // }
93 | return defaultAllocator.Get(size)
94 | }
95 |
96 | func FreeBytes(b []byte) {
97 | // b = b[0:cap(b)] // restore slice
98 | // if cap(b) >= BufSize {
99 | // pool.Put(b)
100 | // }
101 | _ = defaultAllocator.Put(b)
102 | }
103 |
104 | // func init() {
105 | // pool = &sync.Pool{
106 | // New: func() interface{} {
107 |
108 | // return make([]byte, BufSize)
109 | // },
110 | // }
111 | // }
112 |
--------------------------------------------------------------------------------
/runner/runner.go:
--------------------------------------------------------------------------------
1 | package runner
2 |
3 | import (
4 | "sync"
5 | "sync/atomic"
6 | )
7 |
8 | // S is a function that will return true if the
9 | // goroutine should stop executing.
10 | type S func() bool
11 |
12 | const (
13 | FALSE int32 = 0
14 | TRUE int32 = 1
15 | )
16 |
17 | // Go executes the function in a goroutine and returns a
18 | // Task capable of stopping the execution.
19 | func Go(fn func(S) error) *Task {
20 | var run, stop int32
21 | t := &Task{
22 | stopChan: make(chan struct{}),
23 | running: &run,
24 | shouldStop: &stop,
25 | }
26 | atomic.StoreInt32(t.shouldStop, FALSE)
27 | atomic.StoreInt32(t.running, TRUE)
28 | go func() {
29 | // call the target function
30 | err := fn(func() bool {
31 | // this is the shouldStop() function available to the
32 | // target function
33 | shouldStop := atomic.LoadInt32(t.shouldStop)
34 | return shouldStop == TRUE
35 | })
36 | t.err.Store(err)
37 | atomic.StoreInt32(t.running, FALSE)
38 | var closeOnce sync.Once
39 |
40 | closeOnce.Do(t.closeOnceBody)
41 | }()
42 | return t
43 | }
44 |
45 | // Task represents an interruptable goroutine.
46 | type Task struct {
47 | ID string
48 | stopChan chan struct{}
49 | shouldStop *int32
50 | running *int32
51 | err atomic.Value
52 | }
53 |
54 | func (t *Task) closeOnceBody() {
55 | // channel closing is actually a sending operation
56 | close(t.stopChan)
57 | }
58 |
59 | // Stop tells the goroutine to stop.
60 | func (t *Task) Stop() {
61 | // When task is stopped from a different go-routine other than the one
62 | // that actually started it.
63 | atomic.StoreInt32(t.shouldStop, TRUE)
64 | }
65 |
66 | // StopChan gets the stop channel for this task.
67 | // Reading from this channel will block while the task is running, and will
68 | // unblock once the task has stopped (because the channel gets closed).
69 | func (t *Task) StopChan() <-chan struct{} {
70 | return t.stopChan
71 | }
72 |
73 | // Running gets whether the goroutine is
74 | // running or not.
75 | func (t *Task) Running() bool {
76 | running := atomic.LoadInt32(t.running)
77 | return running == TRUE
78 | }
79 |
80 | // Err gets the error returned by the goroutine.
81 | func (t *Task) Err() error {
82 | err := t.err.Load()
83 | return err.(error)
84 | }
85 |
86 | func CheckAndStop(task *Task) {
87 | if task != nil && task.Running() {
88 | task.Stop()
89 | <-task.StopChan()
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/settings/shadowsocks.go:
--------------------------------------------------------------------------------
1 | package settings
2 |
3 | type ShadowsocksServerTarget struct {
4 | Address string `json:"address"`
5 | Port uint16 `json:"port"`
6 | Password string `json:"password"`
7 | Method string `json:"method"`
8 | Email string `json:"email"`
9 | Level byte `json:"level"`
10 | OTA bool `json:"ota"`
11 | }
12 |
13 | type ShadowsocksOutboundsSettings struct {
14 | Servers []*ShadowsocksServerTarget `json:"servers"`
15 | }
16 |
--------------------------------------------------------------------------------
/settings/trojan.go:
--------------------------------------------------------------------------------
1 | package settings
2 |
3 | type TrojanServerTarget struct {
4 | Address string `json:"address"`
5 | Port uint16 `json:"port"`
6 | Password string `json:"password"`
7 | Email string `json:"email"`
8 | Level byte `json:"level"`
9 | }
10 |
11 | type TrojanOutboundsSettings struct {
12 | Servers []*TrojanServerTarget `json:"servers"`
13 | }
14 |
--------------------------------------------------------------------------------
/tun2socks.go:
--------------------------------------------------------------------------------
1 | package tun2socks
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "encoding/json"
7 | "errors"
8 | "fmt"
9 | "io"
10 | "log"
11 | "net"
12 | "os"
13 | "runtime"
14 | "runtime/debug"
15 | "strings"
16 | "syscall"
17 | "time"
18 |
19 | vproxyman "github.com/xtls/xray-core/app/proxyman"
20 | vbytespool "github.com/xtls/xray-core/common/bytespool"
21 | verrors "github.com/xtls/xray-core/common/errors"
22 | vnet "github.com/xtls/xray-core/common/net"
23 | v2filesystem "github.com/xtls/xray-core/common/platform/filesystem"
24 | vcore "github.com/xtls/xray-core/core"
25 | v2stats "github.com/xtls/xray-core/features/stats"
26 | "github.com/xtls/xray-core/infra/conf"
27 |
28 | // "github.com/xtls/xray-core/infra/conf/cfgcommon"
29 | v2serial "github.com/xtls/xray-core/infra/conf/serial"
30 | vinternet "github.com/xtls/xray-core/transport/internet"
31 | mobasset "golang.org/x/mobile/asset"
32 |
33 | xbytespool "github.com/xtls/xray-core/common/bytespool"
34 | xsession "github.com/xtls/xray-core/common/session"
35 | xcore "github.com/xtls/xray-core/core"
36 | x2stats "github.com/xtls/xray-core/features/stats"
37 | xinternet "github.com/xtls/xray-core/transport/internet"
38 |
39 | "github.com/eycorsican/go-tun2socks/core"
40 | "github.com/xxf098/go-tun2socks-build/features"
41 | "github.com/xxf098/go-tun2socks-build/lite"
42 | "github.com/xxf098/go-tun2socks-build/ping"
43 | "github.com/xxf098/go-tun2socks-build/pool"
44 | "github.com/xxf098/go-tun2socks-build/runner"
45 | "github.com/xxf098/go-tun2socks-build/v2ray"
46 | "github.com/xxf098/go-tun2socks-build/xray"
47 |
48 | "github.com/sagernet/sing/common/control"
49 | ldns "github.com/xxf098/lite-proxy/dns"
50 | "github.com/xxf098/lite-proxy/download"
51 | loutbound "github.com/xxf098/lite-proxy/outbound"
52 | "github.com/xxf098/lite-proxy/request"
53 | lDialer "github.com/xxf098/lite-proxy/transport/dialer"
54 | "github.com/xxf098/lite-proxy/web"
55 | )
56 |
57 | var localDNS = "223.5.5.5:53"
58 | var err error
59 | var lwipStack core.LWIPStack
60 | var x *xcore.Instance
61 | var v *vcore.Instance
62 | var l loutbound.Dialer
63 | var mtuUsed int
64 | var lwipTUNDataPipeTask *runner.Task
65 | var updateStatusPipeTask *runner.Task
66 | var tunDev *pool.Interface
67 | var lwipWriter io.Writer
68 | var statsManager v2stats.Manager
69 | var xStatsManager x2stats.Manager
70 | var isStopped = false
71 |
72 | const (
73 | v2Asset = "v2ray.location.asset"
74 | )
75 |
76 | type errPathObjHolder struct{}
77 |
78 | // const (
79 | // VMESS string = "vmess"
80 | // VLESS string = "vless"
81 | // TROJAN string = "trojan"
82 | // SHADOWSOCKS string = "shadowsocks"
83 | // )
84 |
85 | func newError(values ...interface{}) *verrors.Error {
86 | return verrors.New(values...).WithPathObj(errPathObjHolder{})
87 | }
88 |
89 | type VmessOptions features.VmessOptions
90 | type Trojan features.Trojan
91 | type Vmess features.Vmess
92 | type Vless features.Vless
93 | type Shadowsocks features.Shadowsocks
94 |
95 | func NewTrojan(Add string, Port int, Password string, SNI string, SkipCertVerify bool, Net string, Path string, Host string, opt []byte) *Trojan {
96 | t := Trojan(*features.NewTrojan(Add, Port, Password, SNI, SkipCertVerify, Net, Path, Host, opt))
97 | return &t
98 | }
99 |
100 | func (t *Trojan) toVmess() *Vmess {
101 | trojan := features.Trojan(*t)
102 | return &Vmess{
103 | Protocol: v2ray.TROJAN,
104 | Trojan: &trojan,
105 | VmessOptions: t.VmessOptions,
106 | }
107 | }
108 |
109 | func NewVless(Add string, Port int, ID string, TLS string, HeaderType string, Encryption string, Net string, Flow string, Security string, Path string, Host string, SNI string, opt []byte) *Vless {
110 | l := Vless(*features.NewVless(Add, Port, ID, TLS, HeaderType, Encryption, Net, Flow, Security, Path, Host, SNI, opt))
111 | return &l
112 | }
113 |
114 | func (l *Vless) toVmess() *Vmess {
115 | return &Vmess{
116 | Add: l.Add,
117 | Port: l.Port,
118 | ID: l.ID,
119 | TLS: l.TLS,
120 | Net: l.Net,
121 | Type: l.Type,
122 | Encryption: l.Encryption,
123 | Security: l.Security,
124 | Flow: l.Flow,
125 | Path: l.Path,
126 | Host: l.Host,
127 | SNI: l.SNI,
128 | Protocol: v2ray.VLESS,
129 | VmessOptions: l.VmessOptions,
130 | }
131 | }
132 |
133 | func NewLiteShadowSocks(Add string, Port int, ID string, Security string, opt []byte) *Vmess {
134 | options := features.NewVmessOptions(opt)
135 | return &Vmess{
136 | Add: Add,
137 | Port: Port,
138 | ID: ID,
139 | Security: Security,
140 | Protocol: v2ray.SHADOWSOCKS,
141 | VmessOptions: options,
142 | Trojan: nil,
143 | }
144 | }
145 |
146 | func NewShadowSocks(Add string, Port int, Password string, Method string, opt []byte) *Shadowsocks {
147 | ss := Shadowsocks(*features.NewShadowsocks(Add, Port, Password, Method, opt))
148 | return &ss
149 | }
150 |
151 | func (ss *Shadowsocks) toVmess() *Vmess {
152 | shadowsocks := features.Shadowsocks(*ss)
153 | return &Vmess{
154 | Protocol: v2ray.SHADOWSOCKS,
155 | Shadowsocks: &shadowsocks,
156 | VmessOptions: ss.VmessOptions,
157 | }
158 | }
159 |
160 | // TODO: default value
161 | func NewVmess(Host string, Path string, TLS string, Add string, Port int, Aid int, Net string, ID string, Type string, Security string, opt []byte) *Vmess {
162 | v := Vmess(*features.NewVmess(Host, Path, TLS, Add, Port, Aid, Net, ID, Type, Security, opt))
163 | return &v
164 | }
165 |
166 | func (profile *Vmess) getProxyOutboundDetourConfig() conf.OutboundDetourConfig {
167 | proxyOutboundConfig := conf.OutboundDetourConfig{}
168 | if profile.Protocol == v2ray.VMESS {
169 | proxyOutboundConfig = createVmessOutboundDetourConfig(profile)
170 | }
171 | if profile.Protocol == v2ray.TROJAN {
172 | proxyOutboundConfig = createTrojanOutboundDetourConfig(profile)
173 | }
174 | if profile.Protocol == v2ray.SHADOWSOCKS {
175 | proxyOutboundConfig = createShadowsocksOutboundDetourConfig(profile)
176 | }
177 | if profile.Protocol == v2ray.VLESS {
178 | proxyOutboundConfig = createVlessOutboundDetourConfig(profile)
179 | }
180 | return proxyOutboundConfig
181 | }
182 |
183 | // TODO: try with native struct config conf.vmess
184 | func generateVmessConfig(profile *Vmess) ([]byte, error) {
185 | vmessConfig := v2ray.VmessConfig{
186 | Stats: v2ray.Stats{},
187 | Log: v2ray.Log{Loglevel: "warning"},
188 | Inbounds: nil,
189 | }
190 | vmessConfig.DNS = v2ray.DNS{
191 | Servers: []string{"8.8.8.8"},
192 | Hosts: v2ray.Hosts{"domain:googleapis.cn": "googleapis.com"},
193 | }
194 | vmessConfig.Routing = v2ray.Routing{
195 | DomainStrategy: "IPIfNonMatch",
196 | Rules: []v2ray.Rules{
197 | v2ray.Rules{
198 | Type: "field",
199 | OutboundTag: "direct",
200 | IP: []string{"geoip:private", "geoip:cn"},
201 | },
202 | v2ray.Rules{
203 | Type: "field",
204 | OutboundTag: "direct",
205 | Domain: []string{"geosite:cn"},
206 | },
207 | },
208 | }
209 | outbound := v2ray.Outbounds{
210 | Tag: "proxy",
211 | Protocol: "vmess",
212 | Mux: &v2ray.Mux{Enabled: false, Concurrency: -1},
213 | Settings: v2ray.OutboundsSettings{
214 | Vnext: []v2ray.Vnext{
215 | v2ray.Vnext{
216 | Address: profile.Add,
217 | Port: profile.Port,
218 | Users: []v2ray.Users{
219 | v2ray.Users{
220 | AlterID: profile.Aid,
221 | Email: "v2ray@email.com",
222 | ID: profile.ID,
223 | Security: profile.Security,
224 | Level: 8,
225 | },
226 | },
227 | },
228 | },
229 | },
230 | StreamSettings: &v2ray.StreamSettings{Network: "tcp", Security: ""},
231 | }
232 | if profile.Net == "ws" {
233 | outbound.StreamSettings = &v2ray.StreamSettings{
234 | Network: profile.Net,
235 | Wssettings: &v2ray.Wssettings{
236 | ConnectionReuse: true, Path: profile.Path,
237 | },
238 | }
239 | if profile.Host != "" {
240 | outbound.StreamSettings.Wssettings.Headers = v2ray.Headers{
241 | Host: profile.Host,
242 | }
243 | }
244 | }
245 | if profile.TLS == "tls" {
246 | outbound.StreamSettings.Security = profile.TLS
247 | outbound.StreamSettings.TLSSettings = &v2ray.TLSSettings{AllowInsecure: true}
248 | }
249 | // vmess must be the first
250 | vmessConfig.Outbounds = []v2ray.Outbounds{
251 | outbound,
252 | v2ray.Outbounds{
253 | Protocol: "freedom",
254 | Tag: "direct",
255 | Settings: v2ray.OutboundsSettings{
256 | DomainStrategy: "UseIP",
257 | },
258 | },
259 | }
260 | vmessConfig.Policy = v2ray.Policy{
261 | Levels: map[string]v2ray.Level{
262 | "8": v2ray.Level{
263 | ConnIdle: 300,
264 | DownlinkOnly: 1,
265 | Handshake: 4,
266 | UplinkOnly: 1,
267 | },
268 | },
269 | System: v2ray.System{
270 | StatsOutboundUplink: true,
271 | StatsOutboundDownlink: true,
272 | },
273 | }
274 | // errStr, _ := json.Marshal(vmessConfig)
275 | return json.MarshalIndent(vmessConfig, "", " ")
276 | }
277 |
278 | func loadVmessConfig(profile *Vmess) (*conf.Config, error) {
279 | jsonConfig := &conf.Config{}
280 | jsonConfig.LogConfig = &conf.LogConfig{
281 | // AccessLog: "",
282 | // ErrorLog: "",
283 | LogLevel: profile.Loglevel,
284 | }
285 | // https://github.com/Loyalsoldier/v2ray-rules-dat
286 | jsonConfig.DNSConfig = createDNSConfig(profile.RouteMode, profile.DNS)
287 | // update rules
288 | jsonConfig.RouterConfig = createRouterConfig(profile.RouteMode)
289 | // policy
290 | // connectionIdle := uint32(300)
291 | // downlinkOnly := uint32(1)
292 | // handshake := uint32(4)
293 | // uplinkOnly := uint32(1)
294 | // jsonConfig.Policy = &conf.PolicyConfig{
295 | // Levels: map[uint32]*conf.Policy{
296 | // 8: &conf.Policy{
297 | // ConnectionIdle: &connectionIdle,
298 | // DownlinkOnly: &downlinkOnly,
299 | // Handshake: &handshake,
300 | // UplinkOnly: &uplinkOnly,
301 | // },
302 | // },
303 | // System: &conf.SystemPolicy{
304 | // StatsInboundDownlink: true,
305 | // StatsInboundUplink: true,
306 | // },
307 | // }
308 | // inboundsSettings, _ := json.Marshal(v2ray.InboundsSettings{
309 | // Auth: "noauth",
310 | // IP: "127.0.0.1",
311 | // UDP: true,
312 | // })
313 | // inboundsSettingsMsg := json.RawMessage(inboundsSettings)
314 | // jsonConfig.InboundConfigs = []conf.InboundDetourConfig{
315 | // conf.InboundDetourConfig{
316 | // Tag: "socks-in",
317 | // Protocol: "socks",
318 | // PortRange: &conf.PortRange{From: 8088, To: 8088},
319 | // ListenOn: &conf.Address{vnet.IPAddress([]byte{127, 0, 0, 1})},
320 | // Settings: &inboundsSettingsMsg,
321 | // },
322 | // conf.InboundDetourConfig{
323 | // Tag: "http-in",
324 | // Protocol: "http",
325 | // PortRange: &conf.PortRange{From: 8090, To: 8090},
326 | // ListenOn: &conf.Address{vnet.IPAddress([]byte{127, 0, 0, 1})},
327 | // },
328 | // }
329 | proxyOutboundConfig := profile.getProxyOutboundDetourConfig()
330 | // if profile.Protocol == VMESS {
331 | // proxyOutboundConfig = createVmessOutboundDetourConfig(profile)
332 | // }
333 | // if profile.Protocol == TROJAN {
334 | // proxyOutboundConfig = createTrojanOutboundDetourConfig(profile)
335 | // }
336 | freedomOutboundDetourConfig := createFreedomOutboundDetourConfig(profile.UseIPv6)
337 | // order matters
338 | // GFWList mode, use 'direct' as default
339 | if profile.RouteMode == 4 {
340 | jsonConfig.OutboundConfigs = []conf.OutboundDetourConfig{
341 | freedomOutboundDetourConfig,
342 | proxyOutboundConfig,
343 | }
344 | } else {
345 | jsonConfig.OutboundConfigs = []conf.OutboundDetourConfig{
346 | proxyOutboundConfig,
347 | freedomOutboundDetourConfig,
348 | }
349 | }
350 | // policy
351 | jsonConfig.Policy = creatPolicyConfig()
352 | // stats
353 | jsonConfig.Stats = &conf.StatsConfig{}
354 | return jsonConfig, nil
355 | }
356 |
357 | // func logConfig(logLevel string) *vlog.Config {
358 | // config := &vlog.Config{
359 | // ErrorLogLevel: clog.Severity_Warning,
360 | // ErrorLogType: vlog.LogType_Console,
361 | // AccessLogType: vlog.LogType_Console,
362 | // }
363 | // level := strings.ToLower(logLevel)
364 | // switch level {
365 | // case "debug":
366 | // config.ErrorLogLevel = clog.Severity_Debug
367 | // case "info":
368 | // config.ErrorLogLevel = clog.Severity_Info
369 | // case "error":
370 | // config.ErrorLogLevel = clog.Severity_Error
371 | // case "none":
372 | // config.ErrorLogType = vlog.LogType_None
373 | // config.AccessLogType = vlog.LogType_None
374 | // }
375 | // return config
376 | // }
377 |
378 | // func vmessToCoreConfig(profile *Vmess, inboundDetourConfig *conf.InboundDetourConfig) (*vcore.Config, error) {
379 | // // vmess outbound
380 | // vmessUser, _ := json.Marshal(conf.VMessAccount{
381 | // ID: profile.ID,
382 | // AlterIds: uint16(profile.Aid),
383 | // Security: "auto",
384 | // })
385 | // vmessOutboundConfig := conf.VMessOutboundConfig{
386 | // Receivers: []*conf.VMessOutboundTarget{
387 | // &conf.VMessOutboundTarget{
388 | // Address: &conf.Address{Address: vnet.NewIPOrDomain(vnet.ParseAddress(profile.Add)).AsAddress()},
389 | // Port: uint16(profile.Port),
390 | // Users: []json.RawMessage{json.RawMessage(vmessUser)},
391 | // },
392 | // },
393 | // }
394 | // oc, err := vmessOutboundConfig.Build()
395 | // if err != nil {
396 | // return nil, err
397 | // }
398 | // outboundProxy := vcomserial.ToTypedMessage(oc)
399 |
400 | // // freedom proxy
401 | // freedomOutboundsSettings, _ := json.Marshal(v2ray.OutboundsSettings{DomainStrategy: "UseIP"})
402 | // freedomOutboundsSettingsMsg := json.RawMessage(freedomOutboundsSettings)
403 | // freedomProxy := conf.OutboundDetourConfig{
404 | // Protocol: "freedom",
405 | // Tag: "direct",
406 | // Settings: &freedomOutboundsSettingsMsg,
407 | // }
408 | // freedomConf, err := freedomProxy.Build()
409 | // if err != nil {
410 | // return nil, err
411 | // }
412 |
413 | // var transportSettings proto.Message
414 | // var connectionReuse bool
415 | // mode := profile.Net
416 | // switch profile.Net {
417 | // case "ws":
418 | // transportSettings = &websocket.Config{
419 | // Path: profile.Path,
420 | // Header: []*websocket.Header{
421 | // {Key: "Host", Value: profile.Host},
422 | // },
423 | // }
424 | // connectionReuse = true
425 | // mode = "websocket"
426 | // case "quic":
427 | // transportSettings = &quic.Config{
428 | // Security: &protocol.SecurityConfig{Type: protocol.SecurityType_NONE},
429 | // }
430 | // profile.TLS = "tls"
431 | // case "":
432 | // default:
433 | // return nil, newError("unsupported mode:", profile.Net)
434 | // }
435 |
436 | // streamConfig := vinternet.StreamConfig{
437 | // ProtocolName: mode,
438 | // TransportSettings: []*vinternet.TransportConfig{{
439 | // ProtocolName: mode,
440 | // Settings: vcomserial.ToTypedMessage(transportSettings),
441 | // }},
442 | // }
443 | // // TODO: support cert
444 | // if profile.TLS == "tls" {
445 | // tlsConfig := tls.Config{ServerName: profile.Host}
446 | // streamConfig.SecurityType = vcomserial.GetMessageType(&tlsConfig)
447 | // streamConfig.SecuritySettings = []*vcomserial.TypedMessage{vcomserial.ToTypedMessage(&tlsConfig)}
448 | // }
449 | // //router config
450 | // routerConfig, err := createRouterConfig().Build()
451 | // if err != nil {
452 | // return nil, err
453 | // }
454 | // // dns config
455 | // dnsConfig, err := createDNSConfig().Build()
456 | // if err != nil {
457 | // return nil, err
458 | // }
459 | // apps := []*vcomserial.TypedMessage{
460 | // vcomserial.ToTypedMessage(&dispatcher.Config{}),
461 | // vcomserial.ToTypedMessage(&vproxyman.InboundConfig{}),
462 | // vcomserial.ToTypedMessage(&vproxyman.OutboundConfig{}),
463 | // vcomserial.ToTypedMessage(logConfig(profile.Loglevel)),
464 | // vcomserial.ToTypedMessage(routerConfig),
465 | // vcomserial.ToTypedMessage(dnsConfig),
466 | // }
467 | // senderConfig := vproxyman.SenderConfig{StreamSettings: &streamConfig}
468 | // if connectionReuse {
469 | // senderConfig.MultiplexSettings = &vproxyman.MultiplexingConfig{Enabled: true, Concurrency: 16}
470 | // }
471 | // vcoreconfig := vcore.Config{
472 | // Outbound: []*vcore.OutboundHandlerConfig{
473 | // {
474 | // SenderSettings: vcomserial.ToTypedMessage(&senderConfig),
475 | // ProxySettings: outboundProxy,
476 | // Tag: "proxy",
477 | // },
478 | // freedomConf,
479 | // },
480 | // App: apps,
481 | // }
482 | // if inboundDetourConfig != nil {
483 | // inboundConfig, err := inboundDetourConfig.Build()
484 | // if err != nil {
485 | // return nil, err
486 | // }
487 | // vcoreconfig.Inbound = []*vcore.InboundHandlerConfig{
488 | // inboundConfig,
489 | // }
490 | // }
491 | // return &vcoreconfig, nil
492 | // }
493 |
494 | func loadVmessTestConfig(profile *Vmess, port uint32) (*conf.Config, error) {
495 | jsonConfig := &conf.Config{}
496 | jsonConfig.LogConfig = &conf.LogConfig{
497 | LogLevel: profile.Loglevel,
498 | }
499 | jsonConfig.DNSConfig = &conf.DNSConfig{
500 | Servers: []*conf.NameServerConfig{
501 | &conf.NameServerConfig{
502 | Address: &conf.Address{vnet.IPAddress([]byte{223, 5, 5, 5})},
503 | Port: 53,
504 | },
505 | },
506 | }
507 | if port > 0 && port < 65535 {
508 | jsonConfig.InboundConfigs = []conf.InboundDetourConfig{
509 | createInboundDetourConfig(port),
510 | }
511 | }
512 | jsonConfig.OutboundConfigs = []conf.OutboundDetourConfig{
513 | profile.getProxyOutboundDetourConfig(),
514 | }
515 | jsonConfig.Stats = &conf.StatsConfig{}
516 | return jsonConfig, nil
517 | }
518 |
519 | func startInstance(profile *Vmess, config *conf.Config) (*vcore.Instance, error) {
520 | if config == nil {
521 | defaultConfig, err := loadVmessConfig(profile)
522 | if err != nil {
523 | return nil, err
524 | }
525 | config = defaultConfig
526 | }
527 | coreConfig, err := config.Build()
528 | if err != nil {
529 | return nil, err
530 | }
531 | instance, err := vcore.New(coreConfig)
532 | if err != nil {
533 | return nil, err
534 | }
535 | if err := instance.Start(); err != nil {
536 | return nil, err
537 | }
538 | statsManager = instance.GetFeature(v2stats.ManagerType()).(v2stats.Manager)
539 | return instance, nil
540 | }
541 |
542 | func startXRayInstance(profile *Vmess) (*xcore.Instance, error) {
543 | // fProfile := features.Vmess(*profile)
544 | jsonConfig, err := loadVmessConfig(profile)
545 | if err != nil {
546 | return nil, err
547 | }
548 | // config.DNSConfig = nil
549 | // b, err := json.Marshal(config)
550 | // if err != nil {
551 | // return nil, err
552 | // }
553 | // jsonConfig, err := xserial.DecodeJSONConfig(bytes.NewReader(b))
554 | jsonConfig.DNSConfig = xray.CreateDNSConfig(profile.VmessOptions)
555 | pbConfig, err := jsonConfig.Build()
556 | if err != nil {
557 | return nil, err
558 | }
559 | instance, err := xcore.New(pbConfig)
560 | if err != nil {
561 | return nil, err
562 | }
563 | err = instance.Start()
564 | if err != nil {
565 | return nil, err
566 | }
567 | xStatsManager = instance.GetFeature(x2stats.ManagerType()).(x2stats.Manager)
568 | return instance, nil
569 | }
570 |
571 | func startLiteInstance(profile *Vmess) (loutbound.Dialer, error) {
572 | switch profile.Protocol {
573 | case v2ray.VMESS:
574 | return vmess2Lite(profile)
575 | case v2ray.TROJAN:
576 | return trojan2Lite(profile)
577 | case v2ray.SHADOWSOCKS:
578 | return ss2Lite(profile)
579 | default:
580 | return nil, newError("not supported protocol")
581 | }
582 | }
583 |
584 | // VpnService should be implemented in Java/Kotlin.
585 | type VpnService interface {
586 | // Protect is just a proxy to the VpnService.protect() method.
587 | // See also: https://developer.android.com/reference/android/net/VpnService.html#protect(int)
588 | Protect(fd int) bool
589 | }
590 |
591 | // PacketFlow should be implemented in Java/Kotlin.
592 | type PacketFlow interface {
593 | // WritePacket should writes packets to the TUN fd.
594 | WritePacket(packet []byte)
595 | }
596 |
597 | // Write IP packets to the lwIP stack. Call this function in the main loop of
598 | // the VpnService in Java/Kotlin, which should reads packets from the TUN fd.
599 | func InputPacket(data []byte) {
600 | if lwipStack != nil {
601 | lwipStack.Write(data)
602 | }
603 | }
604 |
605 | type QuerySpeed interface {
606 | UpdateTraffic(up int64, down int64)
607 | PersistTraffic(up int64, down int64) // update and save traffic
608 | }
609 |
610 | type TestLatency interface {
611 | UpdateLatency(id int, elapsed int64)
612 | }
613 |
614 | type TestLatencyStop interface {
615 | UpdateLatency(id int, elapsed int64) bool
616 | }
617 |
618 | type TestDownload interface {
619 | UpdateSpeed(id int, speed int64, elapse int64)
620 | UpdateTraffic(id int, traffic int64)
621 | }
622 |
623 | // SetNonblock puts the fd in blocking or non-blocking mode.
624 | func SetNonblock(fd int, nonblocking bool) bool {
625 | err := syscall.SetNonblock(fd, nonblocking)
626 | return err == nil
627 | }
628 |
629 | // SetLocalDNS sets the DNS server that used by Go's default resolver, it accepts
630 | // string in the form "host:port", e.g. 223.5.5.5:53
631 | func SetLocalDNS(dns string) {
632 | localDNS = dns
633 | }
634 |
635 | // StartV2Ray sets up lwIP stack, starts a V2Ray instance and registers the instance as the
636 | // connection handler for tun2socks.
637 | func StartV2Ray(
638 | packetFlow PacketFlow,
639 | vpnService VpnService,
640 | logService LogService,
641 | querySpeed QuerySpeed,
642 | configBytes []byte,
643 | assetPath string) error {
644 | if packetFlow != nil {
645 |
646 | if lwipStack == nil {
647 | // Setup the lwIP stack.
648 | lwipStack = core.NewLWIPStack()
649 | }
650 |
651 | // Assets
652 | os.Setenv(v2Asset, assetPath)
653 | // log
654 | registerLogService(logService)
655 |
656 | // Protect file descriptors of net connections in the VPN process to prevent infinite loop.
657 | protectFd := func(s VpnService, fd int) error {
658 | if s.Protect(fd) {
659 | return nil
660 | } else {
661 | return fmt.Errorf("failed to protect fd %v", fd)
662 | }
663 | }
664 | // netCtlr := func(network, address string, fd uintptr) error {
665 | // return protectFd(vpnService, int(fd))
666 | // }
667 | netCtlr := func(network, address string, conn syscall.RawConn) error {
668 | return control.Raw(conn, func(fd uintptr) error {
669 | return protectFd(vpnService, int(fd))
670 | })
671 | }
672 | vinternet.RegisterDialerController(netCtlr)
673 | vinternet.RegisterListenerController(netCtlr)
674 |
675 | // Share the buffer pool.
676 | core.SetBufferPool(vbytespool.GetPool(core.BufSize))
677 |
678 | // Start the V2Ray instance.
679 | v, err = vcore.StartInstance("json", configBytes)
680 | if err != nil {
681 | log.Fatalf("start V instance failed: %v", err)
682 | return err
683 | }
684 |
685 | // Configure sniffing settings for traffic coming from tun2socks.
686 | sniffingConfig := &vproxyman.SniffingConfig{
687 | Enabled: true,
688 | DestinationOverride: strings.Split("tls,http", ","),
689 | }
690 | ctx := contextWithSniffingConfig(context.Background(), sniffingConfig)
691 |
692 | // Register tun2socks connection handlers.
693 | // vhandler := v2ray.NewHandler(ctx, v)
694 | // core.RegisterTCPConnectionHandler(vhandler)
695 | // core.RegisterUDPConnectionHandler(vhandler)
696 | core.RegisterTCPConnHandler(v2ray.NewTCPHandler(ctx, v))
697 | core.RegisterUDPConnHandler(v2ray.NewUDPHandler(ctx, v, 3*time.Minute))
698 |
699 | // Write IP packets back to TUN.
700 | core.RegisterOutputFn(func(data []byte) (int, error) {
701 | if !isStopped {
702 | packetFlow.WritePacket(data)
703 | }
704 | return len(data), nil
705 | })
706 |
707 | statsManager = v.GetFeature(v2stats.ManagerType()).(v2stats.Manager)
708 | runner.CheckAndStop(updateStatusPipeTask)
709 | updateStatusPipeTask = createUpdateStatusPipeTask(querySpeed)
710 | isStopped = false
711 | logService.WriteLog(fmt.Sprintf("V2Ray %s started!", CheckVersion()))
712 | return nil
713 | }
714 | return errors.New("packetFlow is null")
715 | }
716 |
717 | func StartXRay(
718 | packetFlow PacketFlow,
719 | vpnService VpnService,
720 | logService LogService,
721 | querySpeed QuerySpeed,
722 | configBytes []byte,
723 | assetPath string) error {
724 | if packetFlow != nil {
725 |
726 | if lwipStack == nil {
727 | // Setup the lwIP stack.
728 | lwipStack = core.NewLWIPStack()
729 | }
730 |
731 | // Assets
732 | os.Setenv("xray.location.asset", assetPath)
733 | // log
734 | registerLogService(logService)
735 |
736 | // Protect file descriptors of net connections in the VPN process to prevent infinite loop.
737 | protectFd := func(s VpnService, fd int) error {
738 | if s.Protect(fd) {
739 | return nil
740 | } else {
741 | return fmt.Errorf("failed to protect fd %v", fd)
742 | }
743 | }
744 | // netCtlr := func(network, address string, fd uintptr) error {
745 | // return protectFd(vpnService, int(fd))
746 | // }
747 | netCtlr := func(network, address string, conn syscall.RawConn) error {
748 | return control.Raw(conn, func(fd uintptr) error {
749 | return protectFd(vpnService, int(fd))
750 | })
751 | }
752 | xinternet.RegisterDialerController(netCtlr)
753 | xinternet.RegisterListenerController(netCtlr)
754 |
755 | // Share the buffer pool.
756 | core.SetBufferPool(xbytespool.GetPool(core.BufSize))
757 |
758 | // Start the V2Ray instance.
759 | x, err = xray.StartInstance(configBytes)
760 | if err != nil {
761 | log.Fatalf("start V instance failed: %v", err)
762 | return err
763 | }
764 |
765 | // Configure sniffing settings for traffic coming from tun2socks.
766 | ctx := context.Background()
767 | content := xsession.ContentFromContext(ctx)
768 | if content == nil {
769 | content = new(xsession.Content)
770 | ctx = xsession.ContextWithContent(ctx, content)
771 | }
772 |
773 | core.RegisterTCPConnHandler(xray.NewTCPHandler(ctx, x))
774 | core.RegisterUDPConnHandler(xray.NewUDPHandler(ctx, x, 3*time.Minute))
775 |
776 | // Write IP packets back to TUN.
777 | core.RegisterOutputFn(func(data []byte) (int, error) {
778 | if !isStopped {
779 | packetFlow.WritePacket(data)
780 | }
781 | return len(data), nil
782 | })
783 |
784 | xStatsManager = x.GetFeature(x2stats.ManagerType()).(x2stats.Manager)
785 | runner.CheckAndStop(updateStatusPipeTask)
786 | updateStatusPipeTask = createUpdateStatusPipeTask(querySpeed)
787 | isStopped = false
788 | logService.WriteLog(fmt.Sprintf("XRay %s started!", CheckXVersion()))
789 | return nil
790 | }
791 | return errors.New("packetFlow is null")
792 | }
793 |
794 | func GenerateVmessString(profile *Vmess) (string, error) {
795 | configBytes, err := generateVmessConfig(profile)
796 | if err != nil {
797 | log.Fatalf("start V instance failed: %v", err)
798 | return "", err
799 | }
800 | return string(configBytes), nil
801 | }
802 |
803 | // StartV2Ray sets up lwIP stack, starts a V2Ray instance and registers the instance as the
804 | // connection handler for tun2socks.
805 | func StartV2RayWithVmess(
806 | packetFlow PacketFlow,
807 | vpnService VpnService,
808 | logService LogService,
809 | profile *Vmess,
810 | assetPath string) error {
811 | if packetFlow != nil {
812 |
813 | if lwipStack == nil {
814 | // Setup the lwIP stack.
815 | lwipStack = core.NewLWIPStack()
816 | }
817 |
818 | // Assets
819 | os.Setenv(v2Asset, assetPath)
820 | // logger
821 | registerLogService(logService)
822 | // Protect file descriptors of net connections in the VPN process to prevent infinite loop.
823 | protectFd := func(s VpnService, fd int) error {
824 | if s.Protect(fd) {
825 | return nil
826 | } else {
827 | return fmt.Errorf("failed to protect fd %v", fd)
828 | }
829 | }
830 | // netCtlr := func(network, address string, fd uintptr) error {
831 | // return protectFd(vpnService, int(fd))
832 | // }
833 | netCtlr := func(network, address string, conn syscall.RawConn) error {
834 | return control.Raw(conn, func(fd uintptr) error {
835 | return protectFd(vpnService, int(fd))
836 | })
837 | }
838 | vinternet.RegisterDialerController(netCtlr)
839 | vinternet.RegisterListenerController(netCtlr)
840 |
841 | // Share the buffer pool.
842 | core.SetBufferPool(vbytespool.GetPool(core.BufSize))
843 |
844 | // Start the V2Ray instance.
845 | // configBytes, err := generateVmessConfig(profile)
846 | // if err != nil {
847 | // return err
848 | // }
849 | // v, err = vcore.StartInstance("json", configBytes)
850 | v, err = startInstance(profile, nil)
851 | if err != nil {
852 | log.Fatalf("start V instance failed: %v", err)
853 | return err
854 | }
855 | ctx := context.WithValue(context.Background(), "routeMode", profile.RouteMode)
856 | ctx = context.WithValue(ctx, "disableDNSCache", profile.DisableDNSCache)
857 | // Configure sniffing settings for traffic coming from tun2socks.
858 | if profile.EnableSniffing || profile.RouteMode == 4 {
859 | sniffingConfig := &vproxyman.SniffingConfig{
860 | Enabled: true,
861 | DestinationOverride: strings.Split("tls,http", ","),
862 | }
863 | ctx = contextWithSniffingConfig(ctx, sniffingConfig)
864 | }
865 | // Register tun2socks connection handlers.
866 | core.RegisterTCPConnHandler(v2ray.NewTCPHandler(ctx, v))
867 | core.RegisterUDPConnHandler(v2ray.NewUDPHandler(ctx, v, 3*time.Minute))
868 |
869 | // Write IP packets back to TUN.
870 | core.RegisterOutputFn(func(data []byte) (int, error) {
871 | if !isStopped {
872 | packetFlow.WritePacket(data)
873 | }
874 | return len(data), nil
875 | })
876 |
877 | isStopped = false
878 | logService.WriteLog("V2Ray Started!")
879 | return nil
880 | }
881 | return errors.New("packetFlow is null")
882 | }
883 |
884 | func StartV2RayWithTunFd(
885 | tunFd int,
886 | vpnService VpnService,
887 | logService LogService,
888 | querySpeed QuerySpeed,
889 | profile *Vmess,
890 | assetPath string) error {
891 | tunDev, err = pool.OpenTunDevice(tunFd)
892 | if err != nil {
893 | log.Fatalf("failed to open tun device: %v", err)
894 | }
895 | if lwipStack != nil {
896 | lwipStack.Close()
897 | }
898 | lwipStack = core.NewLWIPStack()
899 | lwipWriter = lwipStack.(io.Writer)
900 |
901 | // init v2ray
902 | os.Setenv(v2Asset, assetPath)
903 | registerLogService(logService)
904 | // Protect file descriptors of net connections in the VPN process to prevent infinite loop.
905 | protectFd := func(s VpnService, fd int) error {
906 | if s.Protect(fd) {
907 | return nil
908 | } else {
909 | return fmt.Errorf("failed to protect fd %v", fd)
910 | }
911 | }
912 | // netCtlr := func(network, address string, fd uintptr) error {
913 | // return protectFd(vpnService, int(fd))
914 | // }
915 | netCtlr := func(network, address string, conn syscall.RawConn) error {
916 | return control.Raw(conn, func(fd uintptr) error {
917 | return protectFd(vpnService, int(fd))
918 | })
919 | }
920 | vinternet.RegisterDialerController(netCtlr)
921 | vinternet.RegisterListenerController(netCtlr)
922 | core.SetBufferPool(vbytespool.GetPool(core.BufSize))
923 |
924 | v, err = startInstance(profile, nil)
925 | if err != nil {
926 | log.Fatalf("start V instance failed: %v", err)
927 | return err
928 | }
929 | ctx := context.WithValue(context.Background(), "routeMode", profile.RouteMode)
930 | ctx = context.WithValue(ctx, "disableDNSCache", profile.DisableDNSCache)
931 | // Configure sniffing settings for traffic coming from tun2socks.
932 | if profile.EnableSniffing || profile.RouteMode == 4 {
933 | sniffingConfig := &vproxyman.SniffingConfig{
934 | Enabled: true,
935 | DestinationOverride: strings.Split("tls,http", ","),
936 | }
937 | ctx = contextWithSniffingConfig(ctx, sniffingConfig)
938 | }
939 | // Register tun2socks connection handlers.
940 | core.RegisterTCPConnHandler(v2ray.NewTCPHandler(ctx, v))
941 | core.RegisterUDPConnHandler(v2ray.NewUDPHandler(ctx, v, 3*time.Minute))
942 |
943 | // Write IP packets back to TUN.
944 | // output := make(chan []byte, 2400)
945 | core.RegisterOutputFn(func(data []byte) (int, error) {
946 | // buf := vbytespool.Alloc(int32(len(data)))
947 | // l := copy(buf, data)
948 | // output <- data
949 | return tunDev.Write(data)
950 | })
951 | // go func(ctx context.Context) {
952 | // for {
953 | // select {
954 | // case <-ctx.Done():
955 | // return
956 | // case buf := <-output:
957 | // tunDev.Write(buf)
958 | // // vbytespool.Free(buf)
959 | // }
960 | // }
961 | // }(ctx)
962 | // core.RegisterOutputCh(tunDev.WriteCh)
963 | isStopped = false
964 | runner.CheckAndStop(lwipTUNDataPipeTask)
965 | runner.CheckAndStop(updateStatusPipeTask)
966 |
967 | lwipTUNDataPipeTask = runner.Go(func(shouldStop runner.S) error {
968 | zeroErr := errors.New("nil")
969 | // handlePacket(ctx, tunDev, lwipWriter, shouldStop)
970 | tunDev.Copy(lwipWriter)
971 | return zeroErr // any errors?
972 | })
973 | updateStatusPipeTask = createUpdateStatusPipeTask(querySpeed)
974 | logService.WriteLog("V2Ray Started!")
975 | return nil
976 | }
977 |
978 | func StartXRayWithTunFd(
979 | tunFd int,
980 | vpnService VpnService,
981 | logService LogService,
982 | querySpeed QuerySpeed,
983 | profile *Vmess,
984 | assetPath string) error {
985 | tunDev, err = pool.OpenTunDevice(tunFd)
986 | if err != nil {
987 | log.Fatalf("failed to open tun device: %v", err)
988 | }
989 | if lwipStack != nil {
990 | lwipStack.Close()
991 | }
992 | lwipStack = core.NewLWIPStack()
993 | lwipWriter = lwipStack.(io.Writer)
994 |
995 | // init v2ray
996 | os.Setenv(v2Asset, assetPath)
997 | registerLogService(logService)
998 | // Protect file descriptors of net connections in the VPN process to prevent infinite loop.
999 | protectFd := func(s VpnService, fd int) error {
1000 | if s.Protect(fd) {
1001 | return nil
1002 | } else {
1003 | return fmt.Errorf("failed to protect fd %v", fd)
1004 | }
1005 | }
1006 | // netCtlr := func(network, address string, fd uintptr) error {
1007 | // return protectFd(vpnService, int(fd))
1008 | // }
1009 | netCtlr := func(network, address string, conn syscall.RawConn) error {
1010 | return control.Raw(conn, func(fd uintptr) error {
1011 | return protectFd(vpnService, int(fd))
1012 | })
1013 | }
1014 | xinternet.RegisterDialerController(netCtlr)
1015 | xinternet.RegisterListenerController(netCtlr)
1016 | core.SetBufferPool(xbytespool.GetPool(core.BufSize))
1017 |
1018 | x, err = startXRayInstance(profile)
1019 | if err != nil {
1020 | log.Fatalf("start V instance failed: %v", err)
1021 | return err
1022 | }
1023 | ctx := context.Background()
1024 | content := xsession.ContentFromContext(ctx)
1025 | if content == nil {
1026 | content = new(xsession.Content)
1027 | ctx = xsession.ContextWithContent(ctx, content)
1028 | }
1029 | // Configure sniffing settings for traffic coming from tun2socks.
1030 | if profile.EnableSniffing || profile.RouteMode == 4 {
1031 | sniffingConfig := &vproxyman.SniffingConfig{
1032 | Enabled: true,
1033 | DestinationOverride: strings.Split("tls,http", ","),
1034 | }
1035 | ctx = contextWithSniffingConfig(ctx, sniffingConfig)
1036 | }
1037 | // Register tun2socks connection handlers.
1038 | core.RegisterTCPConnHandler(xray.NewTCPHandler(ctx, x))
1039 | core.RegisterUDPConnHandler(xray.NewUDPHandler(ctx, x, 3*time.Minute))
1040 |
1041 | // Write IP packets back to TUN.
1042 | core.RegisterOutputFn(func(data []byte) (int, error) {
1043 | return tunDev.Write(data)
1044 | })
1045 | isStopped = false
1046 | runner.CheckAndStop(lwipTUNDataPipeTask)
1047 | runner.CheckAndStop(updateStatusPipeTask)
1048 |
1049 | lwipTUNDataPipeTask = runner.Go(func(shouldStop runner.S) error {
1050 | zeroErr := errors.New("nil")
1051 | tunDev.Copy(lwipWriter)
1052 | return zeroErr // any errors?
1053 | })
1054 | updateStatusPipeTask = createUpdateStatusPipeTask(querySpeed)
1055 | logService.WriteLog(fmt.Sprintf("Start XRay %s", CheckXVersion()))
1056 | return nil
1057 | }
1058 |
1059 | func StartV2RayLiteWithTunFd(
1060 | tunFd int,
1061 | vpnService VpnService,
1062 | logService LogService,
1063 | querySpeed QuerySpeed,
1064 | profile *Vmess,
1065 | assetPath string) error {
1066 | tunDev, err = pool.OpenTunDevice(tunFd)
1067 | if err != nil {
1068 | log.Fatalf("failed to open tun device: %v", err)
1069 | }
1070 | if lwipStack != nil {
1071 | lwipStack.Close()
1072 | }
1073 | lwipStack = core.NewLWIPStack()
1074 | lwipWriter = lwipStack.(io.Writer)
1075 |
1076 | // init v2ray
1077 | registerLogService(logService)
1078 | // Protect file descriptors of net connections in the VPN process to prevent infinite loop.
1079 | protectFd := func(s VpnService, fd int) error {
1080 | if s.Protect(fd) {
1081 | return nil
1082 | } else {
1083 | return fmt.Errorf("failed to protect fd %v", fd)
1084 | }
1085 | }
1086 | netCtlr := func(network, address string, fd uintptr) error {
1087 | return protectFd(vpnService, int(fd))
1088 | }
1089 | lDialer.RegisterDialerController(netCtlr)
1090 | lDialer.RegisterListenerController(netCtlr)
1091 |
1092 | l, err = startLiteInstance(profile)
1093 | if err != nil {
1094 | log.Fatalf("start V instance failed: %v", err)
1095 | return err
1096 | }
1097 | ctx := context.Background()
1098 | // Register tun2socks connection handlers.
1099 | core.RegisterTCPConnHandler(lite.NewTCPHandler(ctx, l))
1100 | core.RegisterUDPConnHandler(lite.NewUDPHandler(ctx, l, 3*time.Minute))
1101 |
1102 | // Write IP packets back to TUN.
1103 | core.RegisterOutputFn(func(data []byte) (int, error) {
1104 | return tunDev.Write(data)
1105 | })
1106 | isStopped = false
1107 | runner.CheckAndStop(lwipTUNDataPipeTask)
1108 | runner.CheckAndStop(updateStatusPipeTask)
1109 |
1110 | lwipTUNDataPipeTask = runner.Go(func(shouldStop runner.S) error {
1111 | zeroErr := errors.New("nil")
1112 | tunDev.Copy(lwipWriter)
1113 | return zeroErr // any errors?
1114 | })
1115 | updateStatusPipeTask = createUpdateStatusPipeTask(querySpeed)
1116 | logService.WriteLog(fmt.Sprintf("Start Lite %s", CheckVersion()))
1117 | return nil
1118 | }
1119 |
1120 | func handlePacket(ctx context.Context, tunDev *pool.Interface, lwipWriter io.Writer, shouldStop runner.S) {
1121 | // inbound := make(chan []byte, 100)
1122 | // outbound := make(chan []byte, 1000)
1123 | ctx, cancel := context.WithCancel(ctx)
1124 | defer cancel()
1125 | // defer close(outbound)
1126 |
1127 | // writer
1128 | go func(ctx context.Context) {
1129 | for {
1130 | select {
1131 | case buffer, ok := <-tunDev.ReadCh:
1132 | if !ok {
1133 | return
1134 | }
1135 | _, _ = lwipWriter.Write(buffer)
1136 | vbytespool.Free(buffer)
1137 | case <-ctx.Done():
1138 | return
1139 | }
1140 | }
1141 | }(ctx)
1142 | tunDev.Run(ctx)
1143 | }
1144 |
1145 | func createUpdateStatusPipeTask(querySpeed QuerySpeed) *runner.Task {
1146 | return runner.Go(func(shouldStop runner.S) error {
1147 | ticker := time.NewTicker(1 * time.Second)
1148 | defer ticker.Stop()
1149 | tickerPersist := time.NewTicker(30 * time.Second)
1150 | defer tickerPersist.Stop()
1151 | zeroErr := errors.New("nil")
1152 | for {
1153 | if shouldStop() {
1154 | break
1155 | }
1156 | select {
1157 | case <-tickerPersist.C:
1158 | up := QueryOutboundStats("proxy", "uplink")
1159 | down := QueryOutboundStats("proxy", "downlink")
1160 | querySpeed.PersistTraffic(up, down)
1161 | default:
1162 | select {
1163 | case <-ticker.C:
1164 | up := QueryOutboundStats("proxy", "uplink")
1165 | down := QueryOutboundStats("proxy", "downlink")
1166 | if up > 0 || down > 0 {
1167 | querySpeed.UpdateTraffic(up, down)
1168 | }
1169 | // case <-lwipTUNDataPipeTask.StopChan():
1170 | // return errors.New("stopped")
1171 | case <-tickerPersist.C:
1172 | up := QueryOutboundStats("proxy", "uplink")
1173 | down := QueryOutboundStats("proxy", "downlink")
1174 | querySpeed.PersistTraffic(up, down)
1175 | }
1176 | }
1177 | }
1178 | return zeroErr
1179 | })
1180 | }
1181 |
1182 | func StartTrojan(
1183 | packetFlow PacketFlow,
1184 | vpnService VpnService,
1185 | logService LogService,
1186 | trojan *Trojan,
1187 | assetPath string) error {
1188 | profile := trojan.toVmess()
1189 | return StartV2RayWithVmess(packetFlow, vpnService, logService, profile, assetPath)
1190 | }
1191 |
1192 | func StartTrojanTunFd(
1193 | tunFd int,
1194 | vpnService VpnService,
1195 | logService LogService,
1196 | querySpeed QuerySpeed,
1197 | trojan *Trojan,
1198 | assetPath string) error {
1199 | profile := trojan.toVmess()
1200 | return StartV2RayWithTunFd(tunFd, vpnService, logService, querySpeed, profile, assetPath)
1201 | }
1202 |
1203 | func StartXTrojanTunFd(
1204 | tunFd int,
1205 | vpnService VpnService,
1206 | logService LogService,
1207 | querySpeed QuerySpeed,
1208 | trojan *Trojan,
1209 | assetPath string) error {
1210 | profile := trojan.toVmess()
1211 | return StartXRayWithTunFd(tunFd, vpnService, logService, querySpeed, profile, assetPath)
1212 | }
1213 |
1214 | func StartShadowsocksTunFd(
1215 | tunFd int,
1216 | vpnService VpnService,
1217 | logService LogService,
1218 | querySpeed QuerySpeed,
1219 | shadowsocks *Shadowsocks,
1220 | assetPath string) error {
1221 | profile := shadowsocks.toVmess()
1222 | // profile.VmessOptions.RouteMode = 3
1223 | return StartV2RayWithTunFd(tunFd, vpnService, logService, querySpeed, profile, assetPath)
1224 | }
1225 |
1226 | func StartXShadowsocksTunFd(
1227 | tunFd int,
1228 | vpnService VpnService,
1229 | logService LogService,
1230 | querySpeed QuerySpeed,
1231 | shadowsocks *Shadowsocks,
1232 | assetPath string) error {
1233 | profile := shadowsocks.toVmess()
1234 | // profile.VmessOptions.RouteMode = 3
1235 | return StartXRayWithTunFd(tunFd, vpnService, logService, querySpeed, profile, assetPath)
1236 | }
1237 |
1238 | func StartXVlessTunFd(
1239 | tunFd int,
1240 | vpnService VpnService,
1241 | logService LogService,
1242 | querySpeed QuerySpeed,
1243 | vl *Vless,
1244 | assetPath string) error {
1245 | profile := vl.toVmess()
1246 | // profile.VmessOptions.RouteMode = 3
1247 | return StartXRayWithTunFd(tunFd, vpnService, logService, querySpeed, profile, assetPath)
1248 | }
1249 |
1250 | // StopV2Ray stop v2ray
1251 | func StopV2Ray() {
1252 | isStopped = true
1253 | if tunDev != nil {
1254 | tunDev.Stop()
1255 | }
1256 | runner.CheckAndStop(updateStatusPipeTask)
1257 | runner.CheckAndStop(lwipTUNDataPipeTask)
1258 |
1259 | if lwipStack != nil {
1260 | lwipStack.Close()
1261 | lwipStack = nil
1262 | }
1263 | if statsManager != nil {
1264 | statsManager.Close()
1265 | statsManager = nil
1266 | }
1267 | if xStatsManager != nil {
1268 | xStatsManager.Close()
1269 | xStatsManager = nil
1270 | }
1271 | if v != nil {
1272 | v.Close()
1273 | v = nil
1274 | }
1275 | if x != nil {
1276 | x.Close()
1277 | x = nil
1278 | }
1279 | l = nil
1280 | }
1281 |
1282 | // ~/go/src/github.com/v2fly/v2ray-core/v4/proxy/vmess/outbound/outbound.go
1283 | func QueryStats(direct string) int64 {
1284 | if statsManager == nil {
1285 | return 0
1286 | }
1287 | name := "vmess>>>" + "ssrray" + ">>>traffic>>>" + direct
1288 | // name := "user>>>" + "xxf098@github.com" + ">>>traffic>>>" + direct + "link"
1289 | counter := statsManager.GetCounter(name)
1290 | if counter == nil {
1291 | return 0
1292 | }
1293 | return counter.Set(0)
1294 | }
1295 |
1296 | // add in v2ray-core v4.26.0
1297 | func QueryOutboundStats(tag string, direct string) int64 {
1298 | if statsManager == nil {
1299 | return QueryOutboundXStats(tag, direct)
1300 | }
1301 | counter := statsManager.GetCounter(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
1302 | if counter == nil {
1303 | return 0
1304 | }
1305 | return counter.Set(0)
1306 | }
1307 |
1308 | func QueryOutboundXStats(tag string, direct string) int64 {
1309 | if xStatsManager == nil {
1310 | return 0
1311 | }
1312 | counter := xStatsManager.GetCounter(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
1313 | if counter == nil {
1314 | return 0
1315 | }
1316 | return counter.Set(0)
1317 | }
1318 |
1319 | // func queryStatsBg(log LogService) {
1320 | // for {
1321 | // if statsManager == nil {
1322 | // log.WriteLog("statsManager nil")
1323 | // return
1324 | // }
1325 | // name := "vmess>>>" + "ssrray" + ">>>traffic>>>" + "down"
1326 | // counter := statsManager.GetCounter(name)
1327 | // if counter == nil {
1328 | // log.WriteLog("counter nil")
1329 | // }
1330 | // time.Sleep(500 * time.Millisecond)
1331 | // }
1332 | // }
1333 |
1334 | func init() {
1335 | net.DefaultResolver = &net.Resolver{
1336 | PreferGo: true,
1337 | Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
1338 | d, _ := vnet.ParseDestination(fmt.Sprintf("%v:%v", network, localDNS))
1339 | return vinternet.DialSystem(ctx, d, nil)
1340 | },
1341 | }
1342 | debug.SetGCPercent(10)
1343 | }
1344 |
1345 | func CheckVersion() string {
1346 | return vcore.Version()
1347 | }
1348 |
1349 | func CheckXVersion() string {
1350 | return xcore.Version()
1351 | }
1352 |
1353 | // TODO: update base on version
1354 | func CopyAssets(assetDir string, force bool) error {
1355 | dats := [2]string{"geoip.dat", "geosite.dat"}
1356 | for _, dat := range dats {
1357 | _, err := os.Stat(assetDir + dat)
1358 | if os.IsNotExist(err) || force {
1359 | src, err := mobasset.Open("dat/" + dat)
1360 | if err != nil {
1361 | return err
1362 | }
1363 | dst, err := os.OpenFile(assetDir+dat, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
1364 | if err != nil {
1365 | return err
1366 | }
1367 | _, err = io.Copy(dst, src)
1368 | if err != nil {
1369 | return err
1370 | }
1371 | src.Close()
1372 | dst.Close()
1373 | }
1374 | }
1375 | return nil
1376 | }
1377 |
1378 | func initV2Env(assetperfix string) {
1379 | if os.Getenv(v2Asset) != "" {
1380 | return
1381 | }
1382 | //Initialize asset API, Since Raymond Will not let notify the asset location inside Process,
1383 | //We need to set location outside V2Ray
1384 | os.Setenv(v2Asset, assetperfix)
1385 | //Now we handle read
1386 | v2filesystem.NewFileReader = func(path string) (io.ReadCloser, error) {
1387 | if strings.HasPrefix(path, assetperfix) {
1388 | p := path[len(assetperfix)+1:]
1389 | //is it overridden?
1390 | //by, ok := overridedAssets[p]
1391 | //if ok {
1392 | // return os.Open(by)
1393 | //}
1394 | return mobasset.Open(p)
1395 | }
1396 | return os.Open(path)
1397 | }
1398 | }
1399 |
1400 | func TestConfig(ConfigureFileContent string, assetperfix string) error {
1401 | initV2Env(assetperfix)
1402 | // os.Setenv("v2ray.location.asset", assetperfix)
1403 | _, err := v2serial.LoadJSONConfig(strings.NewReader(ConfigureFileContent))
1404 | return err
1405 | }
1406 |
1407 | func TestVmessLatency(profile *Vmess, port int) (int64, error) {
1408 | // os.Setenv("v2ray.location.asset", assetPath)
1409 | var proxyPort = testProxyPort
1410 | if port > 0 && port < 65535 {
1411 | proxyPort = uint32(port)
1412 | } else {
1413 | proxyPort = uint32(0)
1414 | }
1415 | config, err := loadVmessTestConfig(profile, proxyPort)
1416 | if err != nil {
1417 | return 0, err
1418 | }
1419 | server, err := startInstance(profile, config)
1420 | if err != nil {
1421 | return 0, err
1422 | }
1423 | defer server.Close()
1424 | runtime.GC()
1425 | // socksProxy := fmt.Sprintf("socks5://127.0.0.1:%d", proxyPort)
1426 | // socksProxy, err := addInboundHandler(server)
1427 | // return testLatency(socksProxy)
1428 | if proxyPort == 0 {
1429 | return testLatencyWithHTTP(server)
1430 | } else {
1431 | c := make(chan latencyResult, 1)
1432 | go func() {
1433 | elapsed, err := testLatencyWithSocks5("127.0.0.1", proxyPort)
1434 | c <- latencyResult{elapsed, err}
1435 | }()
1436 | select {
1437 | case r := <-c:
1438 | return r.elapsed, r.err
1439 | case <-time.After(time.Second * 8):
1440 | return 0, fmt.Errorf("test profile timeout")
1441 | }
1442 | // return testLatencyWithSocks5("127.0.0.1", proxyPort)
1443 | }
1444 | }
1445 |
1446 | // TODO: support more protocol
1447 | func TestVmessLatencyDirect(profile *Vmess) (int64, error) {
1448 | opt, err := profile2Option(profile)
1449 | if err != nil {
1450 | return 0, err
1451 | }
1452 | return request.Ping(opt)
1453 | }
1454 |
1455 | func TestVmessDownload(profile *Vmess, timeout time.Duration, cb TestLatency) (int64, error) {
1456 | c := make(chan int64)
1457 | go func() {
1458 | for {
1459 | select {
1460 | case s := <-c:
1461 | if s < 0 {
1462 | return
1463 | }
1464 | // fmt.Println(download.ByteCountIEC(s))
1465 | cb.UpdateLatency(-1, s)
1466 | }
1467 | }
1468 | }()
1469 | return v2rayDownload(profile, 15*time.Second, c)
1470 | }
1471 |
1472 | func TestLinkDownloadSpeed(link string, cb TestLatencyStop) (int64, error) {
1473 | ctx, cancel := context.WithCancel(context.Background())
1474 | c := make(chan int64)
1475 | go func() {
1476 | for {
1477 | select {
1478 | case s := <-c:
1479 | if s < 0 {
1480 | return
1481 | }
1482 | // fmt.Println(download.ByteCountIEC(s))
1483 | isStop := cb.UpdateLatency(-1, s)
1484 | if isStop {
1485 | if ctx.Err() == nil {
1486 | cancel()
1487 | }
1488 | return
1489 | }
1490 | }
1491 | }
1492 | }()
1493 | return download.DownloadRange(ctx, link, 2, 15*time.Second, 15*time.Second, c, nil)
1494 | }
1495 |
1496 | func BatchTestDownload(link string, concurrency int, testDownload TestDownload) error {
1497 | if concurrency < 1 {
1498 | concurrency = 5
1499 | }
1500 | links := strings.Split(link, ",")
1501 | resultCh := ping.DownloadLinksSpeed(links, concurrency)
1502 | for r := range resultCh {
1503 | if r.Protocol == ping.PROTOCOL_SPEED {
1504 | testDownload.UpdateSpeed(r.Index, r.Result, r.Elapse)
1505 | }
1506 | if r.Protocol == ping.PROTOCOL_TRAFFIC {
1507 | testDownload.UpdateTraffic(r.Index, r.Result)
1508 | }
1509 | }
1510 | return nil
1511 | }
1512 |
1513 | func BatchRenderTestDownload(link string, concurrency int, fontPath string, pngPath string, language string, urlGroup string, testDownload TestDownload) error {
1514 | if concurrency < 1 {
1515 | concurrency = 5
1516 | }
1517 | links := strings.Split(link, ",")
1518 | resultCh := ping.RenderDownloadLinksSpeedAndroid(links, concurrency, fontPath, pngPath, language, urlGroup)
1519 | for r := range resultCh {
1520 | if r.Protocol == ping.PROTOCOL_SPEED {
1521 | testDownload.UpdateSpeed(r.Index, r.Result, r.Elapse)
1522 | }
1523 | if r.Protocol == ping.PROTOCOL_TRAFFIC {
1524 | testDownload.UpdateTraffic(r.Index, r.Result)
1525 | }
1526 | }
1527 | return nil
1528 | }
1529 |
1530 | // FIXME: block on startup
1531 | func BatchTestVmessCoreLatency(link string, concurrency int, testLatency TestLatency) {
1532 | if concurrency < 1 {
1533 | concurrency = 5
1534 | }
1535 | links := strings.Split(link, ",")
1536 | resultCh := ping.BatchTestLinks(links, concurrency, []ping.RunFunc{runCore})
1537 | for range links {
1538 | select {
1539 | case r := <-resultCh:
1540 | testLatency.UpdateLatency(r.Index, r.Result)
1541 | }
1542 | }
1543 | }
1544 |
1545 | func BatchTestLatency(link string, concurrency int, testLatency TestLatency) {
1546 | if concurrency < 1 {
1547 | concurrency = 5
1548 | }
1549 | links := strings.Split(link, ",")
1550 | resultCh := ping.PingLinksLatency(links, concurrency, []ping.RunFunc{runCore})
1551 | for range links {
1552 | select {
1553 | case r := <-resultCh:
1554 | testLatency.UpdateLatency(r.Index, r.Result)
1555 | }
1556 | }
1557 | }
1558 |
1559 | func TestTrojanLatency(trojan *Trojan) (int64, error) {
1560 | profile := trojan.toVmess()
1561 | return TestVmessLatency(profile, -1)
1562 | }
1563 |
1564 | func TestURLLatency(url string) (int64, error) {
1565 | return testLatency(url)
1566 | }
1567 |
1568 | func TestTCPPing(host string, port int) (int64, error) {
1569 | tcpping := ping.NewTCPPing(host, port)
1570 | result := <-tcpping.Start()
1571 | return result.Get()
1572 | }
1573 |
1574 | func TestConfigLatency(configBytes []byte, assetPath string) (int64, error) {
1575 | os.Setenv(v2Asset, assetPath)
1576 | server, err := vcore.StartInstance("json", configBytes)
1577 | if err != nil {
1578 | return 0, err
1579 | }
1580 | defer server.Close()
1581 | runtime.GC()
1582 | socksProxy, err := addInboundHandler(server)
1583 | if err != nil {
1584 | return 0, err
1585 | }
1586 | return testLatency(socksProxy)
1587 | }
1588 |
1589 | func ConvertJSONToVmess(configBytes []byte) (*Vmess, error) {
1590 | vmess := &Vmess{
1591 | Host: "",
1592 | Path: "",
1593 | TLS: "",
1594 | Add: "",
1595 | Port: 0,
1596 | Aid: 0,
1597 | Net: "",
1598 | ID: "",
1599 | Type: "",
1600 | Security: "",
1601 | }
1602 | config, err := DecodeJSONConfig(bytes.NewReader(configBytes))
1603 | if err != nil {
1604 | return nil, err
1605 | }
1606 | outboundConfig := config.OutboundConfigs[0]
1607 | settings := []byte("{}")
1608 | if outboundConfig.Settings != nil {
1609 | settings = ([]byte)(*outboundConfig.Settings)
1610 | }
1611 | outboundConfigLoader := conf.NewJSONConfigLoader(conf.ConfigCreatorCache{
1612 | "blackhole": func() interface{} { return new(conf.BlackholeConfig) },
1613 | "freedom": func() interface{} { return new(conf.FreedomConfig) },
1614 | // "http": func() interface{} { return new(conf.HttpClientConfig) },
1615 | "shadowsocks": func() interface{} { return new(conf.ShadowsocksClientConfig) },
1616 | "vmess": func() interface{} { return new(conf.VMessOutboundConfig) },
1617 | "vless": func() interface{} { return new(conf.VLessOutboundConfig) },
1618 | "socks": func() interface{} { return new(conf.SocksClientConfig) },
1619 | // "mtproto": func() interface{} { return new(conf.MTProtoClientConfig) },
1620 | "dns": func() interface{} { return new(conf.DNSOutboundConfig) },
1621 | }, "protocol", "settings")
1622 | if outboundConfig.Protocol != "vmess" && outboundConfig.Protocol != "vless" {
1623 | return vmess, err
1624 | }
1625 | rawConfig, err := outboundConfigLoader.LoadWithID(settings, outboundConfig.Protocol)
1626 | if err != nil {
1627 | return nil, err
1628 | }
1629 | if outboundConfig.StreamSetting != nil {
1630 | vmess.Net = string(*outboundConfig.StreamSetting.Network)
1631 | }
1632 | if outboundConfig.Protocol == "vmess" {
1633 | vmess.Protocol = v2ray.VMESS
1634 | vmessOutboundConfig, ok := rawConfig.(*conf.VMessOutboundConfig)
1635 | if !ok {
1636 | return nil, newError("Not A VMess Config")
1637 | }
1638 | for _, vnext := range vmessOutboundConfig.Receivers {
1639 | vmess.Add = vnext.Address.String()
1640 | vmess.Port = int(vnext.Port)
1641 | account := new(conf.VMessAccount)
1642 | for _, rawUser := range vnext.Users {
1643 | if err := json.Unmarshal(rawUser, account); err == nil {
1644 | vmess.ID = account.ID
1645 | // vmess.Aid = int(account.AlterIds)
1646 | vmess.Security = account.Security
1647 | }
1648 | }
1649 | }
1650 | }
1651 | if outboundConfig.Protocol == "vless" {
1652 | vlessOutboundConfig, ok := rawConfig.(*conf.VLessOutboundConfig)
1653 | if !ok {
1654 | return nil, newError("Not A VLess Config")
1655 | }
1656 | vmess.Protocol = v2ray.VLESS
1657 | for _, vnext := range vlessOutboundConfig.Vnext {
1658 | vmess.Add = vnext.Address.String()
1659 | vmess.Port = int(vnext.Port)
1660 | account := new(conf.VMessAccount)
1661 | for _, rawUser := range vnext.Users {
1662 | if err := json.Unmarshal(rawUser, account); err == nil {
1663 | vmess.ID = account.ID
1664 | // vmess.Aid = int(account.AlterIds)
1665 | vmess.Security = account.Security
1666 | }
1667 | }
1668 | }
1669 | }
1670 | return vmess, nil
1671 | }
1672 |
1673 | func GetFreePort() (int, error) {
1674 | addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
1675 | if err != nil {
1676 | return 0, err
1677 | }
1678 |
1679 | l, err := net.ListenTCP("tcp", addr)
1680 | if err != nil {
1681 | return 0, err
1682 | }
1683 | defer l.Close()
1684 | return l.Addr().(*net.TCPAddr).Port, nil
1685 | }
1686 |
1687 | // resolve dns
1688 | func Resolve(addr string, enableIPv6 bool, hostname string) (string, error) {
1689 | servers := []ldns.NameServer{
1690 | {
1691 | Net: "udp",
1692 | Addr: hostname,
1693 | },
1694 | }
1695 | c := ldns.Config{
1696 | Main: servers,
1697 | Default: servers,
1698 | }
1699 | resolver := ldns.NewResolver(c)
1700 | var err error
1701 | var ip net.IP
1702 | if enableIPv6 {
1703 | ip, err = resolver.ResolveIP(addr)
1704 | } else {
1705 | ip, err = resolver.ResolveIPv4(addr)
1706 | }
1707 | if err != nil {
1708 | return "", err
1709 | }
1710 | return ip.String(), nil
1711 | }
1712 |
1713 | // convert clash file to vmess/trojan links, links are separated by newline(\n)
1714 | func ParseClash(message string) (string, error) {
1715 | opt := web.ParseOption{Type: web.PARSE_CLASH}
1716 | links, err := web.ParseLinksWithOption(message, opt)
1717 | if err != nil {
1718 | return "", err
1719 | }
1720 | return strings.Join(links, "\n"), nil
1721 | }
1722 |
1723 | // get top n links of clash file
1724 | func PeekClash(message string, n int) (string, error) {
1725 | links, err := web.PeekClash(message, 2)
1726 | if err != nil {
1727 | opt := web.ParseOption{Type: web.PARSE_CLASH}
1728 | links, err = web.ParseLinksWithOption(message, opt)
1729 | if err != nil {
1730 | return "", err
1731 | }
1732 | }
1733 | endIndex := n
1734 | if endIndex > len(links) {
1735 | endIndex = len(links)
1736 | }
1737 | return strings.Join(links[:endIndex], "\n"), nil
1738 | }
1739 |
--------------------------------------------------------------------------------
/tunnel/tun.go:
--------------------------------------------------------------------------------
1 | package tunnel
2 |
3 | import (
4 | "errors"
5 | "io"
6 | "os"
7 | "syscall"
8 | )
9 |
10 | type Interface struct {
11 | io.ReadWriteCloser
12 | ReadCh chan []byte
13 | StopCh chan bool
14 | }
15 |
16 | func OpenTunDevice(tunFd int) (*Interface, error) {
17 | if tunFd < 0 {
18 | return nil, errors.New("must provide a valid TUN file descriptor")
19 | }
20 | file := os.NewFile(uintptr(tunFd), "tun")
21 | _ = syscall.SetNonblock(tunFd, true)
22 | tunDev := &Interface{
23 | ReadWriteCloser: file,
24 | StopCh: make(chan bool, 2),
25 | ReadCh: make(chan []byte, 2000),
26 | }
27 | return tunDev, nil
28 | }
29 |
--------------------------------------------------------------------------------
/v2ray/features.go:
--------------------------------------------------------------------------------
1 | package v2ray
2 |
3 | /*
4 | import (
5 | // The following are necessary as they register handlers in their init functions.
6 |
7 | // Required features. Can't remove unless there is replacements.
8 | _ "github.com/v2fly/v2ray-core/v4/app/dispatcher"
9 | _ "github.com/v2fly/v2ray-core/v4/app/proxyman/inbound"
10 | _ "github.com/v2fly/v2ray-core/v4/app/proxyman/outbound"
11 |
12 | // Default commander and all its services. This is an optional feature.
13 | // _ "github.com/v2fly/v2ray-core/v4/app/commander"
14 | // _ "github.com/v2fly/v2ray-core/v4/app/log/command"
15 | // _ "github.com/v2fly/v2ray-core/v4/app/proxyman/command"
16 | // _ "github.com/v2fly/v2ray-core/v4/app/stats/command"
17 |
18 | // Developer preview services
19 | // _ "github.com/v2fly/v2ray-core/v4/app/observatory/command"
20 |
21 | // Other optional features.
22 | _ "github.com/v2fly/v2ray-core/v4/app/dns"
23 | // _ "github.com/v2fly/v2ray-core/v4/app/dns/fakedns"
24 | _ "github.com/v2fly/v2ray-core/v4/app/log"
25 | _ "github.com/v2fly/v2ray-core/v4/app/policy"
26 |
27 | // _ "github.com/v2fly/v2ray-core/v4/app/reverse"
28 | _ "github.com/v2fly/v2ray-core/v4/app/router"
29 | _ "github.com/v2fly/v2ray-core/v4/app/stats"
30 |
31 | // Fix dependency cycle caused by core import in internet package
32 | // _ "github.com/v2fly/v2ray-core/v4/transport/internet/tagged/taggedimpl"
33 |
34 | // Developer preview features
35 | // _ "github.com/v2fly/v2ray-core/v4/app/observatory"
36 |
37 | // Inbound and outbound proxies.
38 | _ "github.com/v2fly/v2ray-core/v4/proxy/blackhole"
39 | // _ "github.com/v2fly/v2ray-core/v4/proxy/dns"
40 | _ "github.com/v2fly/v2ray-core/v4/proxy/dokodemo"
41 | _ "github.com/v2fly/v2ray-core/v4/proxy/freedom"
42 | _ "github.com/v2fly/v2ray-core/v4/proxy/http"
43 |
44 | // _ "github.com/v2fly/v2ray-core/v4/proxy/mtproto"
45 | _ "github.com/v2fly/v2ray-core/v4/proxy/shadowsocks"
46 | _ "github.com/v2fly/v2ray-core/v4/proxy/socks"
47 | _ "github.com/v2fly/v2ray-core/v4/proxy/trojan"
48 | _ "github.com/v2fly/v2ray-core/v4/proxy/vmess/inbound"
49 | _ "github.com/v2fly/v2ray-core/v4/proxy/vmess/outbound"
50 |
51 | // Transports
52 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/domainsocket"
53 | // _ "github.com/v2fly/v2ray-core/v4/transport/internet/grpc"
54 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/http"
55 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/kcp"
56 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/quic"
57 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/tcp"
58 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/tls"
59 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/udp"
60 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/websocket"
61 |
62 | // Transport headers
63 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/headers/http"
64 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/headers/noop"
65 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/headers/srtp"
66 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/headers/tls"
67 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/headers/utp"
68 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/headers/wechat"
69 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/headers/wireguard"
70 |
71 | // JSON config support. Choose only one from the two below.
72 | // The following line loads JSON from v2ctl
73 | // _ "github.com/v2fly/v2ray-core/v4/main/json"
74 | // The following line loads JSON internally
75 | _ "github.com/v2fly/v2ray-core/v4/main/jsonem"
76 | // Load config from file or http(s)
77 | // _ "github.com/v2fly/v2ray-core/v4/main/confloader/external"
78 | // geodata
79 | // _ "github.com/v2fly/v2ray-core/v4/infra/conf/geodata/memconservative"
80 | _ "github.com/v2fly/v2ray-core/v4/infra/conf/geodata/standard"
81 | )
82 | */
83 |
--------------------------------------------------------------------------------
/v2ray/features_other.go:
--------------------------------------------------------------------------------
1 | //go:build !ios && !android
2 | // +build !ios,!android
3 |
4 | package v2ray
5 |
6 | /*
7 | import (
8 | _ "github.com/v2fly/v2ray-core/v4/app/commander"
9 | _ "github.com/v2fly/v2ray-core/v4/app/log/command"
10 | _ "github.com/v2fly/v2ray-core/v4/app/proxyman/command"
11 | _ "github.com/v2fly/v2ray-core/v4/app/stats/command"
12 |
13 | _ "github.com/v2fly/v2ray-core/v4/app/reverse"
14 |
15 | _ "github.com/v2fly/v2ray-core/v4/transport/internet/domainsocket"
16 | )
17 | */
18 |
--------------------------------------------------------------------------------
/v2ray/hosts.go:
--------------------------------------------------------------------------------
1 | package v2ray
2 |
3 | import (
4 | "github.com/xtls/xray-core/common/net"
5 | "github.com/xtls/xray-core/infra/conf"
6 | )
7 |
8 | var localhost = conf.NewHostAddress(&conf.Address{net.IPAddress([]byte{0, 0, 0, 0})}, nil)
9 | var google = conf.NewHostAddress(&conf.Address{net.DomainAddress("googleapis.com")}, nil)
10 |
11 | // no prefix is fullmatch
12 | var BlockHosts = map[string]*conf.HostAddress{
13 | // "domain:umeng.com": localhost,
14 | // "domain:baidu.com": localhost,
15 | // "domain:sogou.com": localhost,
16 | "domain:doubleclick.net": localhost,
17 | // "domain:byteimg.com": localhost,
18 | // "domain:ixigua.com": localhost,
19 | // "domain:snssdk.com": localhost,
20 | // "domain:uc.com": localhost,
21 | // "domain:uc.cn": localhost,
22 | // "domain:umengcloud.com": localhost,
23 | // "keyword:baidustatic": localhost,
24 | "keyword:auspiciousvp": localhost,
25 | "domain:cnzz.com": localhost,
26 | // "domain:toutiaopage.com": localhost,
27 | // "domain:douyin.com": localhost,
28 | // "domain:bdstatic.com": localhost,
29 | // "domain:360.cn": localhost,
30 | // "domain:umtrack.com": localhost,
31 | // "domain:umsns.com": localhost,
32 | // "domain:qhupdate.com": localhost,
33 | // "domain:qhimg.com": localhost,
34 | "go-updater.brave.com": localhost,
35 | "at3.doubanio.com": localhost,
36 | "p.pinduoduo.com": localhost,
37 | "domain:googleapis.cn": google,
38 | }
39 |
40 | // no prefix is substr
41 | var BlockDomains = []string{
42 | "175.25.22.142",
43 | "175.25.22.149",
44 | "175.25.22.141",
45 | "auspiciousvp.com",
46 | // "www.auspiciousvp.com",
47 | "cnzz.com",
48 | // "toutiao",
49 | // "snssdk.com",
50 | // "ixiguavideo.com",
51 | // "domian:sogou.com",
52 | "domian:doubleclick.net",
53 | "360totalsecurity.com",
54 | "ad.doubanio.com",
55 | "at3.doubanio.com",
56 | "p.pinduoduo.com",
57 | }
58 |
59 | var DirectDomains = []string{
60 | "brave.com",
61 | }
62 |
--------------------------------------------------------------------------------
/v2ray/tcp.go:
--------------------------------------------------------------------------------
1 | package v2ray
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io"
7 | "net"
8 |
9 | vnet "github.com/xtls/xray-core/common/net"
10 | vsession "github.com/xtls/xray-core/common/session"
11 | vcore "github.com/xtls/xray-core/core"
12 |
13 | "github.com/eycorsican/go-tun2socks/core"
14 | "github.com/xtls/xray-core/common/bytespool"
15 | "github.com/xxf098/go-tun2socks-build/pool"
16 | N "github.com/xxf098/lite-proxy/common/net"
17 | )
18 |
19 | type tcpHandler struct {
20 | ctx context.Context
21 | v *vcore.Instance
22 | }
23 |
24 | // sniff address remove google 80
25 | func (h *tcpHandler) relay(lhs net.Conn, rhs net.Conn, addr string) {
26 | go func() {
27 | buf := bytespool.Alloc(pool.BufSize)
28 | _, err := io.CopyBuffer(N.WriteOnlyWriter{Writer: lhs}, N.ReadOnlyReader{Reader: rhs}, buf)
29 | if err != nil {
30 | fmt.Printf("relay: lhs %s, %s\n", addr, err)
31 | }
32 | bytespool.Free(buf)
33 | lhs.Close()
34 | rhs.Close()
35 | }()
36 | buf := bytespool.Alloc(pool.BufSize)
37 | // io.CopyBuffer(lhs, rhs, buf)
38 | _, err := io.CopyBuffer(N.WriteOnlyWriter{Writer: rhs}, N.ReadOnlyReader{Reader: lhs}, buf)
39 | if err != nil {
40 | fmt.Printf("relay: rhs %s, %s\n", addr, err)
41 | }
42 | bytespool.Free(buf)
43 | lhs.Close()
44 | rhs.Close()
45 | }
46 |
47 | func NewTCPHandler(ctx context.Context, instance *vcore.Instance) core.TCPConnHandler {
48 | return &tcpHandler{
49 | ctx: ctx,
50 | v: instance,
51 | }
52 | }
53 |
54 | func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error {
55 | dest := vnet.DestinationFromAddr(target)
56 | sid := vsession.NewID()
57 | ctx := vsession.ContextWithID(h.ctx, sid)
58 | c, err := vcore.Dial(ctx, h.v, dest)
59 | if err != nil {
60 | return fmt.Errorf("dial V proxy connection failed: %v", err)
61 | }
62 | go h.relay(conn, c, target.String())
63 | return nil
64 | }
65 |
--------------------------------------------------------------------------------
/v2ray/udp.go:
--------------------------------------------------------------------------------
1 | package v2ray
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "net"
8 | "sync"
9 | "time"
10 |
11 | vsession "github.com/xtls/xray-core/common/session"
12 | vsignal "github.com/xtls/xray-core/common/signal"
13 | vtask "github.com/xtls/xray-core/common/task"
14 | vcore "github.com/xtls/xray-core/core"
15 |
16 | "github.com/eycorsican/go-tun2socks/common/log"
17 | "github.com/eycorsican/go-tun2socks/core"
18 | "github.com/xtls/xray-core/common/bytespool"
19 | "github.com/xxf098/go-tun2socks-build/pool"
20 | )
21 |
22 | type udpConnEntry struct {
23 | conn net.PacketConn
24 |
25 | // `ReadFrom` method of PacketConn given by V2Ray
26 | // won't return the correct remote address, we treat
27 | // all data receive from V2Ray are coming from the
28 | // same remote host, i.e. the `target` that passed
29 | // to `Connect`.
30 | target *net.UDPAddr
31 |
32 | updater vsignal.ActivityUpdater
33 | }
34 |
35 | type udpHandler struct {
36 | sync.Mutex
37 |
38 | ctx context.Context
39 | v *vcore.Instance
40 | conns map[core.UDPConn]*udpConnEntry
41 | timeout time.Duration // Maybe override by V2Ray local policies for some conns.
42 | }
43 |
44 | func (h *udpHandler) fetchInput(conn core.UDPConn) {
45 | h.Lock()
46 | c, ok := h.conns[conn]
47 | h.Unlock()
48 | if !ok {
49 | return
50 | }
51 |
52 | buf := bytespool.Alloc(pool.BufSize)
53 | defer bytespool.Free(buf)
54 |
55 | for {
56 | n, _, err := c.conn.ReadFrom(buf)
57 | if err != nil && n <= 0 {
58 | h.Close(conn)
59 | conn.Close()
60 | return
61 | }
62 | c.updater.Update()
63 | _, err = conn.WriteFrom(buf[:n], c.target)
64 | if err != nil {
65 | h.Close(conn)
66 | conn.Close()
67 | return
68 | }
69 | }
70 | }
71 |
72 | func NewUDPHandler(ctx context.Context, instance *vcore.Instance, timeout time.Duration) core.UDPConnHandler {
73 | return &udpHandler{
74 | ctx: ctx,
75 | v: instance,
76 | conns: make(map[core.UDPConn]*udpConnEntry, 16),
77 | timeout: timeout,
78 | }
79 | }
80 |
81 | func (h *udpHandler) Connect(conn core.UDPConn, target *net.UDPAddr) error {
82 | if target == nil {
83 | return errors.New("nil target is not allowed")
84 | }
85 | sid := vsession.NewID()
86 | ctx := vsession.ContextWithID(h.ctx, sid)
87 | ctx, cancel := context.WithCancel(ctx)
88 | pc, err := vcore.DialUDP(ctx, h.v)
89 | if err != nil {
90 | cancel()
91 | return fmt.Errorf("dial V proxy connection failed: %v", err)
92 | }
93 | timer := vsignal.CancelAfterInactivity(ctx, cancel, h.timeout)
94 | h.Lock()
95 | h.conns[conn] = &udpConnEntry{
96 | conn: pc,
97 | target: target,
98 | updater: timer,
99 | }
100 | h.Unlock()
101 | fetchTask := func() error {
102 | h.fetchInput(conn)
103 | return nil
104 | }
105 | go func() {
106 | if err := vtask.Run(ctx, fetchTask); err != nil {
107 | pc.Close()
108 | }
109 | }()
110 | log.Infof("new proxy connection for target: %s:%s", target.Network(), target.String())
111 | return nil
112 | }
113 |
114 | func (h *udpHandler) ReceiveTo(conn core.UDPConn, data []byte, addr *net.UDPAddr) error {
115 | h.Lock()
116 | c, ok := h.conns[conn]
117 | h.Unlock()
118 |
119 | if ok {
120 | _, err := c.conn.WriteTo(data, addr)
121 | c.updater.Update()
122 | if err != nil {
123 | h.Close(conn)
124 | return fmt.Errorf("write remote failed: %v", err)
125 | }
126 | return nil
127 | } else {
128 | h.Close(conn)
129 | return fmt.Errorf("proxy connection %v->%v does not exists", conn.LocalAddr(), addr)
130 | }
131 | }
132 |
133 | func (h *udpHandler) Close(conn core.UDPConn) {
134 | h.Lock()
135 | defer h.Unlock()
136 |
137 | if c, found := h.conns[conn]; found {
138 | c.conn.Close()
139 | }
140 | delete(h.conns, conn)
141 | }
142 |
--------------------------------------------------------------------------------
/v2ray/vmess.go:
--------------------------------------------------------------------------------
1 | package v2ray
2 |
3 | const (
4 | VMESS string = "vmess"
5 | VLESS string = "vless"
6 | TROJAN string = "trojan"
7 | SHADOWSOCKS string = "shadowsocks"
8 | )
9 |
10 | type VmessConfig struct {
11 | DNS DNS `json:"dns"`
12 | Inbounds []Inbounds `json:"inbounds,omitempty"`
13 | Log Log `json:"log"`
14 | Outbounds []Outbounds `json:"outbounds"`
15 | Routing Routing `json:"routing"`
16 | Policy Policy `json:"policy"`
17 | Stats Stats `json:"stats"`
18 | }
19 |
20 | type Stats struct {
21 | }
22 |
23 | type Policy struct {
24 | Levels map[string]Level `json:"levels"`
25 | System System `json:"system"`
26 | }
27 |
28 | type Level struct {
29 | ConnIdle int `json:"connIdle"`
30 | DownlinkOnly int `json:"downlinkOnly"`
31 | Handshake int `json:"handshake"`
32 | UplinkOnly int `json:"uplinkOnly"`
33 | }
34 |
35 | type System struct {
36 | StatsOutboundUplink bool `json:"statsOutboundUplink"`
37 | StatsOutboundDownlink bool `json:"statsOutboundDownlink"`
38 | }
39 |
40 | type Hosts map[string]string
41 |
42 | type DNS struct {
43 | Hosts Hosts `json:"hosts"`
44 | Servers []string `json:"servers"`
45 | }
46 | type InboundsSettings struct {
47 | Auth string `json:"auth"`
48 | IP string `json:"ip"`
49 | UDP bool `json:"udp"`
50 | }
51 | type Inbounds struct {
52 | Listen string `json:"listen"`
53 | Port int `json:"port"`
54 | Protocol string `json:"protocol"`
55 | InboundsSettings *InboundsSettings `json:"settings,omitempty"`
56 | Tag string `json:"tag"`
57 | }
58 | type Log struct {
59 | Access string `json:"access"`
60 | Error string `json:"error"`
61 | Loglevel string `json:"loglevel"`
62 | }
63 | type Mux struct {
64 | Enabled bool `json:"enabled"`
65 | Concurrency int `json:"concurrency"`
66 | }
67 | type Users struct {
68 | AlterID int `json:"alterId"`
69 | Email string `json:"email"`
70 | ID string `json:"id"`
71 | Security string `json:"security"`
72 | Level int `json:"level"`
73 | }
74 | type Vnext struct {
75 | Address string `json:"address"`
76 | Port int `json:"port"`
77 | Users []Users `json:"users"`
78 | }
79 | type OutboundsSettings struct {
80 | Vnext []Vnext `json:"vnext,omitempty"`
81 | DomainStrategy string `json:"domainStrategy,omitempty"`
82 | }
83 |
84 | type VlessUser struct {
85 | Encryption string `json:"encryption"`
86 | Flow string `json:"flow"`
87 | ID string `json:"id"`
88 | Level int `json:"level"`
89 | Security string `json:"security"`
90 | }
91 |
92 | type Vlessnext struct {
93 | Address string `json:"address"`
94 | Port int `json:"port"`
95 | Users []VlessUser `json:"users"`
96 | }
97 |
98 | type VlessOutboundsSettings struct {
99 | Vnext []Vlessnext `json:"vnext,omitempty"`
100 | DomainStrategy string `json:"domainStrategy,omitempty"`
101 | }
102 |
103 | type TcpHeader struct {
104 | Type string `json:"type,omitempty"`
105 | }
106 | type Headers struct {
107 | Host string `json:"Host"`
108 | }
109 | type Wssettings struct {
110 | ConnectionReuse bool `json:"connectionReuse"`
111 | Headers Headers `json:"headers,omitempty"`
112 | Path string `json:"path"`
113 | }
114 |
115 | type QUICSettingsHeader struct {
116 | Type string `json:"type"`
117 | }
118 |
119 | type KCPSettingsHeader struct {
120 | Type string `json:"type"`
121 | }
122 | type TCPSettingsHeader struct {
123 | Type string `json:"type"`
124 | TCPSettingsRequest TCPSettingsRequest `json:"request"`
125 | }
126 |
127 | type TCPSettingsRequest struct {
128 | Version string `json:"version"`
129 | Method string `json:"method"`
130 | Path []string `json:"path"`
131 | Headers HTTPHeaders `json:"headers"`
132 | }
133 |
134 | type HTTPHeaders struct {
135 | UserAgent []string `json:"User-Agent"`
136 | AcceptEncoding []string `json:"Accept-Encoding"`
137 | Connection string `json:"Connection"`
138 | Pragma string `json:"Pragma"`
139 | Host []string `json:"Host"`
140 | }
141 |
142 | type TLSSettings struct {
143 | AllowInsecure bool `json:"allowInsecure"`
144 | }
145 |
146 | type StreamSettings struct {
147 | Network string `json:"network"`
148 | Wssettings *Wssettings `json:"wssettings,omitempty"`
149 | Security string `json:"security"`
150 | TLSSettings *TLSSettings `json:"tlsSettings,omitempty"`
151 | }
152 |
153 | type Outbounds struct {
154 | Mux *Mux `json:"mux,omitempty"`
155 | Protocol string `json:"protocol"`
156 | Settings OutboundsSettings `json:"settings,omitempty"`
157 | StreamSettings *StreamSettings `json:"streamSettings,omitempty"`
158 | Tag string `json:"tag"`
159 | }
160 | type Rules struct {
161 | IP []string `json:"ip,omitempty"`
162 | OutboundTag string `json:"outboundTag"`
163 | Type string `json:"type"`
164 | Domain []string `json:"domain,omitempty"`
165 | }
166 | type Routing struct {
167 | DomainStrategy string `json:"domainStrategy"`
168 | Rules []Rules `json:"rules"`
169 | }
170 |
--------------------------------------------------------------------------------
/xray/config.go:
--------------------------------------------------------------------------------
1 | package xray
2 |
3 | import (
4 | "encoding/json"
5 | "strings"
6 |
7 | "github.com/xtls/xray-core/common/net"
8 | "github.com/xtls/xray-core/infra/conf"
9 | "github.com/xxf098/go-tun2socks-build/features"
10 | "github.com/xxf098/go-tun2socks-build/settings"
11 | "github.com/xxf098/go-tun2socks-build/v2ray"
12 | )
13 |
14 | var localhost = conf.NewHostAddress(&conf.Address{Address: net.IPAddress([]byte{0, 0, 0, 0})}, nil)
15 | var googleapis = conf.NewHostAddress(&conf.Address{Address: net.DomainAddress("googleapis.com")}, nil)
16 |
17 | var BlockHosts = map[string]*conf.HostAddress{
18 | // "domain:umeng.com": localhost,
19 | // "domain:baidu.com": localhost,
20 | // "domain:sogou.com": localhost,
21 | "domain:doubleclick.net": localhost,
22 | // "domain:byteimg.com": localhost,
23 | // "domain:ixigua.com": localhost,
24 | // "domain:snssdk.com": localhost,
25 | // "domain:uc.com": localhost,
26 | // "domain:uc.cn": localhost,
27 | // "domain:umengcloud.com": localhost,
28 | // "keyword:baidustatic": localhost,
29 | "keyword:auspiciousvp": localhost,
30 | "domain:cnzz.com": localhost,
31 | // "domain:toutiaopage.com": localhost,
32 | // "domain:douyin.com": localhost,
33 | // "domain:bdstatic.com": localhost,
34 | // "domain:360.cn": localhost,
35 | // "domain:umtrack.com": localhost,
36 | // "domain:umsns.com": localhost,
37 | // "domain:qhupdate.com": localhost,
38 | // "domain:qhimg.com": localhost,
39 | "at3.doubanio.com": localhost,
40 | "p.pinduoduo.com": localhost,
41 | "pos.baidu.com": localhost,
42 | "hm.baidu.com": localhost,
43 | "cpro.baidu.com": localhost,
44 | "dns.google": conf.NewHostAddress(&conf.Address{Address: net.IPAddress([]byte{8, 8, 8, 8})}, nil),
45 | "domain:googleapis.cn": googleapis,
46 | }
47 |
48 | func createDNSConfig(routeMode int, dnsConf string) *conf.DNSConfig {
49 | // nameServerConfig := []*conf.NameServerConfig{
50 | // &conf.NameServerConfig{
51 | // Address: &conf.Address{vnet.IPAddress([]byte{223, 5, 5, 5})},
52 | // Port: 53,
53 | // // Domains: []string{"geosite:cn"},
54 | // },
55 | // &conf.NameServerConfig{Address: &conf.Address{vnet.IPAddress([]byte{1, 1, 1, 1})}, Port: 53},
56 | // }
57 | // if routeMode == 2 || routeMode == 3 || routeMode == 4 {
58 | // nameServerConfig = []*conf.NameServerConfig{
59 | // &conf.NameServerConfig{Address: &conf.Address{vnet.IPAddress([]byte{1, 1, 1, 1})}, Port: 53},
60 | // }
61 | // }
62 | dns := strings.Split(dnsConf, ",")
63 | nameServerConfig := []*conf.NameServerConfig{}
64 | if routeMode == 2 || routeMode == 3 || routeMode == 4 {
65 | for i := len(dns) - 1; i >= 0; i-- {
66 | if newConfig := toNameServerConfig(dns[i]); newConfig != nil {
67 | if i == 1 {
68 | newConfig.Domains = []string{"geosite:cn"}
69 | }
70 | nameServerConfig = append(nameServerConfig, newConfig)
71 | }
72 | }
73 | } else {
74 | if newConfig := toNameServerConfig(dns[0]); newConfig != nil {
75 | nameServerConfig = append(nameServerConfig, newConfig)
76 | }
77 | }
78 | return &conf.DNSConfig{
79 | Hosts: &conf.HostsWrapper{Hosts: BlockHosts},
80 | Servers: nameServerConfig,
81 | }
82 | }
83 |
84 | // 0 all
85 | // 1 bypass LAN
86 | // 2 bypass China
87 | // 3 bypass LAN & China
88 | // 4 GFWList
89 | // 5 ChinaList
90 | // >= 6 bypass LAN & China & AD block
91 | //
92 | // 0: "Plain", 1: "Regex", 2: "Domain", 3: "Full",
93 | //
94 | // https://github.com/Loyalsoldier/v2ray-rules-dat
95 | func createRouterConfig(routeMode int) *conf.RouterConfig {
96 | domainStrategy := "IPIfNonMatch"
97 | bypassLAN, _ := json.Marshal(v2ray.Rules{
98 | Type: "field",
99 | OutboundTag: "direct",
100 | IP: []string{"geoip:private"},
101 | })
102 | bypassChinaIP, _ := json.Marshal(v2ray.Rules{
103 | Type: "field",
104 | OutboundTag: "direct",
105 | IP: []string{"geoip:cn"},
106 | })
107 | bypassChinaSite, _ := json.Marshal(v2ray.Rules{
108 | Type: "field",
109 | OutboundTag: "direct",
110 | Domain: []string{"geosite:cn"},
111 | })
112 | blockDomain, _ := json.Marshal(v2ray.Rules{
113 | Type: "field",
114 | OutboundTag: "blocked",
115 | Domain: v2ray.BlockDomains,
116 | })
117 | directDomains, _ := json.Marshal(v2ray.Rules{
118 | Type: "field",
119 | OutboundTag: "direct",
120 | Domain: v2ray.DirectDomains,
121 | })
122 | // blockAd, _ := json.Marshal(v2ray.Rules{
123 | // Type: "field",
124 | // OutboundTag: "blocked",
125 | // Domain: []string{"geosite:category-ads-all"},
126 | // })
127 | gfwList, _ := json.Marshal(v2ray.Rules{
128 | Type: "field",
129 | OutboundTag: "proxy",
130 | Domain: []string{"geosite:geolocation-!cn"},
131 | })
132 | gfwListIP, _ := json.Marshal(v2ray.Rules{
133 | Type: "field",
134 | OutboundTag: "proxy",
135 | IP: []string{
136 | "8.8.8.8/32",
137 | "8.8.4.4/32",
138 | "1.1.1.1/32",
139 | "1.0.0.1/32",
140 | "149.154.160.0/22",
141 | "149.154.164.0/22",
142 | "91.108.4.0/22",
143 | "91.108.56.0/22",
144 | "91.108.8.0/22",
145 | "95.161.64.0/20",
146 | },
147 | })
148 | chinaListSite, _ := json.Marshal(v2ray.Rules{
149 | Type: "field",
150 | OutboundTag: "proxy",
151 | Domain: []string{"geosite:cn"},
152 | })
153 | chinaListIP, _ := json.Marshal(v2ray.Rules{
154 | Type: "field",
155 | OutboundTag: "proxy",
156 | IP: []string{"geoip:cn"},
157 | })
158 | googleAPI, _ := json.Marshal(v2ray.Rules{
159 | Type: "field",
160 | OutboundTag: "proxy",
161 | Domain: []string{"domain:googleapis.cn", "domain:gstatic.com", "domain:ampproject.org", "domain:google.com.hk"},
162 | })
163 | rules := []json.RawMessage{}
164 | if routeMode == 1 {
165 | rules = []json.RawMessage{
166 | json.RawMessage(googleAPI),
167 | json.RawMessage(bypassLAN),
168 | json.RawMessage(blockDomain),
169 | }
170 | }
171 | if routeMode == 2 {
172 | rules = []json.RawMessage{
173 | json.RawMessage(googleAPI),
174 | json.RawMessage(blockDomain),
175 | json.RawMessage(bypassChinaSite),
176 | json.RawMessage(gfwList),
177 | json.RawMessage(bypassChinaIP),
178 | }
179 | }
180 | if routeMode == 3 {
181 | rules = []json.RawMessage{
182 | json.RawMessage(googleAPI),
183 | json.RawMessage(blockDomain),
184 | json.RawMessage(bypassLAN),
185 | json.RawMessage(directDomains),
186 | json.RawMessage(bypassChinaSite),
187 | json.RawMessage(gfwList), // sniff
188 | json.RawMessage(bypassChinaIP),
189 | }
190 | }
191 | if routeMode == 4 {
192 | rules = []json.RawMessage{
193 | json.RawMessage(googleAPI),
194 | json.RawMessage(blockDomain),
195 | json.RawMessage(gfwListIP),
196 | json.RawMessage(gfwList),
197 | }
198 | }
199 | if routeMode == 5 {
200 | rules = []json.RawMessage{
201 | // json.RawMessage(googleAPI),
202 | json.RawMessage(blockDomain),
203 | json.RawMessage(chinaListSite),
204 | json.RawMessage(chinaListIP),
205 | }
206 | }
207 | if routeMode >= 5 {
208 | rules = []json.RawMessage{
209 | json.RawMessage(googleAPI),
210 | json.RawMessage(bypassLAN),
211 | json.RawMessage(bypassChinaSite),
212 | json.RawMessage(bypassChinaIP),
213 | json.RawMessage(blockDomain),
214 | // json.RawMessage(blockAd),
215 | }
216 | }
217 | return &conf.RouterConfig{
218 | DomainStrategy: &domainStrategy,
219 | RuleList: rules,
220 | }
221 | }
222 |
223 | func configVmessTransport(profile *features.Vmess, outboundsSettingsMsg1 json.RawMessage) conf.OutboundDetourConfig {
224 | muxEnabled := false
225 | if profile.VmessOptions.Mux > 0 {
226 | muxEnabled = true
227 | } else {
228 | profile.VmessOptions.Mux = -1
229 | }
230 | tcp := conf.TransportProtocol("tcp")
231 | streamSetting := &conf.StreamConfig{
232 | Network: &tcp,
233 | Security: "",
234 | }
235 | if profile.Protocol == v2ray.VLESS {
236 | tcpHeader, _ := json.Marshal(v2ray.TcpHeader{Type: profile.Type})
237 | tcpHeaderMsg := json.RawMessage(tcpHeader)
238 | tcpSetting := &conf.TCPConfig{
239 | HeaderConfig: tcpHeaderMsg,
240 | }
241 | streamSetting.TCPSettings = tcpSetting
242 | }
243 | vmessOutboundDetourConfig := conf.OutboundDetourConfig{
244 | Protocol: "vmess",
245 | Tag: "proxy",
246 | MuxSettings: &conf.MuxConfig{Enabled: muxEnabled, Concurrency: int16(profile.VmessOptions.Mux)},
247 | Settings: &outboundsSettingsMsg1,
248 | StreamSetting: streamSetting,
249 | }
250 | if profile.Protocol == v2ray.VLESS {
251 | vmessOutboundDetourConfig.Protocol = "vless"
252 | }
253 |
254 | if profile.Net == "ws" {
255 | transportProtocol := conf.TransportProtocol(profile.Net)
256 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
257 | Network: &transportProtocol,
258 | WSSettings: &conf.WebSocketConfig{Path: profile.Path},
259 | }
260 | if profile.Host != "" {
261 | vmessOutboundDetourConfig.StreamSetting.WSSettings.Headers =
262 | map[string]string{"Host": profile.Host}
263 | }
264 | }
265 |
266 | if profile.Net == "h2" {
267 | transportProtocol := conf.TransportProtocol(profile.Net)
268 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
269 | Network: &transportProtocol,
270 | HTTPSettings: &conf.HTTPConfig{Path: profile.Path},
271 | }
272 | if profile.Host != "" {
273 | hosts := strings.Split(profile.Host, ",")
274 | vmessOutboundDetourConfig.StreamSetting.HTTPSettings.Host = conf.NewStringList(hosts)
275 | }
276 | }
277 |
278 | if profile.Net == "quic" {
279 | transportProtocol := conf.TransportProtocol(profile.Net)
280 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
281 | Network: &transportProtocol,
282 | QUICSettings: &conf.QUICConfig{Key: profile.Path},
283 | }
284 | if profile.Host != "" {
285 | vmessOutboundDetourConfig.StreamSetting.QUICSettings.Security = profile.Host
286 | }
287 | if profile.Type != "" {
288 | header, _ := json.Marshal(v2ray.QUICSettingsHeader{Type: profile.Type})
289 | vmessOutboundDetourConfig.StreamSetting.QUICSettings.Header = json.RawMessage(header)
290 | }
291 | }
292 |
293 | if profile.Net == "kcp" {
294 | transportProtocol := conf.TransportProtocol(profile.Net)
295 | mtu := uint32(1350)
296 | tti := uint32(50)
297 | upCap := uint32(12)
298 | downap := uint32(100)
299 | congestion := false
300 | readBufferSize := uint32(1)
301 | writeBufferSize := uint32(1)
302 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
303 | Network: &transportProtocol,
304 | KCPSettings: &conf.KCPConfig{
305 | Mtu: &mtu,
306 | Tti: &tti,
307 | UpCap: &upCap,
308 | DownCap: &downap,
309 | Congestion: &congestion,
310 | ReadBufferSize: &readBufferSize,
311 | WriteBufferSize: &writeBufferSize,
312 | },
313 | }
314 | if profile.Type != "" {
315 | header, _ := json.Marshal(v2ray.KCPSettingsHeader{Type: profile.Type})
316 | vmessOutboundDetourConfig.StreamSetting.KCPSettings.HeaderConfig = json.RawMessage(header)
317 | }
318 | }
319 |
320 | // tcp带http伪装
321 | if profile.Net == "tcp" && profile.Type == "http" {
322 | transportProtocol := conf.TransportProtocol(profile.Net)
323 | tcpSettingsHeader := v2ray.TCPSettingsHeader{
324 | Type: profile.Type,
325 | TCPSettingsRequest: v2ray.TCPSettingsRequest{
326 | Version: "1.1",
327 | Method: "GET",
328 | Path: []string{profile.Path}, // TODO: split by ","
329 | Headers: v2ray.HTTPHeaders{
330 | UserAgent: []string{"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"},
331 | AcceptEncoding: []string{"gzip, deflate"},
332 | Connection: "keep-alive",
333 | Pragma: "no-cache",
334 | Host: []string{profile.Host}, // TODO: split by ","
335 | },
336 | },
337 | }
338 | header, _ := json.Marshal(tcpSettingsHeader)
339 | vmessOutboundDetourConfig.StreamSetting = &conf.StreamConfig{
340 | Network: &transportProtocol,
341 | Security: profile.TLS,
342 | TCPSettings: &conf.TCPConfig{
343 | HeaderConfig: json.RawMessage(header),
344 | },
345 | }
346 | }
347 |
348 | if profile.TLS == "tls" {
349 | vmessOutboundDetourConfig.StreamSetting.Security = profile.TLS
350 | tlsConfig := &conf.TLSConfig{Insecure: profile.AllowInsecure}
351 | if profile.Host != "" {
352 | tlsConfig.ServerName = profile.Host
353 | }
354 | if profile.SNI != "" {
355 | tlsConfig.ServerName = profile.SNI
356 | }
357 | vmessOutboundDetourConfig.StreamSetting.TLSSettings = tlsConfig
358 | }
359 | return vmessOutboundDetourConfig
360 | }
361 |
362 | func createVlessOutboundDetourConfig(profile *features.Vmess) conf.OutboundDetourConfig {
363 | security := profile.Security
364 | if len(security) < 1 {
365 | security = "auto"
366 | }
367 | encryption := profile.Encryption
368 | if len(encryption) < 1 {
369 | encryption = "none"
370 | }
371 | outboundsSettings1, _ := json.Marshal(v2ray.VlessOutboundsSettings{
372 | Vnext: []v2ray.Vlessnext{
373 | {
374 | Address: profile.Add,
375 | Port: profile.Port,
376 | Users: []v2ray.VlessUser{
377 | {
378 | Encryption: encryption,
379 | Flow: profile.Flow,
380 | ID: profile.ID,
381 | Level: 8,
382 | Security: security,
383 | },
384 | },
385 | },
386 | },
387 | })
388 | outboundsSettingsMsg := json.RawMessage(outboundsSettings1)
389 | return configVmessTransport(profile, outboundsSettingsMsg)
390 | }
391 |
392 | func createVmessOutboundDetourConfig(profile *features.Vmess) conf.OutboundDetourConfig {
393 | outboundsSettings1, _ := json.Marshal(v2ray.OutboundsSettings{
394 | Vnext: []v2ray.Vnext{
395 | {
396 | Address: profile.Add,
397 | Port: profile.Port,
398 | Users: []v2ray.Users{
399 | {
400 | AlterID: profile.Aid,
401 | Email: "xxf098@github.com",
402 | ID: profile.ID,
403 | Level: 8,
404 | Security: profile.Security,
405 | },
406 | },
407 | },
408 | },
409 | })
410 | outboundsSettingsMsg1 := json.RawMessage(outboundsSettings1)
411 | return configVmessTransport(profile, outboundsSettingsMsg1)
412 | }
413 |
414 | func createTrojanOutboundDetourConfig(profile *features.Vmess) conf.OutboundDetourConfig {
415 | config := profile.Trojan
416 | // outboundsSettings, _ := json.Marshal(trojan.OutboundsSettings{
417 | // Address: config.Add,
418 | // Password: config.Password,
419 | // Port: config.Port,
420 | // ServerName: config.SNI,
421 | // SkipVerify: config.SkipCertVerify,
422 | // })
423 | outboundsSettings, _ := json.Marshal(settings.TrojanOutboundsSettings{
424 | Servers: []*settings.TrojanServerTarget{
425 | {
426 | Address: config.Add,
427 | Email: "xxf098@github.com",
428 | Level: 8,
429 | Password: config.Password,
430 | Port: uint16(config.Port),
431 | },
432 | },
433 | })
434 | outboundsSettingsMsg := json.RawMessage(outboundsSettings)
435 | transportProtocol := conf.TransportProtocol("tcp")
436 | trojanOutboundDetourConfig := conf.OutboundDetourConfig{
437 | Protocol: "trojan",
438 | Tag: "proxy",
439 | Settings: &outboundsSettingsMsg,
440 | StreamSetting: &conf.StreamConfig{
441 | Security: "tls",
442 | Network: &transportProtocol,
443 | TLSSettings: &conf.TLSConfig{
444 | Insecure: config.SkipCertVerify,
445 | ServerName: config.SNI,
446 | },
447 | },
448 | }
449 | return trojanOutboundDetourConfig
450 | }
451 |
452 | func createShadowsocksOutboundDetourConfig(profile *features.Vmess) conf.OutboundDetourConfig {
453 | config := profile.Shadowsocks
454 | outboundsSettings, _ := json.Marshal(settings.ShadowsocksOutboundsSettings{
455 | Servers: []*settings.ShadowsocksServerTarget{
456 | {
457 | Address: config.Add,
458 | Method: config.Method,
459 | Email: "xxf098@github.com",
460 | Level: 0,
461 | OTA: false,
462 | Password: config.Password,
463 | Port: uint16(config.Port),
464 | },
465 | },
466 | })
467 | outboundsSettingsMsg := json.RawMessage(outboundsSettings)
468 | shadowsocksOutboundDetourConfig := conf.OutboundDetourConfig{
469 | Protocol: "shadowsocks",
470 | Tag: "proxy",
471 | Settings: &outboundsSettingsMsg,
472 | }
473 | return shadowsocksOutboundDetourConfig
474 | }
475 |
476 | func getProxyOutboundDetourConfig(profile *features.Vmess) conf.OutboundDetourConfig {
477 | proxyOutboundConfig := conf.OutboundDetourConfig{}
478 | if profile.Protocol == v2ray.VMESS {
479 | proxyOutboundConfig = createVmessOutboundDetourConfig(profile)
480 | }
481 | if profile.Protocol == v2ray.TROJAN {
482 | proxyOutboundConfig = createTrojanOutboundDetourConfig(profile)
483 | }
484 | if profile.Protocol == v2ray.SHADOWSOCKS {
485 | proxyOutboundConfig = createShadowsocksOutboundDetourConfig(profile)
486 | }
487 | if profile.Protocol == v2ray.VLESS {
488 | proxyOutboundConfig = createVlessOutboundDetourConfig(profile)
489 | }
490 | return proxyOutboundConfig
491 | }
492 |
493 | func createFreedomOutboundDetourConfig(useIPv6 bool) conf.OutboundDetourConfig {
494 | domainStrategy := "useipv4"
495 | if useIPv6 {
496 | domainStrategy = "useip"
497 | }
498 | outboundsSettings2, _ := json.Marshal(v2ray.OutboundsSettings{DomainStrategy: domainStrategy})
499 | outboundsSettingsMsg2 := json.RawMessage(outboundsSettings2)
500 | return conf.OutboundDetourConfig{
501 | Protocol: "freedom",
502 | Tag: "direct",
503 | Settings: &outboundsSettingsMsg2,
504 | }
505 | }
506 |
507 | func creatPolicyConfig() *conf.PolicyConfig {
508 | handshake := uint32(4)
509 | connIdle := uint32(300)
510 | downlinkOnly := uint32(1)
511 | uplinkOnly := uint32(1)
512 | return &conf.PolicyConfig{
513 | Levels: map[uint32]*conf.Policy{
514 | 8: {
515 | ConnectionIdle: &connIdle,
516 | DownlinkOnly: &downlinkOnly,
517 | Handshake: &handshake,
518 | UplinkOnly: &uplinkOnly,
519 | },
520 | },
521 | System: &conf.SystemPolicy{
522 | StatsOutboundUplink: true,
523 | StatsOutboundDownlink: true,
524 | },
525 | }
526 | }
527 |
528 | func LoadXVmessConfig(profile *features.Vmess) (*conf.Config, error) {
529 | jsonConfig := &conf.Config{}
530 | jsonConfig.LogConfig = &conf.LogConfig{
531 | // AccessLog: "",
532 | // ErrorLog: "",
533 | LogLevel: profile.Loglevel,
534 | }
535 | jsonConfig.DNSConfig = createDNSConfig(profile.RouteMode, profile.DNS)
536 | jsonConfig.RouterConfig = createRouterConfig(profile.RouteMode)
537 | proxyOutboundConfig := getProxyOutboundDetourConfig(profile)
538 | freedomOutboundDetourConfig := createFreedomOutboundDetourConfig(profile.UseIPv6)
539 | if profile.RouteMode == 4 {
540 | jsonConfig.OutboundConfigs = []conf.OutboundDetourConfig{
541 | freedomOutboundDetourConfig,
542 | proxyOutboundConfig,
543 | }
544 | } else {
545 | jsonConfig.OutboundConfigs = []conf.OutboundDetourConfig{
546 | proxyOutboundConfig,
547 | freedomOutboundDetourConfig,
548 | }
549 | }
550 | // policy
551 | jsonConfig.Policy = creatPolicyConfig()
552 | // stats
553 | jsonConfig.Stats = &conf.StatsConfig{}
554 | return jsonConfig, nil
555 | }
556 |
--------------------------------------------------------------------------------
/xray/features.go:
--------------------------------------------------------------------------------
1 | package xray
2 |
3 | import (
4 | // The following are necessary as they register handlers in their init functions.
5 |
6 | // Required features. Can't remove unless there is replacements.
7 | _ "github.com/xtls/xray-core/app/dispatcher"
8 | _ "github.com/xtls/xray-core/app/proxyman/inbound"
9 | _ "github.com/xtls/xray-core/app/proxyman/outbound"
10 |
11 | // Default commander and all its services. This is an optional feature.
12 | // _ "github.com/xtls/xray-core/app/commander"
13 | // _ "github.com/xtls/xray-core/app/log/command"
14 | // _ "github.com/xtls/xray-core/app/proxyman/command"
15 | // _ "github.com/xtls/xray-core/app/stats/command"
16 |
17 | // Other optional features.
18 | _ "github.com/xtls/xray-core/app/dns"
19 | _ "github.com/xtls/xray-core/app/log"
20 | _ "github.com/xtls/xray-core/app/policy"
21 | _ "github.com/xtls/xray-core/app/reverse"
22 | _ "github.com/xtls/xray-core/app/router"
23 | _ "github.com/xtls/xray-core/app/stats"
24 |
25 | // Inbound and outbound proxies.
26 | _ "github.com/xtls/xray-core/proxy/blackhole"
27 | _ "github.com/xtls/xray-core/proxy/dns"
28 | _ "github.com/xtls/xray-core/proxy/dokodemo"
29 | _ "github.com/xtls/xray-core/proxy/freedom"
30 | _ "github.com/xtls/xray-core/proxy/http"
31 |
32 | // _ "github.com/xtls/xray-core/proxy/mtproto"
33 | _ "github.com/xtls/xray-core/proxy/shadowsocks"
34 | _ "github.com/xtls/xray-core/proxy/socks"
35 | _ "github.com/xtls/xray-core/proxy/trojan"
36 |
37 | // _ "github.com/xtls/xray-core/proxy/vless/inbound"
38 | _ "github.com/xtls/xray-core/proxy/vless/outbound"
39 | // _ "github.com/xtls/xray-core/proxy/vmess/inbound"
40 | _ "github.com/xtls/xray-core/proxy/vmess/outbound"
41 |
42 | // Transports
43 | _ "github.com/xtls/xray-core/transport/internet/domainsocket"
44 | _ "github.com/xtls/xray-core/transport/internet/grpc"
45 | _ "github.com/xtls/xray-core/transport/internet/http"
46 | _ "github.com/xtls/xray-core/transport/internet/kcp"
47 | _ "github.com/xtls/xray-core/transport/internet/quic"
48 | _ "github.com/xtls/xray-core/transport/internet/tcp"
49 | _ "github.com/xtls/xray-core/transport/internet/tls"
50 | _ "github.com/xtls/xray-core/transport/internet/udp"
51 | _ "github.com/xtls/xray-core/transport/internet/websocket"
52 |
53 | // Transport headers
54 | _ "github.com/xtls/xray-core/transport/internet/headers/http"
55 | _ "github.com/xtls/xray-core/transport/internet/headers/noop"
56 | _ "github.com/xtls/xray-core/transport/internet/headers/srtp"
57 | _ "github.com/xtls/xray-core/transport/internet/headers/tls"
58 | _ "github.com/xtls/xray-core/transport/internet/headers/utp"
59 | _ "github.com/xtls/xray-core/transport/internet/headers/wechat"
60 | _ "github.com/xtls/xray-core/transport/internet/headers/wireguard"
61 |
62 | // JSON & TOML & YAML
63 | _ "github.com/xtls/xray-core/main/json"
64 | // _ "github.com/xtls/xray-core/main/toml"
65 | // _ "github.com/xtls/xray-core/main/yaml"
66 | // Load config from file or http(s)
67 | // _ "github.com/xtls/xray-core/main/confloader/external"
68 | // Commands
69 | // _ "github.com/xtls/xray-core/main/commands/all"
70 | )
71 |
--------------------------------------------------------------------------------
/xray/tcp.go:
--------------------------------------------------------------------------------
1 | package xray
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io"
7 | "net"
8 |
9 | xnet "github.com/xtls/xray-core/common/net"
10 | xsession "github.com/xtls/xray-core/common/session"
11 | xcore "github.com/xtls/xray-core/core"
12 |
13 | "github.com/eycorsican/go-tun2socks/core"
14 | "github.com/xtls/xray-core/common/bytespool"
15 | "github.com/xxf098/go-tun2socks-build/pool"
16 | )
17 |
18 | type tcpHandler struct {
19 | ctx context.Context
20 | v *xcore.Instance
21 | }
22 |
23 | func (h *tcpHandler) relay(lhs net.Conn, rhs net.Conn) {
24 | go func() {
25 | buf := bytespool.Alloc(pool.BufSize)
26 | io.CopyBuffer(rhs, lhs, buf)
27 | bytespool.Free(buf)
28 | lhs.Close()
29 | rhs.Close()
30 | }()
31 | buf := bytespool.Alloc(pool.BufSize)
32 | io.CopyBuffer(lhs, rhs, buf)
33 | bytespool.Free(buf)
34 | lhs.Close()
35 | rhs.Close()
36 | }
37 |
38 | func NewTCPHandler(ctx context.Context, instance *xcore.Instance) core.TCPConnHandler {
39 | return &tcpHandler{
40 | ctx: ctx,
41 | v: instance,
42 | }
43 | }
44 |
45 | func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error {
46 | dest := xnet.DestinationFromAddr(target)
47 | sid := xsession.NewID()
48 | ctx := xsession.ContextWithID(h.ctx, sid)
49 | c, err := xcore.Dial(ctx, h.v, dest)
50 | if err != nil {
51 | return fmt.Errorf("dial V proxy connection failed: %v", err)
52 | }
53 | go h.relay(conn, c)
54 | return nil
55 | }
56 |
--------------------------------------------------------------------------------
/xray/udp.go:
--------------------------------------------------------------------------------
1 | package xray
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "net"
8 | "sync"
9 | "time"
10 |
11 | xsession "github.com/xtls/xray-core/common/session"
12 | xsignal "github.com/xtls/xray-core/common/signal"
13 | xtask "github.com/xtls/xray-core/common/task"
14 | xcore "github.com/xtls/xray-core/core"
15 |
16 | "github.com/eycorsican/go-tun2socks/common/log"
17 | "github.com/eycorsican/go-tun2socks/core"
18 | "github.com/xtls/xray-core/common/bytespool"
19 | "github.com/xxf098/go-tun2socks-build/pool"
20 | )
21 |
22 | type udpConnEntry struct {
23 | conn net.PacketConn
24 |
25 | // `ReadFrom` method of PacketConn given by V2Ray
26 | // won't return the correct remote address, we treat
27 | // all data receive from V2Ray are coming from the
28 | // same remote host, i.e. the `target` that passed
29 | // to `Connect`.
30 | target *net.UDPAddr
31 |
32 | updater xsignal.ActivityUpdater
33 | }
34 |
35 | type udpHandler struct {
36 | sync.Mutex
37 |
38 | ctx context.Context
39 | v *xcore.Instance
40 | conns map[core.UDPConn]*udpConnEntry
41 | timeout time.Duration // Maybe override by V2Ray local policies for some conns.
42 | }
43 |
44 | func (h *udpHandler) fetchInput(conn core.UDPConn) {
45 | h.Lock()
46 | c, ok := h.conns[conn]
47 | h.Unlock()
48 | if !ok {
49 | return
50 | }
51 |
52 | buf := bytespool.Alloc(pool.BufSize)
53 | defer bytespool.Free(buf)
54 |
55 | for {
56 | n, _, err := c.conn.ReadFrom(buf)
57 | if err != nil && n <= 0 {
58 | h.Close(conn)
59 | conn.Close()
60 | return
61 | }
62 | c.updater.Update()
63 | _, err = conn.WriteFrom(buf[:n], c.target)
64 | if err != nil {
65 | h.Close(conn)
66 | conn.Close()
67 | return
68 | }
69 | }
70 | }
71 |
72 | func NewUDPHandler(ctx context.Context, instance *xcore.Instance, timeout time.Duration) core.UDPConnHandler {
73 | return &udpHandler{
74 | ctx: ctx,
75 | v: instance,
76 | conns: make(map[core.UDPConn]*udpConnEntry, 16),
77 | timeout: timeout,
78 | }
79 | }
80 |
81 | func (h *udpHandler) Connect(conn core.UDPConn, target *net.UDPAddr) error {
82 | if target == nil {
83 | return errors.New("nil target is not allowed")
84 | }
85 | sid := xsession.NewID()
86 | ctx := xsession.ContextWithID(h.ctx, sid)
87 | ctx, cancel := context.WithCancel(ctx)
88 | pc, err := xcore.DialUDP(ctx, h.v)
89 | if err != nil {
90 | cancel()
91 | return fmt.Errorf("dial V proxy connection failed: %v", err)
92 | }
93 | timer := xsignal.CancelAfterInactivity(ctx, cancel, h.timeout)
94 | h.Lock()
95 | h.conns[conn] = &udpConnEntry{
96 | conn: pc,
97 | target: target,
98 | updater: timer,
99 | }
100 | h.Unlock()
101 | fetchTask := func() error {
102 | h.fetchInput(conn)
103 | return nil
104 | }
105 | go func() {
106 | if err := xtask.Run(ctx, fetchTask); err != nil {
107 | pc.Close()
108 | }
109 | }()
110 | log.Infof("new proxy connection for target: %s:%s", target.Network(), target.String())
111 | return nil
112 | }
113 |
114 | func (h *udpHandler) ReceiveTo(conn core.UDPConn, data []byte, addr *net.UDPAddr) error {
115 | h.Lock()
116 | c, ok := h.conns[conn]
117 | h.Unlock()
118 |
119 | if ok {
120 | _, err := c.conn.WriteTo(data, addr)
121 | c.updater.Update()
122 | if err != nil {
123 | h.Close(conn)
124 | return fmt.Errorf("write remote failed: %v", err)
125 | }
126 | return nil
127 | } else {
128 | h.Close(conn)
129 | return fmt.Errorf("proxy connection %v->%v does not exists", conn.LocalAddr(), addr)
130 | }
131 | }
132 |
133 | func (h *udpHandler) Close(conn core.UDPConn) {
134 | h.Lock()
135 | defer h.Unlock()
136 |
137 | if c, found := h.conns[conn]; found {
138 | c.conn.Close()
139 | }
140 | delete(h.conns, conn)
141 | }
142 |
--------------------------------------------------------------------------------
/xray/xray.go:
--------------------------------------------------------------------------------
1 | package xray
2 |
3 | import (
4 | "bytes"
5 | "net"
6 | "strconv"
7 | "strings"
8 |
9 | xnet "github.com/xtls/xray-core/common/net"
10 | "github.com/xtls/xray-core/core"
11 | "github.com/xtls/xray-core/infra/conf"
12 | "github.com/xtls/xray-core/infra/conf/serial"
13 | "github.com/xxf098/go-tun2socks-build/features"
14 | )
15 |
16 | // Start start with config
17 | func StartInstance(config []byte) (*core.Instance, error) {
18 | jsonConfig, err := serial.DecodeJSONConfig(bytes.NewReader(config))
19 | if err != nil {
20 | return nil, err
21 | }
22 | pbConfig, err := jsonConfig.Build()
23 | if err != nil {
24 | return nil, err
25 | }
26 | instance, err := core.New(pbConfig)
27 | if err != nil {
28 | return nil, err
29 | }
30 | err = instance.Start()
31 | if err != nil {
32 | return nil, err
33 | }
34 | return instance, nil
35 | }
36 |
37 | func CreateDNSConfig(option features.VmessOptions) *conf.DNSConfig {
38 | routeMode := option.RouteMode
39 | dnsConf := option.DNS
40 | dns := strings.Split(dnsConf, ",")
41 | nameServerConfig := []*conf.NameServerConfig{}
42 | if routeMode == 2 || routeMode == 3 || routeMode == 4 {
43 | // for i := len(dns) - 1; i >= 0; i-- {
44 | for i := 0; i < len(dns); i++ {
45 | if newConfig := toNameServerConfig(dns[i]); newConfig != nil {
46 | if i == 1 {
47 | newConfig.Domains = []string{"geosite:cn"}
48 | }
49 | nameServerConfig = append(nameServerConfig, newConfig)
50 | }
51 | }
52 | } else {
53 | if newConfig := toNameServerConfig(dns[0]); newConfig != nil {
54 | nameServerConfig = append(nameServerConfig, newConfig)
55 | }
56 | }
57 | return &conf.DNSConfig{
58 | Servers: nameServerConfig,
59 | }
60 | }
61 |
62 | func toNameServerConfig(hostport string) *conf.NameServerConfig {
63 | host, port, err := net.SplitHostPort(hostport)
64 | if err != nil {
65 | if strings.HasPrefix(hostport, "https://") {
66 | return &conf.NameServerConfig{Address: &conf.Address{Address: xnet.ParseAddress(host)}}
67 | }
68 | return nil
69 | }
70 | p, err := strconv.Atoi(port)
71 | if err != nil {
72 | return nil
73 | }
74 | newConfig := &conf.NameServerConfig{Address: &conf.Address{Address: xnet.ParseAddress(host)}, Port: uint16(p)}
75 | return newConfig
76 | }
77 |
--------------------------------------------------------------------------------