├── .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 | ![build](https://github.com/xxf098/go-tun2socks-build/workflows/build/badge.svg?branch=master&event=push) 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 | --------------------------------------------------------------------------------