From a54be6bcc9b45d122cb8412891027fabac695b2c Mon Sep 17 00:00:00 2001 From: TsungKang Date: Thu, 20 Aug 2015 08:57:29 +0800 Subject: [PATCH] roll to official version 2.5.6 --- CHANGES | 38 + LICENSE.txt | 363 ++++++- README.md | 81 +- shadowsocks-csharp.sln | 17 +- shadowsocks-csharp/3rd/ProxySocket/AuthMethod.cs | 113 --- shadowsocks-csharp/3rd/ProxySocket/AuthNone.cs | 58 -- shadowsocks-csharp/3rd/ProxySocket/AuthUserPass.cs | 156 --- .../3rd/ProxySocket/IAsyncProxyResult.cs | 96 -- .../3rd/ProxySocket/ProxyException.cs | 82 -- shadowsocks-csharp/3rd/ProxySocket/ProxySocket.cs | 385 ------- .../3rd/ProxySocket/Socks4Handler.cs | 234 ----- .../3rd/ProxySocket/Socks5Handler.cs | 418 -------- shadowsocks-csharp/3rd/ProxySocket/SocksHandler.cs | 204 ---- .../3rd/ProxySocket/SocksHttpWebRequest.cs | 283 ------ .../3rd/ProxySocket/SocksHttpWebResponse.cs | 109 -- .../3rd/ProxySocket/SocksWebClient.cs | 312 ------ shadowsocks-csharp/3rd/QQWry.cs | 754 -------------- shadowsocks-csharp/Controller/FileManager.cs | 3 - shadowsocks-csharp/Controller/I18N.cs | 2 +- shadowsocks-csharp/Controller/Logging.cs | 19 +- .../Controller/Service/AvailabilityStatistics.cs | 117 +++ .../Controller/{ => Service}/GfwListUpdater.cs | 198 ++-- .../Controller/{ => Service}/Listener.cs | 379 ++++--- .../Controller/{ => Service}/PACServer.cs | 417 ++++---- .../Controller/{ => Service}/PolipoRunner.cs | 303 +++--- .../Controller/{ => Service}/PortForwarder.cs | 531 +++++----- .../Controller/{Local.cs => Service/TCPRelay.cs} | 1049 ++++++++++++-------- shadowsocks-csharp/Controller/Service/UDPRelay.cs | 205 ++++ .../Controller/{ => Service}/UpdateChecker.cs | 296 +++--- .../Controller/ShadowsocksController.cs | 115 ++- .../Controller/Strategy/BalancingStrategy.cs | 71 ++ .../Strategy/HighAvailabilityStrategy.cs | 185 ++++ .../Controller/Strategy/IStrategy.cs | 56 ++ .../Strategy/SimplyChooseByStatisticsStrategy.cs | 176 ++++ .../Controller/Strategy/StrategyManager.cs | 24 + .../Controller/{ => System}/AutoStartup.cs | 110 +- .../Controller/{ => System}/SystemProxy.cs | 248 +++-- shadowsocks-csharp/Data/cn.txt | 43 +- shadowsocks-csharp/Data/mgwz.dll.gz | Bin 0 -> 48551 bytes shadowsocks-csharp/Data/polipo.exe.gz | Bin 65830 -> 0 bytes shadowsocks-csharp/Data/polipo_config.txt | 10 - shadowsocks-csharp/Data/privoxy.exe.gz | Bin 0 -> 157170 bytes shadowsocks-csharp/Data/privoxy_conf.txt | 5 + shadowsocks-csharp/Encryption/IVEncryptor.cs | 1 - shadowsocks-csharp/Encryption/PolarSSL.cs | 4 +- shadowsocks-csharp/Encryption/Sodium.cs | 3 +- shadowsocks-csharp/Encryption/SodiumEncryptor.cs | 86 +- shadowsocks-csharp/Encryption/TableEncryptor.cs | 2 - shadowsocks-csharp/Model/Configuration.cs | 31 +- shadowsocks-csharp/Model/Server.cs | 11 + shadowsocks-csharp/Program.cs | 8 +- .../Properties/Resources.Designer.cs | 35 +- shadowsocks-csharp/Properties/Resources.resx | 17 +- shadowsocks-csharp/Util/Util.cs | 51 +- shadowsocks-csharp/View/ConfigForm.Designer.cs | 54 +- shadowsocks-csharp/View/ConfigForm.cs | 131 ++- shadowsocks-csharp/View/ConfigForm.resx | 4 +- shadowsocks-csharp/View/LogForm.Designer.cs | 199 ++++ shadowsocks-csharp/View/LogForm.cs | 150 +++ .../View/{PingForm.resx => LogForm.resx} | 29 +- shadowsocks-csharp/View/MenuViewController.cs | 120 +-- shadowsocks-csharp/View/PingForm.Designer.cs | 237 ----- shadowsocks-csharp/View/PingForm.cs | 325 ------ shadowsocks-csharp/View/QRCodeForm.Designer.cs | 18 +- shadowsocks-csharp/View/QRCodeForm.cs | 23 +- shadowsocks-csharp/View/QRCodeForm.resx | 4 +- shadowsocks-csharp/View/QRCodeSplashForm.cs | 2 - shadowsocks-csharp/app.config | 21 +- shadowsocks-csharp/shadowsocks-csharp.csproj | 197 ++-- 69 files changed, 4260 insertions(+), 5768 deletions(-) delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/AuthMethod.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/AuthNone.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/AuthUserPass.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/IAsyncProxyResult.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/ProxyException.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/ProxySocket.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/Socks4Handler.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/Socks5Handler.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/SocksHandler.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebRequest.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebResponse.cs delete mode 100644 shadowsocks-csharp/3rd/ProxySocket/SocksWebClient.cs delete mode 100644 shadowsocks-csharp/3rd/QQWry.cs create mode 100644 shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs rename shadowsocks-csharp/Controller/{ => Service}/GfwListUpdater.cs (97%) rename shadowsocks-csharp/Controller/{ => Service}/Listener.cs (60%) mode change 100755 => 100644 rename shadowsocks-csharp/Controller/{ => Service}/PACServer.cs (96%) mode change 100755 => 100644 rename shadowsocks-csharp/Controller/{ => Service}/PolipoRunner.cs (54%) mode change 100755 => 100644 rename shadowsocks-csharp/Controller/{ => Service}/PortForwarder.cs (96%) mode change 100755 => 100644 rename shadowsocks-csharp/Controller/{Local.cs => Service/TCPRelay.cs} (54%) mode change 100755 => 100644 create mode 100644 shadowsocks-csharp/Controller/Service/UDPRelay.cs rename shadowsocks-csharp/Controller/{ => Service}/UpdateChecker.cs (67%) mode change 100755 => 100644 create mode 100644 shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs create mode 100644 shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs create mode 100644 shadowsocks-csharp/Controller/Strategy/IStrategy.cs create mode 100644 shadowsocks-csharp/Controller/Strategy/SimplyChooseByStatisticsStrategy.cs create mode 100644 shadowsocks-csharp/Controller/Strategy/StrategyManager.cs rename shadowsocks-csharp/Controller/{ => System}/AutoStartup.cs (96%) rename shadowsocks-csharp/Controller/{ => System}/SystemProxy.cs (74%) mode change 100755 => 100644 create mode 100644 shadowsocks-csharp/Data/mgwz.dll.gz delete mode 100755 shadowsocks-csharp/Data/polipo.exe.gz delete mode 100755 shadowsocks-csharp/Data/polipo_config.txt create mode 100644 shadowsocks-csharp/Data/privoxy.exe.gz create mode 100644 shadowsocks-csharp/Data/privoxy_conf.txt create mode 100644 shadowsocks-csharp/View/LogForm.Designer.cs create mode 100644 shadowsocks-csharp/View/LogForm.cs rename shadowsocks-csharp/View/{PingForm.resx => LogForm.resx} (78%) delete mode 100644 shadowsocks-csharp/View/PingForm.Designer.cs delete mode 100644 shadowsocks-csharp/View/PingForm.cs diff --git a/CHANGES b/CHANGES index 6e8b7840..5c5aba5f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,41 @@ +2.5.6 2015-08-19 +- Add portable mode. Create shadowsocks_portable_mode.txt to use it +- Support server reorder + +2.5.5 2015-08-17 +- Fix crash when enabling Availability Statistics and some servers can not be resolved +- Allow multiple instances +- Other fixes + +2.5.4 2015-08-16 +- Hide Privoxy icon + +2.5.3 2015-08-16 +- Replace Polipo with Privoxy +- Add Choose by Total Packet Loss + +2.5.2 2015-08-04 +- Add log viewer + +2.5.1 2015-07-26 +- Prevent HA from switching servers too frequently +- Fix server settings can not be updated when using HA +- Fix server port can't be 8123 +- Other minor fixes + +2.5 2015-07-25 +- Support load balance +- Support high availability + +2.4 2015-07-11 +- Support UDP relay +- Support online PAC +- Migrate update checker to GitHub releases +- Other fixes + +2.3.1 2015-03-06 +- Support user rule + 2.3 2015-01-25 - Use the same port for every profile - Use the same port for HTTP/Socks5/PAC diff --git a/LICENSE.txt b/LICENSE.txt index c09e49f7..187e3658 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -20,30 +20,348 @@ along with this program. If not, see . 3rd party projects ================== -polipo +Privoxy ------------------ -https://github.com/jech/polipo - -Copyright (c) 2003-2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. PolarSSL @@ -98,7 +416,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - libsodium --------- diff --git a/README.md b/README.md index f3accda4..83bff1ef 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,83 @@ Shadowsocks for Windows ======================= -#### Features compare with official version +[![Build Status]][Appveyor] -1. Add a speed test form -2. Get server location -3. Download speed test +[中文说明] -#### 和官方版本相比有什么不同 +#### Features -1. 新增了一个测速的功能 -2. 可以获取获取服务器IP归属地,如果不想联网获取,请下载[纯真IP库]并把qqwry.dat文件放置在当前目录 -3. 下载速度测试(通过下载谷歌浏览器的安装器来测试,大概900多K) +1. System proxy configuration +2. PAC mode and global mode +3. [GFWList] and user rules +4. Supports HTTP proxy +5. Supports server auto switching +6. Supports UDP relay (see Usage) #### Download -Download [latest release]. +Download the [latest release]. -#### Usage +#### Basic -See [official] +1. Find Shadowsocks icon in the notification tray +2. You can add multiple servers in servers menu +3. Select `Enable System Proxy` menu to enable system proxy. Please disable other +proxy addons in your browser, or set them to use system proxy +4. You can also configure your browser proxy manually if you don't want to enable +system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this +port in `Servers -> Edit Servers` -### Develop +#### PAC -Visual Studio 2015 is recommended. +1. You can change PAC rules by editing the PAC file. When you save the PAC file +with any editor, Shadowsocks will notify browsers about the change automatically +2. You can also update PAC file from [GFWList] (maintained by 3rd party) +3. You can also use online PAC URL + +#### Server Auto Switching + +1. Load balance: choosing server randomly +2. High availability: choosing the best server (low latency and packet loss) +3. Choose By Total Package Loss: ping and choose. Please also enable + `Availability Statistics` in the menu if you want to use this +4. Write your own strategy by implement IStrategy interface and send us a pull request! + +#### UDP + +For UDP, you need to use SocksCap or ProxyCap to force programs you want +to be proxied to tunnel over Shadowsocks + +#### Multiple Instances + +If you want to manage multiple servers using other tools like SwitchyOmega, +you can start multiple Shadowsocks instances. To avoid configuration conflicts, +copy Shadowsocks to a new directory and choose a different local port. + +Also, make sure to use `SOCKS5` proxy in SwitchyOmega, since we have only +one HTTP proxy instance. + +#### Server Configuration + +Please visit [Servers] for more information. + +#### Portable Mode + +If you want to put all temporary files into shadowsocks/temp folder instead of +system temp folder, create a `shadowsocks_portable_mode.txt` into shadowsocks folder. + +#### Develop + +Visual Studio 2015 is required. #### License GPLv3 -[latest release]: https://github.com/TkYu/shadowsocks-csharp-withping/releases -[official]: https://github.com/shadowsocks/shadowsocks-csharp -[纯真IP库]: http://update.cz88.net/soft/setup.zip \ No newline at end of file + +[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp +[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master +[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases +[GFWList]: https://github.com/gfwlist/gfwlist +[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side +[中文说明]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E diff --git a/shadowsocks-csharp.sln b/shadowsocks-csharp.sln index ac402ae1..83869ae2 100755 --- a/shadowsocks-csharp.sln +++ b/shadowsocks-csharp.sln @@ -1,13 +1,11 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.22823.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shadowsocks-csharp", "shadowsocks-csharp\shadowsocks-csharp.csproj", "{F58374A5-9AFB-430A-AF20-C509D3DCED3F}" +# Visual Studio Express 2012 for Windows Desktop +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shadowsocks-csharp", "shadowsocks-csharp\shadowsocks-csharp.csproj", "{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{45913187-0685-4903-B250-DCEF0479CD86}" ProjectSection(ProjectDependencies) = postProject - {F58374A5-9AFB-430A-AF20-C509D3DCED3F} = {F58374A5-9AFB-430A-AF20-C509D3DCED3F} + {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} = {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} EndProjectSection EndProject Global @@ -16,10 +14,11 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F58374A5-9AFB-430A-AF20-C509D3DCED3F}.Debug|x86.ActiveCfg = Debug|Any CPU - {F58374A5-9AFB-430A-AF20-C509D3DCED3F}.Debug|x86.Build.0 = Debug|Any CPU - {F58374A5-9AFB-430A-AF20-C509D3DCED3F}.Release|x86.ActiveCfg = Release|Any CPU - {F58374A5-9AFB-430A-AF20-C509D3DCED3F}.Release|x86.Build.0 = Release|Any CPU + {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.ActiveCfg = Debug|x86 + {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.Build.0 = Debug|x86 + {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.Deploy.0 = Debug|x86 + {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.ActiveCfg = Release|x86 + {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.Build.0 = Release|x86 {45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.ActiveCfg = Debug|x86 {45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.Build.0 = Debug|x86 {45913187-0685-4903-B250-DCEF0479CD86}.Release|x86.ActiveCfg = Release|x86 diff --git a/shadowsocks-csharp/3rd/ProxySocket/AuthMethod.cs b/shadowsocks-csharp/3rd/ProxySocket/AuthMethod.cs deleted file mode 100644 index ade427c9..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/AuthMethod.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* - Copyright ?2002, The KPD-Team - All rights reserved. - http://www.mentalis.org/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Neither the name of the KPD-Team, nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -using System; -using System.Net; -using System.Net.Sockets; - -namespace Shadowsocks._3rd.ProxySocket { - /// - /// Implements a SOCKS authentication scheme. - /// - /// This is an abstract class; it must be inherited. - internal abstract class AuthMethod { - /// - /// Initializes an AuthMethod instance. - /// - /// The socket connection with the proxy server. - public AuthMethod(Socket server) { - Server = server; - } - /// - /// Authenticates the user. - /// - /// Authentication with the proxy server failed. - /// The proxy server uses an invalid protocol. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - public abstract void Authenticate(); - /// - /// Authenticates the user asynchronously. - /// - /// The method to call when the authentication is complete. - /// Authentication with the proxy server failed. - /// The proxy server uses an invalid protocol. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - public abstract void BeginAuthenticate(HandShakeComplete callback); - /// - /// Gets or sets the socket connection with the proxy server. - /// - /// The socket connection with the proxy server. - protected Socket Server { - get { - return m_Server; - } - set { - if (value == null) - throw new ArgumentNullException(); - m_Server = value; - } - } - /// - /// Gets or sets a byt array that can be used to store data. - /// - /// A byte array to store data. - protected byte[] Buffer { - get { - return m_Buffer; - } - set { - m_Buffer = value; - } - } - /// - /// Gets or sets the number of bytes that have been received from the remote proxy server. - /// - /// An integer that holds the number of bytes that have been received from the remote proxy server. - protected int Received { - get { - return m_Received; - } - set { - m_Received = value; - } - } - // private variables - /// Holds the value of the Buffer property. - private byte[] m_Buffer; - /// Holds the value of the Server property. - private Socket m_Server; - /// Holds the address of the method to call when the proxy has authenticated the client. - protected HandShakeComplete CallBack; - /// Holds the value of the Received property. - private int m_Received; - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/3rd/ProxySocket/AuthNone.cs b/shadowsocks-csharp/3rd/ProxySocket/AuthNone.cs deleted file mode 100644 index 8c1b0314..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/AuthNone.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - Copyright ?2002, The KPD-Team - All rights reserved. - http://www.mentalis.org/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Neither the name of the KPD-Team, nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -using System.Net.Sockets; - -namespace Shadowsocks._3rd.ProxySocket { - /// - /// This class implements the 'No Authentication' scheme. - /// - internal sealed class AuthNone : AuthMethod { - /// - /// Initializes an AuthNone instance. - /// - /// The socket connection with the proxy server. - public AuthNone(Socket server) : base(server) {} - /// - /// Authenticates the user. - /// - public override void Authenticate() { - return; // Do Nothing - } - /// - /// Authenticates the user asynchronously. - /// - /// The method to call when the authentication is complete. - /// This method immediately calls the callback method. - public override void BeginAuthenticate(HandShakeComplete callback) { - callback(null); - } - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/3rd/ProxySocket/AuthUserPass.cs b/shadowsocks-csharp/3rd/ProxySocket/AuthUserPass.cs deleted file mode 100644 index 58fc14ae..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/AuthUserPass.cs +++ /dev/null @@ -1,156 +0,0 @@ -/* - Copyright ?2002, The KPD-Team - All rights reserved. - http://www.mentalis.org/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Neither the name of the KPD-Team, nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -using System; -using System.Net.Sockets; -using System.Text; - -namespace Shadowsocks._3rd.ProxySocket { - /// - /// This class implements the 'username/password authentication' scheme. - /// - internal sealed class AuthUserPass : AuthMethod { - /// - /// Initializes a new AuthUserPass instance. - /// - /// The socket connection with the proxy server. - /// The username to use. - /// The password to use. - /// user -or- pass is null. - public AuthUserPass(Socket server, string user, string pass) : base(server) { - Username = user; - Password = pass; - } - /// - /// Creates an array of bytes that has to be sent if the user wants to authenticate with the username/password authentication scheme. - /// - /// An array of bytes that has to be sent if the user wants to authenticate with the username/password authentication scheme. - private byte[] GetAuthenticationBytes() { - byte[] buffer = new byte[3 + Username.Length + Password.Length]; - buffer[0] = 1; - buffer[1] = (byte)Username.Length; - Array.Copy(Encoding.ASCII.GetBytes(Username), 0, buffer, 2, Username.Length); - buffer[Username.Length + 2] = (byte)Password.Length; - Array.Copy(Encoding.ASCII.GetBytes(Password), 0, buffer, Username.Length + 3, Password.Length); - return buffer; - } - /// - /// Starts the authentication process. - /// - public override void Authenticate() { - Server.Send(GetAuthenticationBytes()); - byte[] buffer = new byte[2]; - int received = 0; - while (received != 2) { - received += Server.Receive(buffer, received, 2 - received, SocketFlags.None); - } - if (buffer[1] != 0) { - Server.Close(); - throw new ProxyException("Username/password combination rejected."); - } - return; - } - /// - /// Starts the asynchronous authentication process. - /// - /// The method to call when the authentication is complete. - public override void BeginAuthenticate(HandShakeComplete callback) { - CallBack = callback; - Server.BeginSend(GetAuthenticationBytes(), 0, 3 + Username.Length + Password.Length, SocketFlags.None, new AsyncCallback(this.OnSent), Server); - return; - } - /// - /// Called when the authentication bytes have been sent. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnSent(IAsyncResult ar) { - try { - Server.EndSend(ar); - Buffer = new byte[2]; - Server.BeginReceive(Buffer, 0, 2, SocketFlags.None, new AsyncCallback(this.OnReceive), Server); - } catch (Exception e) { - CallBack(e); - } - } - /// - /// Called when the socket received an authentication reply. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnReceive(IAsyncResult ar) { - try { - Received += Server.EndReceive(ar); - if (Received == Buffer.Length) - if (Buffer[1] == 0) - CallBack(null); - else - throw new ProxyException("Username/password combination not accepted."); - else - Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, new AsyncCallback(this.OnReceive), Server); - } catch (Exception e) { - CallBack(e); - } - } - /// - /// Gets or sets the username to use when authenticating with the proxy server. - /// - /// The username to use when authenticating with the proxy server. - /// The specified value is null. - private string Username { - get { - return m_Username; - } - set { - if (value == null) - throw new ArgumentNullException(); - m_Username = value; - } - } - /// - /// Gets or sets the password to use when authenticating with the proxy server. - /// - /// The password to use when authenticating with the proxy server. - /// The specified value is null. - private string Password { - get { - return m_Password; - } - set { - if (value == null) - throw new ArgumentNullException(); - m_Password = value; - } - } - // private variables - /// Holds the value of the Username property. - private string m_Username; - /// Holds the value of the Password property. - private string m_Password; - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/3rd/ProxySocket/IAsyncProxyResult.cs b/shadowsocks-csharp/3rd/ProxySocket/IAsyncProxyResult.cs deleted file mode 100644 index 786fbb8b..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/IAsyncProxyResult.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - Copyright ?2002, The KPD-Team - All rights reserved. - http://www.mentalis.org/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Neither the name of the KPD-Team, nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -using System; -using System.Threading; - -namespace Shadowsocks._3rd.ProxySocket { - /// - /// A class that implements the IAsyncResult interface. Objects from this class are returned by the BeginConnect method of the ProxySocket class. - /// - internal class IAsyncProxyResult : IAsyncResult { - /// Initializes the internal variables of this object - /// An object that contains state information for this request. - internal void Init(object stateObject) { - m_StateObject = stateObject; - m_Completed = false; - if (m_WaitHandle != null) - m_WaitHandle.Reset(); - - } - /// Initializes the internal variables of this object - internal void Reset() { - m_StateObject = null; - m_Completed = true; - if (m_WaitHandle != null) - m_WaitHandle.Set(); - } - /// Gets a value that indicates whether the server has completed processing the call. It is illegal for the server to use any client supplied resources outside of the agreed upon sharing semantics after it sets the IsCompleted property to "true". Thus, it is safe for the client to destroy the resources after IsCompleted property returns "true". - /// A boolean that indicates whether the server has completed processing the call. - public bool IsCompleted { - get { - return m_Completed; - } - } - /// Gets a value that indicates whether the BeginXXXX call has been completed synchronously. If this is detected in the AsyncCallback delegate, it is probable that the thread that called BeginInvoke is the current thread. - /// Returns false. - public bool CompletedSynchronously { - get { - return false; - } - } - /// Gets an object that was passed as the state parameter of the BeginXXXX method call. - /// The object that was passed as the state parameter of the BeginXXXX method call. - public object AsyncState { - get { - return m_StateObject; - } - } - /// - /// The AsyncWaitHandle property returns the WaitHandle that can use to perform a WaitHandle.WaitOne or WaitAny or WaitAll. The object which implements IAsyncResult need not derive from the System.WaitHandle classes directly. The WaitHandle wraps its underlying synchronization primitive and should be signaled after the call is completed. This enables the client to wait for the call to complete instead polling. The Runtime supplies a number of waitable objects that mirror Win32 synchronization primitives e.g. ManualResetEvent, AutoResetEvent and Mutex. - /// WaitHandle supplies methods that support waiting for such synchronization objects to become signaled with "any" or "all" semantics i.e. WaitHandle.WaitOne, WaitAny and WaitAll. Such methods are context aware to avoid deadlocks. The AsyncWaitHandle can be allocated eagerly or on demand. It is the choice of the IAsyncResult implementer. - /// - /// The WaitHandle associated with this asynchronous result. - public WaitHandle AsyncWaitHandle { - get { - if (m_WaitHandle == null) - m_WaitHandle = new ManualResetEvent(false); - return m_WaitHandle; - } - } - // private variables - /// Used internally to represent the state of the asynchronous request - internal bool m_Completed = true; - /// Holds the value of the StateObject property. - private object m_StateObject; - /// Holds the value of the WaitHandle property. - private ManualResetEvent m_WaitHandle; - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/3rd/ProxySocket/ProxyException.cs b/shadowsocks-csharp/3rd/ProxySocket/ProxyException.cs deleted file mode 100644 index de791c63..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/ProxyException.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - Copyright ?2002, The KPD-Team - All rights reserved. - http://www.mentalis.org/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Neither the name of the KPD-Team, nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -using System; - -namespace Shadowsocks._3rd.ProxySocket { - /// - /// The exception that is thrown when a proxy error occurs. - /// - public class ProxyException : Exception { - /// - /// Initializes a new instance of the ProxyException class. - /// - public ProxyException() : this("An error occured while talking to the proxy server.") {} - /// - /// Initializes a new instance of the ProxyException class. - /// - /// The message that describes the error. - public ProxyException(string message) : base(message) {} - /// - /// Initializes a new instance of the ProxyException class. - /// - /// The error number returned by a SOCKS5 server. - public ProxyException(int socks5Error) : this(ProxyException.Socks5ToString(socks5Error)) {} - /// - /// Converts a SOCKS5 error number to a human readable string. - /// - /// The error number returned by a SOCKS5 server. - /// A string representation of the specified SOCKS5 error number. - public static string Socks5ToString(int socks5Error) { - switch(socks5Error) { - case 0: - return "Connection succeeded."; - case 1: - return "General SOCKS server failure."; - case 2: - return "Connection not allowed by ruleset."; - case 3: - return "Network unreachable."; - case 4: - return "Host unreachable."; - case 5: - return "Connection refused."; - case 6: - return "TTL expired."; - case 7: - return "Command not supported."; - case 8: - return "Address type not supported."; - default: - return "Unspecified SOCKS error."; - } - } - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/3rd/ProxySocket/ProxySocket.cs b/shadowsocks-csharp/3rd/ProxySocket/ProxySocket.cs deleted file mode 100644 index aa74d8ae..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/ProxySocket.cs +++ /dev/null @@ -1,385 +0,0 @@ -/* - Copyright ?2002, The KPD-Team - All rights reserved. - http://www.mentalis.org/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Neither the name of the KPD-Team, nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -using System; -using System.Net; -using System.Net.Sockets; - -// Implements a number of classes to allow Sockets to connect trough a firewall. -namespace Shadowsocks._3rd.ProxySocket { - /// - /// Specifies the type of proxy servers that an instance of the ProxySocket class can use. - /// - public enum ProxyTypes { - /// No proxy server; the ProxySocket object behaves exactly like an ordinary Socket object. - None, - /// A SOCKS4[A] proxy server. - Socks4, - /// A SOCKS5 proxy server. - Socks5 - } - /// - /// Implements a Socket class that can connect trough a SOCKS proxy server. - /// - /// This class implements SOCKS4[A] and SOCKS5.
It does not, however, implement the BIND commands, so you cannot .
- public class ProxySocket : Socket { - /// - /// Initializes a new instance of the ProxySocket class. - /// - /// One of the AddressFamily values. - /// One of the SocketType values. - /// One of the ProtocolType values. - /// The combination of addressFamily, socketType, and protocolType results in an invalid socket. - public ProxySocket (AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) : this(addressFamily, socketType, protocolType, "") {} - /// - /// Initializes a new instance of the ProxySocket class. - /// - /// One of the AddressFamily values. - /// One of the SocketType values. - /// One of the ProtocolType values. - /// The username to use when authenticating with the proxy server. - /// The combination of addressFamily, socketType, and protocolType results in an invalid socket. - /// proxyUsername is null. - public ProxySocket (AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, string proxyUsername) : this(addressFamily, socketType, protocolType, proxyUsername, "") {} - /// - /// Initializes a new instance of the ProxySocket class. - /// - /// One of the AddressFamily values. - /// One of the SocketType values. - /// One of the ProtocolType values. - /// The username to use when authenticating with the proxy server. - /// The password to use when authenticating with the proxy server. - /// The combination of addressFamily, socketType, and protocolType results in an invalid socket. - /// proxyUsername -or- proxyPassword is null. - public ProxySocket (AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, string proxyUsername, string proxyPassword) : base(addressFamily, socketType, protocolType) { - ProxyUser = proxyUsername; - ProxyPass = proxyPassword; - ToThrow = new InvalidOperationException(); - } - /// - /// Establishes a connection to a remote device. - /// - /// An EndPoint that represents the remote device. - /// The remoteEP parameter is a null reference (Nothing in Visual Basic). - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - /// An error occured while talking to the proxy server. - public new void Connect(EndPoint remoteEP) { - if (remoteEP == null) - throw new ArgumentNullException(" cannot be null."); - if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) - base.Connect(remoteEP); - else { - base.Connect(ProxyEndPoint); - if (ProxyType == ProxyTypes.Socks4) - (new Socks4Handler(this, ProxyUser)).Negotiate((IPEndPoint)remoteEP); - else if (ProxyType == ProxyTypes.Socks5) - (new Socks5Handler(this, ProxyUser, ProxyPass)).Negotiate((IPEndPoint)remoteEP); - } - } - /// - /// Establishes a connection to a remote device. - /// - /// The remote host to connect to. - /// The remote port to connect to. - /// The host parameter is a null reference (Nothing in Visual Basic). - /// The port parameter is invalid. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - /// An error occured while talking to the proxy server. - /// If you use this method with a SOCKS4 server, it will let the server resolve the hostname. Not all SOCKS4 servers support this 'remote DNS' though. - public void Connect(string host, int port) { - if (host == null) - throw new ArgumentNullException(" cannot be null."); - if (port <= 0 || port > 65535) - throw new ArgumentException("Invalid port."); - if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) - base.Connect(new IPEndPoint(Dns.Resolve(host).AddressList[0], port)); - else { - base.Connect(ProxyEndPoint); - if (ProxyType == ProxyTypes.Socks4) - (new Socks4Handler(this, ProxyUser)).Negotiate(host, port); - else if (ProxyType == ProxyTypes.Socks5) - (new Socks5Handler(this, ProxyUser, ProxyPass)).Negotiate(host, port); - } - } - /// - /// Begins an asynchronous request for a connection to a network device. - /// - /// An EndPoint that represents the remote device. - /// The AsyncCallback delegate. - /// An object that contains state information for this request. - /// An IAsyncResult that references the asynchronous connection. - /// The remoteEP parameter is a null reference (Nothing in Visual Basic). - /// An operating system error occurs while creating the Socket. - /// The Socket has been closed. - public new IAsyncResult BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state) { - if (remoteEP == null || callback == null) - throw new ArgumentNullException(); - if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) { - return base.BeginConnect(remoteEP, callback, state); - } else { - CallBack = callback; - if (ProxyType == ProxyTypes.Socks4) { - AsyncResult = (new Socks4Handler(this, ProxyUser)).BeginNegotiate((IPEndPoint)remoteEP, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); - return AsyncResult; - } else if(ProxyType == ProxyTypes.Socks5) { - AsyncResult = (new Socks5Handler(this, ProxyUser, ProxyPass)).BeginNegotiate((IPEndPoint)remoteEP, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); - return AsyncResult; - } - return null; - } - } - /// - /// Begins an asynchronous request for a connection to a network device. - /// - /// The host to connect to. - /// The port on the remote host to connect to. - /// The AsyncCallback delegate. - /// An object that contains state information for this request. - /// An IAsyncResult that references the asynchronous connection. - /// The host parameter is a null reference (Nothing in Visual Basic). - /// The port parameter is invalid. - /// An operating system error occurs while creating the Socket. - /// The Socket has been closed. - public IAsyncResult BeginConnect(string host, int port, AsyncCallback callback, object state) { - if (host == null || callback == null) - throw new ArgumentNullException(); - if (port <= 0 || port > 65535) - throw new ArgumentException(); - CallBack = callback; - if (this.ProtocolType != ProtocolType.Tcp || ProxyType == ProxyTypes.None || ProxyEndPoint == null) { - RemotePort = port; - AsyncResult = BeginDns(host, new HandShakeComplete(this.OnHandShakeComplete)); - return AsyncResult; - } else { - if (ProxyType == ProxyTypes.Socks4) { - AsyncResult = (new Socks4Handler(this, ProxyUser)).BeginNegotiate(host, port, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); - return AsyncResult; - } else if(ProxyType == ProxyTypes.Socks5) { - AsyncResult = (new Socks5Handler(this, ProxyUser, ProxyPass)).BeginNegotiate(host, port, new HandShakeComplete(this.OnHandShakeComplete), ProxyEndPoint); - return AsyncResult; - } - return null; - } - } - /// - /// Ends a pending asynchronous connection request. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - /// The asyncResult parameter is a null reference (Nothing in Visual Basic). - /// The asyncResult parameter was not returned by a call to the BeginConnect method. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - /// EndConnect was previously called for the asynchronous connection. - /// The proxy server refused the connection. - public new void EndConnect(IAsyncResult asyncResult) { - if (asyncResult == null) - throw new ArgumentNullException(); - if (!asyncResult.IsCompleted) - throw new ArgumentException(); - if (ToThrow != null) - throw ToThrow; - return; - } - /// - /// Begins an asynchronous request to resolve a DNS host name or IP address in dotted-quad notation to an IPAddress instance. - /// - /// The host to resolve. - /// The method to call when the hostname has been resolved. - /// An IAsyncResult instance that references the asynchronous request. - /// There was an error while trying to resolve the host. - internal IAsyncProxyResult BeginDns(string host, HandShakeComplete callback) { - try { - Dns.BeginResolve(host, new AsyncCallback(this.OnResolved), this); - return new IAsyncProxyResult(); - } catch { - throw new SocketException(); - } - } - /// - /// Called when the specified hostname has been resolved. - /// - /// The result of the asynchronous operation. - private void OnResolved(IAsyncResult asyncResult) { - try { - IPHostEntry dns = Dns.EndResolve(asyncResult); - base.BeginConnect(new IPEndPoint(dns.AddressList[0], RemotePort), new AsyncCallback(this.OnConnect), State); - } catch (Exception e) { - OnHandShakeComplete(e); - } - } - /// - /// Called when the Socket is connected to the remote host. - /// - /// The result of the asynchronous operation. - private void OnConnect(IAsyncResult asyncResult) { - try { - base.EndConnect(asyncResult); - OnHandShakeComplete(null); - } catch (Exception e) { - OnHandShakeComplete(e); - } - } - /// - /// Called when the Socket has finished talking to the proxy server and is ready to relay data. - /// - /// The error to throw when the EndConnect method is called. - private void OnHandShakeComplete(Exception error) { - if (error != null) - this.Close(); - ToThrow = error; - AsyncResult.Reset(); - if (CallBack != null) - CallBack(AsyncResult); - } - /// - /// Gets or sets the EndPoint of the proxy server. - /// - /// An IPEndPoint object that holds the IP address and the port of the proxy server. - public IPEndPoint ProxyEndPoint { - get { - return m_ProxyEndPoint; - } - set { - m_ProxyEndPoint = value; - } - } - /// - /// Gets or sets the type of proxy server to use. - /// - /// One of the ProxyTypes values. - public ProxyTypes ProxyType { - get { - return m_ProxyType; - } - set { - m_ProxyType = value; - } - } - /// - /// Gets or sets a user-defined object. - /// - /// The user-defined object. - private object State { - get { - return m_State; - } - set { - m_State = value; - } - } - /// - /// Gets or sets the username to use when authenticating with the proxy. - /// - /// A string that holds the username that's used when authenticating with the proxy. - /// The specified value is null. - public string ProxyUser { - get { - return m_ProxyUser; - } - set { - if (value == null) - throw new ArgumentNullException(); - m_ProxyUser = value; - } - } - /// - /// Gets or sets the password to use when authenticating with the proxy. - /// - /// A string that holds the password that's used when authenticating with the proxy. - /// The specified value is null. - public string ProxyPass { - get { - return m_ProxyPass; - } - set { - if (value == null) - throw new ArgumentNullException(); - m_ProxyPass = value; - } - } - /// - /// Gets or sets the asynchronous result object. - /// - /// An instance of the IAsyncProxyResult class. - private IAsyncProxyResult AsyncResult { - get { - return m_AsyncResult; - } - set { - m_AsyncResult = value; - } - } - /// - /// Gets or sets the exception to throw when the EndConnect method is called. - /// - /// An instance of the Exception class (or subclasses of Exception). - private Exception ToThrow { - get { - return m_ToThrow; - } - set { - m_ToThrow = value; - } - } - /// - /// Gets or sets the remote port the user wants to connect to. - /// - /// An integer that specifies the port the user wants to connect to. - private int RemotePort { - get { - return m_RemotePort; - } - set { - m_RemotePort = value; - } - } - // private variables - /// Holds the value of the State property. - private object m_State; - /// Holds the value of the ProxyEndPoint property. - private IPEndPoint m_ProxyEndPoint = null; - /// Holds the value of the ProxyType property. - private ProxyTypes m_ProxyType = ProxyTypes.None; - /// Holds the value of the ProxyUser property. - private string m_ProxyUser = null; - /// Holds the value of the ProxyPass property. - private string m_ProxyPass = null; - /// Holds a pointer to the method that should be called when the Socket is connected to the remote device. - private AsyncCallback CallBack = null; - /// Holds the value of the AsyncResult property. - private IAsyncProxyResult m_AsyncResult; - /// Holds the value of the ToThrow property. - private Exception m_ToThrow = null; - /// Holds the value of the RemotePort property. - private int m_RemotePort; - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/3rd/ProxySocket/Socks4Handler.cs b/shadowsocks-csharp/3rd/ProxySocket/Socks4Handler.cs deleted file mode 100644 index 9deb279b..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/Socks4Handler.cs +++ /dev/null @@ -1,234 +0,0 @@ -/* - Copyright ?2002, The KPD-Team - All rights reserved. - http://www.mentalis.org/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Neither the name of the KPD-Team, nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; - -namespace Shadowsocks._3rd.ProxySocket { - /// - /// Implements the SOCKS4[A] protocol. - /// - internal sealed class Socks4Handler : SocksHandler { - /// - /// Initilizes a new instance of the SocksHandler class. - /// - /// The socket connection with the proxy server. - /// The username to use when authenticating with the server. - /// server -or- user is null. - public Socks4Handler(Socket server, string user) : base(server, user) {} - /// - /// Creates an array of bytes that has to be sent when the user wants to connect to a specific host/port combination. - /// - /// The host to connect to. - /// The port to connect to. - /// An array of bytes that has to be sent when the user wants to connect to a specific host/port combination. - /// Resolving the host name will be done at server side. Do note that some SOCKS4 servers do not implement this functionality. - /// host is null. - /// port is invalid. - private byte[] GetHostPortBytes(string host, int port) { - if (host == null) - throw new ArgumentNullException(); - if (port <= 0 || port > 65535) - throw new ArgumentException(); - byte [] connect = new byte[10 + Username.Length + host.Length]; - connect[0] = 4; - connect[1] = 1; - Array.Copy(PortToBytes(port), 0, connect, 2, 2); - connect[4] = connect[5] = connect[6] = 0; - connect[7] = 1; - Array.Copy(Encoding.ASCII.GetBytes(Username), 0, connect, 8, Username.Length); - connect[8 + Username.Length] = 0; - Array.Copy(Encoding.ASCII.GetBytes(host), 0, connect, 9 + Username.Length, host.Length); - connect[9 + Username.Length + host.Length] = 0; - return connect; - } - /// - /// Creates an array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint. - /// - /// The IPEndPoint to connect to. - /// An array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint. - /// remoteEP is null. - private byte[] GetEndPointBytes(IPEndPoint remoteEP) { - if (remoteEP == null) - throw new ArgumentNullException(); - byte [] connect = new byte[9 + Username.Length]; - connect[0] = 4; - connect[1] = 1; - Array.Copy(PortToBytes(remoteEP.Port), 0, connect, 2, 2); - Array.Copy(AddressToBytes(remoteEP.Address.Address), 0, connect, 4, 4); - Array.Copy(Encoding.ASCII.GetBytes(Username), 0, connect, 8, Username.Length); - connect[8 + Username.Length] = 0; - return connect; - } - /// - /// Starts negotiating with the SOCKS server. - /// - /// The host to connect to. - /// The port to connect to. - /// host is null. - /// port is invalid. - /// The proxy rejected the request. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - public override void Negotiate(string host, int port) { - Negotiate(GetHostPortBytes(host, port)); - } - /// - /// Starts negotiating with the SOCKS server. - /// - /// The IPEndPoint to connect to. - /// remoteEP is null. - /// The proxy rejected the request. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - public override void Negotiate(IPEndPoint remoteEP) { - Negotiate(GetEndPointBytes(remoteEP)); - } - /// - /// Starts negotiating with the SOCKS server. - /// - /// The bytes to send when trying to authenticate. - /// connect is null. - /// connect is too small. - /// The proxy rejected the request. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - private void Negotiate(byte [] connect) { - if (connect == null) - throw new ArgumentNullException(); - if (connect.Length < 2) - throw new ArgumentException(); - Server.Send(connect); - byte [] buffer = ReadBytes(8); - if (buffer[1] != 90) { - Server.Close(); - throw new ProxyException("Negotiation failed."); - } - } - /// - /// Starts negotiating asynchronously with a SOCKS proxy server. - /// - /// The remote server to connect to. - /// The remote port to connect to. - /// The method to call when the connection has been established. - /// The IPEndPoint of the SOCKS proxy server. - /// An IAsyncProxyResult that references the asynchronous connection. - public override IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback, IPEndPoint proxyEndPoint) { - ProtocolComplete = callback; - Buffer = GetHostPortBytes(host, port); - Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server); - AsyncResult = new IAsyncProxyResult(); - return AsyncResult; - } - /// - /// Starts negotiating asynchronously with a SOCKS proxy server. - /// - /// An IPEndPoint that represents the remote device. - /// The method to call when the connection has been established. - /// The IPEndPoint of the SOCKS proxy server. - /// An IAsyncProxyResult that references the asynchronous connection. - public override IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback, IPEndPoint proxyEndPoint) { - ProtocolComplete = callback; - Buffer = GetEndPointBytes(remoteEP); - Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server); - AsyncResult = new IAsyncProxyResult(); - return AsyncResult; - } - /// - /// Called when the Socket is connected to the remote proxy server. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnConnect(IAsyncResult ar) { - try { - Server.EndConnect(ar); - } catch (Exception e) { - ProtocolComplete(e); - return; - } - try { - Server.BeginSend(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnSent), Server); - } catch (Exception e) { - ProtocolComplete(e); - } - } - /// - /// Called when the Socket has sent the handshake data. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnSent(IAsyncResult ar) { - try { - if (Server.EndSend(ar) < Buffer.Length) { - ProtocolComplete(new SocketException()); - return; - } - } catch (Exception e) { - ProtocolComplete(e); - return; - } - try { - Buffer = new byte[8]; - Received = 0; - Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceive), Server); - } catch (Exception e) { - ProtocolComplete(e); - } - } - /// - /// Called when the Socket has received a reply from the remote proxy server. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnReceive(IAsyncResult ar) { - try { - int received = Server.EndReceive(ar); - if (received <= 0) { - ProtocolComplete(new SocketException()); - return; - } - Received += received; - if (Received == 8) { - if (Buffer[1] == 90) - ProtocolComplete(null); - else { - Server.Close(); - ProtocolComplete(new ProxyException("Negotiation failed.")); - } - } else { - Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, new AsyncCallback(this.OnReceive), Server); - } - } catch (Exception e) { - ProtocolComplete(e); - } - } - } -} - - diff --git a/shadowsocks-csharp/3rd/ProxySocket/Socks5Handler.cs b/shadowsocks-csharp/3rd/ProxySocket/Socks5Handler.cs deleted file mode 100644 index 6b417248..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/Socks5Handler.cs +++ /dev/null @@ -1,418 +0,0 @@ -/* - Copyright ?2002, The KPD-Team - All rights reserved. - http://www.mentalis.org/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Neither the name of the KPD-Team, nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; - -namespace Shadowsocks._3rd.ProxySocket { - /// - /// Implements the SOCKS5 protocol. - /// - internal sealed class Socks5Handler : SocksHandler { - /// - /// Initiliazes a new Socks5Handler instance. - /// - /// The socket connection with the proxy server. - /// server is null. - public Socks5Handler(Socket server) : this(server, "") {} - /// - /// Initiliazes a new Socks5Handler instance. - /// - /// The socket connection with the proxy server. - /// The username to use. - /// server -or- user is null. - public Socks5Handler(Socket server, string user) : this(server, user, "") {} - /// - /// Initiliazes a new Socks5Handler instance. - /// - /// The socket connection with the proxy server. - /// The username to use. - /// The password to use. - /// server -or- user -or- pass is null. - public Socks5Handler(Socket server, string user, string pass) : base(server, user) { - Password = pass; - } - /// - /// Starts the synchronous authentication process. - /// - /// Authentication with the proxy server failed. - /// The proxy server uses an invalid protocol. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - private void Authenticate() { - Server.Send(new byte [] {5, 2, 0, 2}); - byte[] buffer = ReadBytes(2); - if (buffer[1] == 255) - throw new ProxyException("No authentication method accepted."); - AuthMethod authenticate; - switch (buffer[1]) { - case 0: - authenticate = new AuthNone(Server); - break; - case 2: - authenticate = new AuthUserPass(Server, Username, Password); - break; - default: - throw new ProtocolViolationException(); - } - authenticate.Authenticate(); - } - /// - /// Creates an array of bytes that has to be sent when the user wants to connect to a specific host/port combination. - /// - /// The host to connect to. - /// The port to connect to. - /// An array of bytes that has to be sent when the user wants to connect to a specific host/port combination. - /// host is null. - /// port or host is invalid. - private byte[] GetHostPortBytes(string host, int port) { - if (host == null) - throw new ArgumentNullException(); - if (port <= 0 || port > 65535 || host.Length > 255) - throw new ArgumentException(); - byte [] connect = new byte[7 + host.Length]; - connect[0] = 5; - connect[1] = 1; - connect[2] = 0; //reserved - connect[3] = 3; - connect[4] = (byte)host.Length; - Array.Copy(Encoding.ASCII.GetBytes(host), 0, connect, 5, host.Length); - Array.Copy(PortToBytes(port), 0, connect, host.Length + 5, 2); - return connect; - } - /// - /// Creates an array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint. - /// - /// The IPEndPoint to connect to. - /// An array of bytes that has to be sent when the user wants to connect to a specific IPEndPoint. - /// remoteEP is null. - private byte[] GetEndPointBytes(IPEndPoint remoteEP) { - if (remoteEP == null) - throw new ArgumentNullException(); - byte [] connect = new byte[10]; - connect[0] = 5; - connect[1] = 1; - connect[2] = 0; //reserved - connect[3] = 1; - Array.Copy(AddressToBytes(remoteEP.Address.Address), 0, connect, 4, 4); - Array.Copy(PortToBytes(remoteEP.Port), 0, connect, 8, 2); - return connect; - } - /// - /// Starts negotiating with the SOCKS server. - /// - /// The host to connect to. - /// The port to connect to. - /// host is null. - /// port is invalid. - /// The proxy rejected the request. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - /// The proxy server uses an invalid protocol. - public override void Negotiate(string host, int port) { - Negotiate(GetHostPortBytes(host, port)); - } - /// - /// Starts negotiating with the SOCKS server. - /// - /// The IPEndPoint to connect to. - /// remoteEP is null. - /// The proxy rejected the request. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - /// The proxy server uses an invalid protocol. - public override void Negotiate(IPEndPoint remoteEP) { - Negotiate(GetEndPointBytes(remoteEP)); - } - /// - /// Starts negotiating with the SOCKS server. - /// - /// The bytes to send when trying to authenticate. - /// connect is null. - /// connect is too small. - /// The proxy rejected the request. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - /// The proxy server uses an invalid protocol. - private void Negotiate(byte[] connect) { - Authenticate(); - Server.Send(connect); - byte[] buffer = ReadBytes(4); - if (buffer[1] != 0) { - Server.Close(); - throw new ProxyException(buffer[1]); - } - switch(buffer[3]) { - case 1: - buffer = ReadBytes(6); //IPv4 address with port - break; - case 3: - buffer = ReadBytes(1); - buffer = ReadBytes(buffer[0] + 2); //domain name with port - break; - case 4: - buffer = ReadBytes(18); //IPv6 address with port - break; - default: - Server.Close(); - throw new ProtocolViolationException(); - } - } - /// - /// Starts negotiating asynchronously with the SOCKS server. - /// - /// The host to connect to. - /// The port to connect to. - /// The method to call when the negotiation is complete. - /// The IPEndPoint of the SOCKS proxy server. - /// An IAsyncProxyResult that references the asynchronous connection. - public override IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback, IPEndPoint proxyEndPoint) { - ProtocolComplete = callback; - HandShake = GetHostPortBytes(host, port); - Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server); - AsyncResult = new IAsyncProxyResult(); - return AsyncResult; - } - /// - /// Starts negotiating asynchronously with the SOCKS server. - /// - /// An IPEndPoint that represents the remote device. - /// The method to call when the negotiation is complete. - /// The IPEndPoint of the SOCKS proxy server. - /// An IAsyncProxyResult that references the asynchronous connection. - public override IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback, IPEndPoint proxyEndPoint) { - ProtocolComplete = callback; - HandShake = GetEndPointBytes(remoteEP); - Server.BeginConnect(proxyEndPoint, new AsyncCallback(this.OnConnect), Server); - AsyncResult = new IAsyncProxyResult(); - return AsyncResult; - } - /// - /// Called when the socket is connected to the remote server. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnConnect(IAsyncResult ar) { - try { - Server.EndConnect(ar); - } catch (Exception e) { - ProtocolComplete(e); - return; - } - try { - Server.BeginSend(new byte [] {5, 2, 0, 2}, 0, 4, SocketFlags.None, new AsyncCallback(this.OnAuthSent), Server); - } catch (Exception e) { - ProtocolComplete(e); - } - } - /// - /// Called when the authentication bytes have been sent. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnAuthSent(IAsyncResult ar) { - try { - Server.EndSend(ar); - } catch (Exception e) { - ProtocolComplete(e); - return; - } - try { - Buffer = new byte[1024]; - Received = 0; - Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnAuthReceive), Server); - } catch (Exception e) { - ProtocolComplete(e); - } - } - /// - /// Called when an authentication reply has been received. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnAuthReceive(IAsyncResult ar) { - try { - Received += Server.EndReceive(ar); - if (Received <= 0) - throw new SocketException(); - } catch (Exception e) { - ProtocolComplete(e); - return; - } - try { - if (Received < 2) { - Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, new AsyncCallback(this.OnAuthReceive), Server); - } else { - AuthMethod authenticate; - switch(Buffer[1]) { - case 0: - authenticate = new AuthNone(Server); - break; - case 2: - authenticate = new AuthUserPass(Server, Username, Password); - break; - default: - ProtocolComplete(new SocketException()); - return; - } - authenticate.BeginAuthenticate(new HandShakeComplete(this.OnAuthenticated)); - } - } catch (Exception e) { - ProtocolComplete(e); - } - } - /// - /// Called when the socket has been successfully authenticated with the server. - /// - /// The exception that has occured while authenticating, or null if no error occured. - private void OnAuthenticated(Exception e) { - if (e != null) { - ProtocolComplete(e); - return; - } - try { - Server.BeginSend(HandShake, 0, HandShake.Length, SocketFlags.None, new AsyncCallback(this.OnSent), Server); - } catch (Exception ex) { - ProtocolComplete(ex); - } - } - /// - /// Called when the connection request has been sent. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnSent(IAsyncResult ar) { - try { - Server.EndSend(ar); - } catch (Exception e) { - ProtocolComplete(e); - return; - } - try { - Buffer = new byte[5]; - Received = 0; - Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReceive), Server); - } catch (Exception e) { - ProtocolComplete(e); - } - } - /// - /// Called when a connection reply has been received. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnReceive(IAsyncResult ar) { - try { - Received += Server.EndReceive(ar); - } catch (Exception e) { - ProtocolComplete(e); - return; - } - try { - if (Received == Buffer.Length) - ProcessReply(Buffer); - else - Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, new AsyncCallback(this.OnReceive), Server); - } catch (Exception e) { - ProtocolComplete(e); - } - } - /// - /// Processes the received reply. - /// - /// The received reply - /// The received reply is invalid. - private void ProcessReply(byte[] buffer) { - switch(buffer[3]) { - case 1: - Buffer = new byte[5]; //IPv4 address with port - 1 byte - break; - case 3: - Buffer = new byte[buffer[4] + 2]; //domain name with port - break; - case 4: - buffer = new byte[17]; //IPv6 address with port - 1 byte - break; - default: - throw new ProtocolViolationException(); - } - Received = 0; - Server.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, new AsyncCallback(this.OnReadLast), Server); - } - /// - /// Called when the last bytes are read from the socket. - /// - /// Stores state information for this asynchronous operation as well as any user-defined data. - private void OnReadLast(IAsyncResult ar) { - try { - Received += Server.EndReceive(ar); - } catch (Exception e) { - ProtocolComplete(e); - return; - } - try { - if (Received == Buffer.Length) - ProtocolComplete(null); - else - Server.BeginReceive(Buffer, Received, Buffer.Length - Received, SocketFlags.None, new AsyncCallback(this.OnReadLast), Server); - } catch (Exception e) { - ProtocolComplete(e); - } - } - /// - /// Gets or sets the password to use when authenticating with the SOCKS5 server. - /// - /// The password to use when authenticating with the SOCKS5 server. - private string Password { - get { - return m_Password; - } - set { - if (value == null) - throw new ArgumentNullException(); - m_Password = value; - } - } - /// - /// Gets or sets the bytes to use when sending a connect request to the proxy server. - /// - /// The array of bytes to use when sending a connect request to the proxy server. - private byte[] HandShake { - get { - return m_HandShake; - } - set { - m_HandShake = value; - } - } - // private variables - /// Holds the value of the Password property. - private string m_Password; - /// Holds the value of the HandShake property. - private byte[] m_HandShake; - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/3rd/ProxySocket/SocksHandler.cs b/shadowsocks-csharp/3rd/ProxySocket/SocksHandler.cs deleted file mode 100644 index 3eb211df..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/SocksHandler.cs +++ /dev/null @@ -1,204 +0,0 @@ -/* - Copyright ?2002, The KPD-Team - All rights reserved. - http://www.mentalis.org/ - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - - Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - - Neither the name of the KPD-Team, nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -using System; -using System.Net; -using System.Net.Sockets; - -namespace Shadowsocks._3rd.ProxySocket { - /// - /// References the callback method to be called when the protocol negotiation is completed. - /// - internal delegate void HandShakeComplete(Exception error); - /// - /// Implements a specific version of the SOCKS protocol. This is an abstract class; it must be inherited. - /// - internal abstract class SocksHandler { - /// - /// Initilizes a new instance of the SocksHandler class. - /// - /// The socket connection with the proxy server. - /// The username to use when authenticating with the server. - /// server -or- user is null. - public SocksHandler(Socket server, string user) { - Server = server; - Username = user; - } - /// - /// Converts a port number to an array of bytes. - /// - /// The port to convert. - /// An array of two bytes that represents the specified port. - protected byte[] PortToBytes(int port) { - byte [] ret = new byte[2]; - ret[0] = (byte)(port / 256); - ret[1] = (byte)(port % 256); - return ret; - } - /// - /// Converts an IP address to an array of bytes. - /// - /// The IP address to convert. - /// An array of four bytes that represents the specified IP address. - protected byte[] AddressToBytes(long address) { - byte [] ret = new byte[4]; - ret[0] = (byte)(address % 256); - ret[1] = (byte)((address / 256) % 256); - ret[2] = (byte)((address / 65536) % 256); - ret[3] = (byte)(address / 16777216); - return ret; - } - /// - /// Reads a specified number of bytes from the Server socket. - /// - /// The number of bytes to return. - /// An array of bytes. - /// The number of bytes to read is invalid. - /// An operating system error occurs while accessing the Socket. - /// The Socket has been closed. - protected byte[] ReadBytes(int count) { - if (count <= 0) - throw new ArgumentException(); - byte[] buffer = new byte[count]; - int received = 0; - while(received != count) { - received += Server.Receive(buffer, received, count - received, SocketFlags.None); - } - return buffer; - } - /// - /// Gets or sets the socket connection with the proxy server. - /// - /// A Socket object that represents the connection with the proxy server. - /// The specified value is null. - protected Socket Server { - get { - return m_Server; - } - set { - if (value == null) - throw new ArgumentNullException(); - m_Server = value; - } - } - /// - /// Gets or sets the username to use when authenticating with the proxy server. - /// - /// A string that holds the username to use when authenticating with the proxy server. - /// The specified value is null. - protected string Username { - get { - return m_Username; - } - set { - if (value == null) - throw new ArgumentNullException(); - m_Username = value; - } - } - /// - /// Gets or sets the return value of the BeginConnect call. - /// - /// An IAsyncProxyResult object that is the return value of the BeginConnect call. - protected IAsyncProxyResult AsyncResult { - get { - return m_AsyncResult; - } - set { - m_AsyncResult = value; - } - } - /// - /// Gets or sets a byte buffer. - /// - /// An array of bytes. - protected byte[] Buffer { - get { - return m_Buffer; - } - set { - m_Buffer = value; - } - } - /// - /// Gets or sets the number of bytes that have been received from the remote proxy server. - /// - /// An integer that holds the number of bytes that have been received from the remote proxy server. - protected int Received { - get { - return m_Received; - } - set { - m_Received = value; - } - } - // private variables - /// Holds the value of the Server property. - private Socket m_Server; - /// Holds the value of the Username property. - private string m_Username; - /// Holds the value of the AsyncResult property. - private IAsyncProxyResult m_AsyncResult; - /// Holds the value of the Buffer property. - private byte[] m_Buffer; - /// Holds the value of the Received property. - private int m_Received; - /// Holds the address of the method to call when the SOCKS protocol has been completed. - protected HandShakeComplete ProtocolComplete; - /// - /// Starts negotiating with a SOCKS proxy server. - /// - /// The remote server to connect to. - /// The remote port to connect to. - public abstract void Negotiate(string host, int port); - /// - /// Starts negotiating with a SOCKS proxy server. - /// - /// The remote endpoint to connect to. - public abstract void Negotiate(IPEndPoint remoteEP); - /// - /// Starts negotiating asynchronously with a SOCKS proxy server. - /// - /// An IPEndPoint that represents the remote device. - /// The method to call when the connection has been established. - /// The IPEndPoint of the SOCKS proxy server. - /// An IAsyncProxyResult that references the asynchronous connection. - public abstract IAsyncProxyResult BeginNegotiate(IPEndPoint remoteEP, HandShakeComplete callback, IPEndPoint proxyEndPoint); - /// - /// Starts negotiating asynchronously with a SOCKS proxy server. - /// - /// The remote server to connect to. - /// The remote port to connect to. - /// The method to call when the connection has been established. - /// The IPEndPoint of the SOCKS proxy server. - /// An IAsyncProxyResult that references the asynchronous connection. - public abstract IAsyncProxyResult BeginNegotiate(string host, int port, HandShakeComplete callback, IPEndPoint proxyEndPoint); - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebRequest.cs b/shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebRequest.cs deleted file mode 100644 index 2a75542a..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebRequest.cs +++ /dev/null @@ -1,283 +0,0 @@ -using System; -using System.Collections.Specialized; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Text; - -namespace Shadowsocks._3rd.ProxySocket -{ - public class SocksHttpWebRequest : WebRequest - { - - #region Private - private Encoding _correctEncoding; - #endregion Private - - #region Member Variables - - private readonly Uri _requestUri; - private WebHeaderCollection _requestHeaders; - private string _method; - private SocksHttpWebResponse _response; - private string _requestMessage; - private byte[] _requestContentBuffer; - - // darn MS for making everything internal (yeah, I'm talking about you, System.net.KnownHttpVerb) - static readonly StringCollection validHttpVerbs = - new StringCollection { "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "OPTIONS" }; - - #endregion - - #region Constructor - - private SocksHttpWebRequest(Uri requestUri) - { - _requestUri = requestUri; - _correctEncoding = Encoding.Default; - } - - #endregion - - #region WebRequest Members - - public override WebResponse GetResponse() - { - if (Proxy == null) - { - throw new InvalidOperationException("Proxy property cannot be null."); - } - if (String.IsNullOrEmpty(Method)) - { - throw new InvalidOperationException("Method has not been set."); - } - - if (RequestSubmitted) - { - return _response; - } - _response = InternalGetResponse(); - RequestSubmitted = true; - return _response; - } - - public override Uri RequestUri - { - get { return _requestUri; } - } - - public override IWebProxy Proxy { get; set; } - - public override WebHeaderCollection Headers - { - get - { - if (_requestHeaders == null) - { - _requestHeaders = new WebHeaderCollection(); - } - return _requestHeaders; - } - set - { - if (RequestSubmitted) - { - throw new InvalidOperationException("This operation cannot be performed after the request has been submitted."); - } - _requestHeaders = value; - } - } - - public bool RequestSubmitted { get; private set; } - - public override string Method - { - get - { - return _method ?? "GET"; - } - set - { - if (validHttpVerbs.Contains(value)) - { - _method = value; - } - else - { - throw new ArgumentOutOfRangeException("value", string.Format("'{0}' is not a known HTTP verb.", value)); - } - } - } - - public override long ContentLength { get; set; } - - public override string ContentType { get; set; } - - public override Stream GetRequestStream() - { - if (RequestSubmitted) - { - throw new InvalidOperationException("This operation cannot be performed after the request has been submitted."); - } - - if (_requestContentBuffer == null) - { - _requestContentBuffer = new byte[ContentLength]; - } - else if (ContentLength == default(long)) - { - _requestContentBuffer = new byte[int.MaxValue]; - } - else if (_requestContentBuffer.Length != ContentLength) - { - Array.Resize(ref _requestContentBuffer, (int)ContentLength); - } - return new MemoryStream(_requestContentBuffer); - } - - #endregion - - #region Methods - - public static new WebRequest Create(string requestUri) - { - return new SocksHttpWebRequest(new Uri(requestUri)); - } - - public static new WebRequest Create(Uri requestUri) - { - return new SocksHttpWebRequest(requestUri); - } - - private string BuildHttpRequestMessage() - { - if (RequestSubmitted) - { - throw new InvalidOperationException("This operation cannot be performed after the request has been submitted."); - } - - var message = new StringBuilder(); - message.AppendFormat("{0} {1} HTTP/1.0\r\nHost: {2}\r\n", Method, RequestUri.PathAndQuery, RequestUri.Host); - - // add the headers - foreach (var key in Headers.Keys) - { - message.AppendFormat("{0}: {1}\r\n", key, Headers[key.ToString()]); - } - - if (!string.IsNullOrEmpty(ContentType)) - { - message.AppendFormat("Content-Type: {0}\r\n", ContentType); - } - if (ContentLength > 0) - { - message.AppendFormat("Content-Length: {0}\r\n", ContentLength); - } - - // add a blank line to indicate the end of the headers - message.Append("\r\n"); - - // add content - if (_requestContentBuffer != null && _requestContentBuffer.Length > 0) - { - using (var stream = new MemoryStream(_requestContentBuffer, false)) - { - using (var reader = new StreamReader(stream)) - { - message.Append(reader.ReadToEnd()); - } - } - } - - return message.ToString(); - } - - private SocksHttpWebResponse InternalGetResponse() - { - var response = new StringBuilder(); - using (var _socksConnection = - new ProxySocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - { - var proxyUri = Proxy.GetProxy(RequestUri); - var ipAddress = GetProxyIpAddress(proxyUri); - _socksConnection.ProxyEndPoint = new IPEndPoint(ipAddress, proxyUri.Port); - _socksConnection.ProxyType = ProxyTypes.Socks5; - - - - // open connection - _socksConnection.Connect(RequestUri.Host, 80); - // send an HTTP request - _socksConnection.Send(_correctEncoding.GetBytes(RequestMessage)); - // read the HTTP reply - var buffer = new byte[1024]; - - var bytesReceived = _socksConnection.Receive(buffer); - while (bytesReceived > 0) - { - string chunk = _correctEncoding.GetString(buffer, 0, bytesReceived); - string encString = EncodingHelper.GetEncodingFromChunk(chunk); - if (!string.IsNullOrEmpty(encString)) - { - try - { - _correctEncoding = Encoding.GetEncoding(encString); - } - catch - { - //TODO: do something here - } - } - response.Append(chunk); - bytesReceived = _socksConnection.Receive(buffer); - } - } - return new SocksHttpWebResponse(response.ToString(),_correctEncoding); - } - - private static IPAddress GetProxyIpAddress(Uri proxyUri) - { - IPAddress ipAddress; - if (!IPAddress.TryParse(proxyUri.Host, out ipAddress)) - { - try - { - return Dns.GetHostEntry(proxyUri.Host).AddressList[0]; - } - catch (Exception e) - { - throw new InvalidOperationException( - string.Format("Unable to resolve proxy hostname '{0}' to a valid IP address.", proxyUri.Host), e); - } - } - return ipAddress; - } - - #endregion - - #region Properties - - public Encoding CorrectEncoding - { - get - { - return _correctEncoding; - } - } - - public string RequestMessage - { - get - { - if (string.IsNullOrEmpty(_requestMessage)) - { - _requestMessage = BuildHttpRequestMessage(); - } - return _requestMessage; - } - } - - #endregion - - } -} diff --git a/shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebResponse.cs b/shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebResponse.cs deleted file mode 100644 index a2a711b3..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/SocksHttpWebResponse.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Text; - -namespace Shadowsocks._3rd.ProxySocket -{ - public class SocksHttpWebResponse : WebResponse - { - - #region Member Variables - - private WebHeaderCollection _httpResponseHeaders; - private string _responseContent; - - #endregion - - #region Constructors - - public SocksHttpWebResponse(string httpResponseMessage) - { - SetHeadersAndResponseContent(httpResponseMessage); - CorrectEncoding = Encoding.Default; - } - - public SocksHttpWebResponse(string httpResponseMessage, Encoding encoding) - { - SetHeadersAndResponseContent(httpResponseMessage); - CorrectEncoding = encoding; - } - - #endregion - - #region WebResponse Members - - public override Stream GetResponseStream() - { - return ResponseContent.Length == 0 ? Stream.Null : new MemoryStream(CorrectEncoding.GetBytes(ResponseContent)); - } - - public override void Close() { /* the base implementation throws an exception */ } - - public override WebHeaderCollection Headers - { - get - { - if (_httpResponseHeaders == null) - { - _httpResponseHeaders = new WebHeaderCollection(); - } - return _httpResponseHeaders; - } - } - - public override long ContentLength - { - get - { - return ResponseContent.Length; - } - set - { - throw new NotSupportedException(); - } - } - - #endregion - - #region Methods - - private void SetHeadersAndResponseContent(string responseMessage) - { - if (string.IsNullOrEmpty(responseMessage)) - return; - - // the HTTP headers can be found before the first blank line - var indexOfFirstBlankLine = responseMessage.IndexOf("\r\n\r\n"); - - var headers = responseMessage.Substring(0, indexOfFirstBlankLine); - var headerValues = headers.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries); - // ignore the first line in the header since it is the HTTP response code - for (int i = 1; i < headerValues.Length; i++) - { - var headerEntry = headerValues[i].Split(new[] { ':' }); - Headers.Add(headerEntry[0], headerEntry[1]); - } - - ResponseContent = responseMessage.Substring(indexOfFirstBlankLine + 4); - } - - #endregion - - #region Properties - - private string ResponseContent - { - get { return _responseContent ?? string.Empty; } - set { _responseContent = value; } - } - - public Encoding CorrectEncoding - { - get;set; - } - - #endregion - - } -} diff --git a/shadowsocks-csharp/3rd/ProxySocket/SocksWebClient.cs b/shadowsocks-csharp/3rd/ProxySocket/SocksWebClient.cs deleted file mode 100644 index 4f787755..00000000 --- a/shadowsocks-csharp/3rd/ProxySocket/SocksWebClient.cs +++ /dev/null @@ -1,312 +0,0 @@ -using System; -using System.Net; - -namespace Shadowsocks._3rd.ProxySocket -{ - #region old - /* - public class Calculagraph - { - /// - /// 时间到事件 - /// - public event TimeoutCaller TimeOver; - - /// - /// 开始时间 - /// - private DateTime _startTime; - private TimeSpan _timeout = new TimeSpan(0, 0, 10); - private bool _hasStarted = false; - object _userdata; - - /// - /// 计时器构造方法 - /// - /// 计时结束时回调的用户数据 - public Calculagraph(object userdata) - { - TimeOver += new TimeoutCaller(OnTimeOver); - _userdata = userdata; - } - - /// - /// 超时退出 - /// - /// - public virtual void OnTimeOver(object userdata) - { - Stop(); - } - - /// - /// 过期时间(秒) - /// - public int Timeout - { - get - { - return _timeout.Seconds; - } - set - { - if (value <= 0) - return; - _timeout = new TimeSpan(0, 0, value); - } - } - - /// - /// 是否已经开始计时 - /// - public bool HasStarted - { - get - { - return _hasStarted; - } - } - - /// - /// 开始计时 - /// - public void Start() - { - Reset(); - _hasStarted = true; - System.Threading.Thread th = new System.Threading.Thread(WaitCall); - th.IsBackground = true; - th.Start(); - } - - /// - /// 重置 - /// - public void Reset() - { - _startTime = DateTime.Now; - } - - /// - /// 停止计时 - /// - public void Stop() - { - _hasStarted = false; - } - - /// - /// 检查是否过期 - /// - /// - private bool checkTimeout() - { - return (DateTime.Now - _startTime).Seconds >= Timeout; - } - - private void WaitCall() - { - try - { - //循环检测是否过期 - while (_hasStarted && !checkTimeout()) - { - System.Threading.Thread.Sleep(1000); - } - if (TimeOver != null) - TimeOver(_userdata); - } - catch (Exception) - { - Stop(); - } - } - } - - /// - /// 过期时回调委托 - /// - /// - public delegate void TimeoutCaller(object userdata); - - public class CNNWebClient : WebClient - { - - private Calculagraph _timer; - private int _timeOut = 5; - - /// - /// 过期时间 - /// - public int Timeout - { - get - { - return _timeOut; - } - set - { - if (value <= 0) - _timeOut = 5; - _timeOut = value; - } - } - - /// - /// 重写GetWebRequest,添加WebRequest对象超时时间 - /// - /// - /// - protected override WebRequest GetWebRequest(Uri address) - { - HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address); - request.Timeout = 1000 * Timeout; - request.ReadWriteTimeout = 1000 * Timeout; - return request; - } - - /// - /// 带过期计时的下载 - /// - public void DownloadFileAsyncWithTimeout(Uri address, string fileName, object userToken) - { - if (_timer == null) - { - _timer = new Calculagraph(this); - _timer.Timeout = Timeout; - _timer.TimeOver += new TimeoutCaller(_timer_TimeOver); - this.DownloadProgressChanged += new DownloadProgressChangedEventHandler(CNNWebClient_DownloadProgressChanged); - } - - DownloadFileAsync(address, fileName, userToken); - _timer.Start(); - } - - /// - /// WebClient下载过程事件,接收到数据时引发 - /// - /// - /// - void CNNWebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) - { - _timer.Reset();//重置计时器 - } - - /// - /// 计时器过期 - /// - /// - void _timer_TimeOver(object userdata) - { - this.CancelAsync();//取消下载 - } - } - */ - #endregion - - public class SocksWebClient : WebClient - { - public IProxyDetails ProxyDetails { get; set; } - public string UserAgent { get; set; } - - protected override WebRequest GetWebRequest(Uri address) - { - WebRequest result = null; - - if (ProxyDetails != null) - { - if (ProxyDetails.ProxyType == ProxyType.Proxy) - { - result = (HttpWebRequest)WebRequest.Create(address); - result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress); - if (!string.IsNullOrEmpty(UserAgent)) - ((HttpWebRequest)result).UserAgent = UserAgent; - } - else if (ProxyDetails.ProxyType == ProxyType.Socks) - { - result = SocksHttpWebRequest.Create(address); - result.Proxy = new WebProxy(ProxyDetails.FullProxyAddress); - //TODO: implement user and password - - } - else if (ProxyDetails.ProxyType == ProxyType.None) - { - result = (HttpWebRequest)WebRequest.Create(address); - if (!string.IsNullOrEmpty(UserAgent)) - ((HttpWebRequest)result).UserAgent = UserAgent; - } - } - else - { - result = (HttpWebRequest)WebRequest.Create(address); - if (!string.IsNullOrEmpty(UserAgent)) - ((HttpWebRequest)result).UserAgent = UserAgent; - } - return result; - } - } - - public interface IProxyDetails - { - ProxyType ProxyType { get; set; } - /// - /// adress and port - /// - string FullProxyAddress { get; set; } - string ProxyAddress { get; set; } - int ProxyPort { get; set; } - string ProxyUserName { get; set; } - string ProxyPassword { get; set; } - } - public class ProxyDetails : IProxyDetails - { - public ProxyType ProxyType { get; set; } - /// - /// adress and port - /// - public string FullProxyAddress { get; set; } - public string ProxyAddress { get; set; } - public int ProxyPort { get; set; } - public string ProxyUserName { get; set; } - public string ProxyPassword { get; set; } - - public ProxyDetails() - { - } - public ProxyDetails(int port) - { - FullProxyAddress = IPAddress.Loopback + ":" + port; - ProxyAddress = IPAddress.Loopback.ToString(); - ProxyPort = port; - ProxyType = ProxyType.Socks; - } - } - - public enum ProxyType - { - None = 0, - Proxy = 1, - Socks = 2 - } - - public static class EncodingHelper - { - public static string GetEncodingFromChunk(string chunk) - { - string charset = null; - int charsetStart = chunk.IndexOf("charset="); - int charsetEnd = -1; - if (charsetStart != -1) - { - charsetEnd = chunk.IndexOfAny(new[] { ' ', '\"', ';', '\r', '\n' }, charsetStart); - if (charsetEnd != -1) - { - int start = charsetStart + 8; - charset = chunk.Substring(start, charsetEnd - start + 1); - charset = charset.TrimEnd(new Char[] { '>', '"', '\r', '\n' }); - } - } - return charset; - } - } -} diff --git a/shadowsocks-csharp/3rd/QQWry.cs b/shadowsocks-csharp/3rd/QQWry.cs deleted file mode 100644 index f64331d1..00000000 --- a/shadowsocks-csharp/3rd/QQWry.cs +++ /dev/null @@ -1,754 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; - -namespace Shadowsocks._3rd -{ - /// - /// QQWry 的摘要说明。 - /// - public class QQWry - { - #region Properties - /// - ///第一种模式 - /// - private const byte REDIRECT_MODE_1 = 0x01; - - /// - ///第二种模式 - /// - private const byte REDIRECT_MODE_2 = 0x02; - - /// - ///每条记录长度 - /// - private const int IP_RECORD_LENGTH = 7; - - /// - ///文件对象 - /// - private FileStream ipFile; - - private const string unCountry = "未知国家"; - private const string unArea = "未知地区"; - - /// - ///索引开始位置 - /// - private long ipBegin; - - /// - ///索引结束位置 - /// - private long ipEnd; - - /// - /// IP对象 - /// - private IPLocation loc; - - /// - ///存储文本内容 - /// - private byte[] buf; - - /// - ///存储3字节 - /// - private byte[] b3; - - /// - ///存储4字节IP地址 - /// - private byte[] b4; - #endregion - - #region 构造函数 - - /// - ///构造函数 - /// - ///IP数据库文件绝对路径 - public QQWry(string ipfile) - { - - buf = new byte[100]; - b3 = new byte[3]; - b4 = new byte[4]; - try - { - ipFile = new FileStream(ipfile, FileMode.Open); - } - catch (Exception ex) - { - throw new Exception(ex.Message); - } - ipBegin = readLong4(0); - ipEnd = readLong4(4); - loc = new IPLocation(); - } - #endregion - - #region 根据IP地址搜索 - - /// - ///搜索IP地址搜索 - /// - /// - /// - public IPLocation SearchIPLocation(string ip) - { - //将字符IP转换为字节 - string[] ipSp = ip.Split('.'); - if (ipSp.Length != 4) - { - throw new ArgumentOutOfRangeException("不是合法的IP地址!"); - } - byte[] IP = new byte[4]; - for (int i = 0; i < IP.Length; i++) - { - IP[i] = (byte) (Int32.Parse(ipSp[i]) & 0xFF); - } - - IPLocation local = null; - long offset = locateIP(IP); - - if (offset != -1) - { - local = getIPLocation(offset); - } - - if (local == null) - { - local = new IPLocation(); - local.area = unArea; - local.country = unCountry; - } - return local; - } - #endregion - - #region 取得具体信息 - /// - ///取得具体信息 - /// - /// - /// - private IPLocation getIPLocation(long offset) - { - ipFile.Position = offset + 4; - //读取第一个字节判断是否是标志字节 - byte one = (byte) ipFile.ReadByte(); - if (one == REDIRECT_MODE_1) - { - //第一种模式 - //读取国家偏移 - long countryOffset = readLong3(); - //转至偏移处 - ipFile.Position = countryOffset; - //再次检查标志字节 - byte b = (byte) ipFile.ReadByte(); - if (b == REDIRECT_MODE_2) - { - loc.country = readString(readLong3()); - ipFile.Position = countryOffset + 4; - } - else - loc.country = readString(countryOffset); - - //读取地区标志 - loc.area = readArea(ipFile.Position); - - } - else if (one == REDIRECT_MODE_2) - { - //第二种模式 - loc.country = readString(readLong3()); - loc.area = readArea(offset + 8); - } - else - { - //普通模式 - loc.country = readString(--ipFile.Position); - loc.area = readString(ipFile.Position); - } - return loc; - } - #endregion - - #region 取得地区信息 - /// - ///读取地区名称 - /// - /// - /// - private string readArea(long offset) - { - ipFile.Position = offset; - byte one = (byte) ipFile.ReadByte(); - if (one == REDIRECT_MODE_1 || one == REDIRECT_MODE_2) - { - long areaOffset = readLong3(offset + 1); - if (areaOffset == 0) - return unArea; - else - { - return readString(areaOffset); - } - } - else - { - return readString(offset); - } - } - - #endregion - - #region 读取字符串 - - /// - ///读取字符串 - /// - /// - /// - - private string readString(long offset) - { - ipFile.Position = offset; - int i = 0; - for (i = 0, buf[i] = (byte) ipFile.ReadByte(); buf[i] != (byte) (0); buf[++i] = (byte) ipFile.ReadByte()) ; - - if (i > 0) - return Encoding.Default.GetString(buf, 0, i); - else - return ""; - } - #endregion - - #region 查找IP地址所在的绝对偏移量 - - /**/ - - /// - ///查找IP地址所在的绝对偏移量 - /// - /// - /// - - private long locateIP(byte[] ip) - { - long m = 0; - int r; - - //比较第一个IP项 - readIP(ipBegin, b4); - r = compareIP(ip, b4); - if (r == 0) - return ipBegin; - else if (r < 0) - return -1; - //开始二分搜索 - for (long i = ipBegin, j = ipEnd; i < j;) - { - m = this.getMiddleOffset(i, j); - readIP(m, b4); - r = compareIP(ip, b4); - if (r > 0) - i = m; - else if (r < 0) - { - if (m == j) - { - j -= IP_RECORD_LENGTH; - m = j; - } - else - { - j = m; - } - } - else - return readLong3(m + 4); - } - m = readLong3(m + 4); - readIP(m, b4); - r = compareIP(ip, b4); - if (r <= 0) - return m; - else - return -1; - } - - #endregion - - #region 读出4字节的IP地址 - - /**/ - - /// - ///从当前位置读取四字节,此四字节是IP地址 - /// - /// - /// - - private void readIP(long offset, byte[] ip) - { - ipFile.Position = offset; - ipFile.Read(ip, 0, ip.Length); - byte tmp = ip[0]; - ip[0] = ip[3]; - ip[3] = tmp; - tmp = ip[1]; - ip[1] = ip[2]; - ip[2] = tmp; - } - - #endregion - - #region 比较IP地址是否相同 - - /**/ - - /// - ///比较IP地址是否相同 - /// - /// - /// - ///0:相等,1:ip大于beginIP,-1:小于 - - private int compareIP(byte[] ip, byte[] beginIP) - { - for (int i = 0; i < 4; i++) - { - int r = compareByte(ip[i], beginIP[i]); - if (r != 0) - return r; - } - return 0; - } - - #endregion - - #region 比较两个字节是否相等 - - /**/ - - /// - ///比较两个字节是否相等 - /// - /// - /// - /// - - private int compareByte(byte bsrc, byte bdst) - { - if ((bsrc & 0xFF) > (bdst & 0xFF)) - return 1; - else if ((bsrc ^ bdst) == 0) - return 0; - else - return -1; - } - - #endregion - - #region 根据当前位置读取4字节 - - /**/ - - /// - ///从当前位置读取4字节,转换为长整型 - /// - /// - /// - - private long readLong4(long offset) - { - long ret = 0; - ipFile.Position = offset; - ret |= (ipFile.ReadByte() & 0xFF); - ret |= ((ipFile.ReadByte() << 8) & 0xFF00); - ret |= ((ipFile.ReadByte() << 16) & 0xFF0000); - ret |= ((ipFile.ReadByte() << 24) & 0xFF000000); - return ret; - } - - #endregion - - #region 根据当前位置,读取3字节 - - /**/ - - /// - ///根据当前位置,读取3字节 - /// - /// - /// - - private long readLong3(long offset) - { - long ret = 0; - ipFile.Position = offset; - ret |= (ipFile.ReadByte() & 0xFF); - ret |= ((ipFile.ReadByte() << 8) & 0xFF00); - ret |= ((ipFile.ReadByte() << 16) & 0xFF0000); - return ret; - } - - #endregion - - #region 从当前位置读取3字节 - - /**/ - - /// - ///从当前位置读取3字节 - /// - /// - - private long readLong3() - { - long ret = 0; - ret |= (ipFile.ReadByte() & 0xFF); - ret |= ((ipFile.ReadByte() << 8) & 0xFF00); - ret |= ((ipFile.ReadByte() << 16) & 0xFF0000); - return ret; - } - - #endregion - - #region 取得begin和end之间的偏移量 - - /**/ - - /// - ///取得begin和end中间的偏移 - /// - /// - /// - /// - private long getMiddleOffset(long begin, long end) - { - long records = (end - begin)/IP_RECORD_LENGTH; - records >>= 1; - if (records == 0) - records = 1; - return begin + records*IP_RECORD_LENGTH; - } - - #endregion - } - - public class IPLocation - { - public String country; - public String area; - - public IPLocation() - { - country = area = ""; - } - - public IPLocation getCopy() - { - IPLocation ret = new IPLocation(); - ret.country = country; - ret.area = area; - return ret; - } - } - -#region Old - /* - /// - /// 存储地区的结构 - /// - public struct stLocation - { - /// - /// 未使用 - /// - public string Ip; - - /// - /// 国家名 - /// - public string Contry; - - /// - /// 城市名 - /// - public string City; - } - - - /// - /// 纯真IP数据库查询辅助类 - /// - public static class QqwryHelper - { - #region 成员变量 - - private const byte REDIRECT_MODE_1 = 0x01;//名称存储模式一 - private const byte REDIRECT_MODE_2 = 0x02;//名称存储模式二 - private const int IP_RECORD_LENGTH = 7; //每条索引的长度 - - private static long beginIndex = 0;//索引开始 - private static long endIndex = 0;//索引结束 - - private static stLocation loc = new stLocation() { City = "未知城市", Contry = "未知国家" }; - - private static Stream fs; - - #endregion - - #region 私有成员函数 - - /// - /// 在索引区查找指定IP对应的记录区地址 - /// - /// 字节型IP - /// - private static long SearchIpIndex(byte[] _ip) - { - long index = 0; - - byte[] nextIp = new byte[4]; - - ReadIp(beginIndex, ref nextIp); - - int flag = CompareIp(_ip, nextIp); - if (flag == 0) return beginIndex; - else if (flag < 0) return -1; - - for (long i = beginIndex, j = endIndex; i < j; ) - { - index = GetMiddleOffset(i, j); - - ReadIp(index, ref nextIp); - flag = CompareIp(_ip, nextIp); - - if (flag == 0) return ReadLong(index + 4, 3); - else if (flag > 0) i = index; - else if (flag < 0) - { - if (index == j) - { - j -= IP_RECORD_LENGTH; - index = j; - } - else - { - j = index; - } - } - } - - index = ReadLong(index + 4, 3); - ReadIp(index, ref nextIp); - - flag = CompareIp(_ip, nextIp); - if (flag <= 0) return index; - else return -1; - } - - /// - /// 获取两个索引的中间位置 - /// - /// 索引1 - /// 索引2 - /// - private static long GetMiddleOffset(long begin, long end) - { - long records = (end - begin) / IP_RECORD_LENGTH; - records >>= 1; - if (records == 0) records = 1; - return begin + records * IP_RECORD_LENGTH; - } - - /// - /// 读取记录区的地区名称 - /// - /// 位置 - /// - private static string ReadString(long offset) - { - fs.Position = offset; - - byte b = (byte)fs.ReadByte(); - if (b == REDIRECT_MODE_1 || b == REDIRECT_MODE_2) - { - long areaOffset = ReadLong(offset + 1, 3); - if (areaOffset == 0) - return "未知地区"; - - else fs.Position = areaOffset; - } - else - { - fs.Position = offset; - } - - List buf = new List(); - - int i = 0; - for (i = 0, buf.Add((byte)fs.ReadByte()); buf[i] != (byte)(0); ++i, buf.Add((byte)fs.ReadByte())) ; - - if (i > 0) return Encoding.Default.GetString(buf.ToArray(), 0, i); - else return ""; - } - - /// - /// 从自定位置读取指定长度的字节,并转换为big-endian字节序(数据源文件为little-endian字节序) - /// - /// 开始读取位置 - /// 读取长度 - /// - private static long ReadLong(long offset, int length) - { - long ret = 0; - fs.Position = offset; - for (int i = 0; i < length; i++) - { - ret |= ((fs.ReadByte() << (i * 8)) & (0xFF * ((int)Math.Pow(16, i * 2)))); - } - - return ret; - } - - /// - /// 从指定位置处读取一个IP - /// - /// 指定的位置 - /// 保存IP的缓存区 - private static void ReadIp(long offset, ref byte[] _buffIp) - { - fs.Position = offset; - fs.Read(_buffIp, 0, _buffIp.Length); - - for (int i = 0; i < _buffIp.Length / 2; i++) - { - byte temp = _buffIp[i]; - _buffIp[i] = _buffIp[_buffIp.Length - i - 1]; - _buffIp[_buffIp.Length - i - 1] = temp; - } - } - - /// - /// 比较两个IP是否相等,1:IP1大于IP2,-1:IP1小于IP2,0:IP1=IP2 - /// - /// IP1 - /// IP2 - /// - private static int CompareIp(byte[] _buffIp1, byte[] _buffIp2) - { - if (_buffIp1.Length > 4 || _buffIp2.Length > 4) throw new Exception("指定的IP无效。"); - - for (int i = 0; i < 4; i++) - { - if ((_buffIp1[i] & 0xFF) > (_buffIp2[i] & 0xFF)) return 1; - else if ((_buffIp1[i] & 0xFF) < (_buffIp2[i] & 0xFF)) return -1; - } - - return 0; - } - - /// - /// 从指定的地址获取区域名称 - /// - /// - private static void GetAreaName(long offset) - { - fs.Position = offset + 4; - long flag = fs.ReadByte(); - long contryIndex = 0; - if (flag == REDIRECT_MODE_1) - { - contryIndex = ReadLong(fs.Position, 3); - fs.Position = contryIndex; - - flag = fs.ReadByte(); - - if (flag == REDIRECT_MODE_2) //是否仍然为重定向 - { - loc.Contry = ReadString(ReadLong(fs.Position, 3)); - fs.Position = contryIndex + 4; - } - else - { - loc.Contry = ReadString(contryIndex); - } - loc.City = ReadString(fs.Position); - } - else if (flag == REDIRECT_MODE_2) - { - contryIndex = ReadLong(fs.Position, 3); - loc.Contry = ReadString(contryIndex); - loc.City = ReadString(contryIndex + 3); - } - else - { - loc.Contry = ReadString(offset + 4); - loc.City = ReadString(fs.Position); - } - } - - #endregion - - #region 公有成员函数 - - /// - /// 加载数据库文件到缓存 - /// - /// 数据库文件地址 - /// - public static void Init(string path) - { - if (fs != null) return; - var bt = File.ReadAllBytes(path); - fs = new MemoryStream(bt); - } - - /// - /// 根据IP获取区域名 - /// - /// 指定的IP - /// - public static stLocation GetLocation(string ip) - { - IPAddress ipAddress = null; - if (!IPAddress.TryParse(ip, out ipAddress)) throw new Exception("无效的IP地址。"); - - byte[] buff_local_ip = ipAddress.GetAddressBytes(); - - beginIndex = ReadLong(0, 4); - endIndex = ReadLong(4, 4); - - long offset = SearchIpIndex(buff_local_ip); - if (offset != -1) - { - GetAreaName(offset); - } - - loc.Contry = loc.Contry.Trim(); - loc.City = loc.City.Trim().Replace("CZ88.NET", ""); - - return loc; - } - - /// - /// 释放资源 - /// - public static void Dispose() - { - fs.Dispose(); - } - - #endregion - } */ -#endregion -} diff --git a/shadowsocks-csharp/Controller/FileManager.cs b/shadowsocks-csharp/Controller/FileManager.cs index fe11adfd..cc8f3bf9 100755 --- a/shadowsocks-csharp/Controller/FileManager.cs +++ b/shadowsocks-csharp/Controller/FileManager.cs @@ -29,9 +29,6 @@ namespace Shadowsocks.Controller public static void UncompressFile(string fileName, byte[] content) { - //some people use RamDisk, their Temp dir maybe disappeared, so we need check it first - if (!Directory.Exists(Path.GetDirectoryName(fileName)))Directory.CreateDirectory(Path.GetDirectoryName(fileName)); - FileStream destinationFile = File.Create(fileName); // Because the uncompressed size of the file is unknown, diff --git a/shadowsocks-csharp/Controller/I18N.cs b/shadowsocks-csharp/Controller/I18N.cs index 76c51749..c99d0f14 100755 --- a/shadowsocks-csharp/Controller/I18N.cs +++ b/shadowsocks-csharp/Controller/I18N.cs @@ -12,7 +12,7 @@ namespace Shadowsocks.Controller static I18N() { Strings = new Dictionary(); - + if (System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag.ToLowerInvariant().StartsWith("zh")) { string[] lines = Regex.Split(Resources.cn, "\r\n|\r|\n"); diff --git a/shadowsocks-csharp/Controller/Logging.cs b/shadowsocks-csharp/Controller/Logging.cs index 81f16dd8..d77a3fbe 100755 --- a/shadowsocks-csharp/Controller/Logging.cs +++ b/shadowsocks-csharp/Controller/Logging.cs @@ -1,4 +1,5 @@ -using System; +using Shadowsocks.Util; +using System; using System.Collections.Generic; using System.IO; using System.Net.Sockets; @@ -14,14 +15,14 @@ namespace Shadowsocks.Controller { try { - string temppath = Path.GetTempPath(); + string temppath = Utils.GetTempPath(); LogFile = Path.Combine(temppath, "shadowsocks.log"); FileStream fs = new FileStream(LogFile, FileMode.Append); StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs); sw.AutoFlush = true; Console.SetOut(sw); Console.SetError(sw); - + return true; } catch (IOException e) @@ -31,6 +32,14 @@ namespace Shadowsocks.Controller } } + public static void Debug(object o) + { + +#if DEBUG + Console.WriteLine(o); +#endif + } + public static void LogUsefulException(Exception e) { // just log useful exceptions, not all of them @@ -55,12 +64,14 @@ namespace Shadowsocks.Controller Console.WriteLine(e); } } + else if (e is ObjectDisposedException) + { + } else { Console.WriteLine(e); } } - } // Simply extended System.IO.StreamWriter for adding timestamp workaround diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs new file mode 100644 index 00000000..9c051b66 --- /dev/null +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.NetworkInformation; +using System.Threading; +using Shadowsocks.Model; +using System.Reflection; +using Shadowsocks.Util; + +namespace Shadowsocks.Controller +{ + class AvailabilityStatistics + { + private static readonly string StatisticsFilesName = "shadowsocks.availability.csv"; + private static readonly string Delimiter = ","; + private static readonly int Timeout = 500; + private static readonly int Repeat = 4; //repeat times every evaluation + private static readonly int Interval = 10 * 60 * 1000; //evaluate proxies every 15 minutes + private Timer timer = null; + private State state = null; + private List servers; + + public static string AvailabilityStatisticsFile; + + //static constructor to initialize every public static fields before refereced + static AvailabilityStatistics() + { + string temppath = Utils.GetTempPath(); + AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName); + } + + public bool Set(bool enabled) + { + try + { + if (enabled) + { + if (timer?.Change(0, Interval) == null) + { + state = new State(); + timer = new Timer(Evaluate, state, 0, Interval); + } + } + else + { + timer?.Dispose(); + } + return true; + } + catch (Exception e) + { + Logging.LogUsefulException(e); + return false; + } + } + + private void Evaluate(object obj) + { + Ping ping = new Ping(); + State state = (State) obj; + foreach (var server in servers) + { + Logging.Debug("eveluating " + server.FriendlyName()); + foreach (var _ in Enumerable.Range(0, Repeat)) + { + //TODO: do simple analyze of data to provide friendly message, like package loss. + string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + //ICMP echo. we can also set options and special bytes + //seems no need to use SendPingAsync + try + { + PingReply reply = ping.Send(server.server, Timeout); + state.data = new List>(); + state.data.Add(new KeyValuePair("Timestamp", timestamp)); + state.data.Add(new KeyValuePair("Server", server.FriendlyName())); + state.data.Add(new KeyValuePair("Status", reply.Status.ToString())); + state.data.Add(new KeyValuePair("RoundtripTime", reply.RoundtripTime.ToString())); + //state.data.Add(new KeyValuePair("data", reply.Buffer.ToString())); // The data of reply + Append(state.data); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + } + } + } + } + + private static void Append(List> data) + { + string dataLine = string.Join(Delimiter, data.Select(kv => kv.Value).ToArray()); + string[] lines; + if (!File.Exists(AvailabilityStatisticsFile)) + { + string headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray()); + lines = new string[] { headerLine, dataLine }; + } + else + { + lines = new string[] { dataLine }; + } + File.AppendAllLines(AvailabilityStatisticsFile, lines); + } + + internal void UpdateConfiguration(Configuration _config) + { + Set(_config.availabilityStatistics); + servers = _config.configs; + } + + private class State + { + public List> data = new List>(); + } + } +} diff --git a/shadowsocks-csharp/Controller/GfwListUpdater.cs b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs similarity index 97% rename from shadowsocks-csharp/Controller/GfwListUpdater.cs rename to shadowsocks-csharp/Controller/Service/GfwListUpdater.cs index 70720fb9..070c2b4e 100644 --- a/shadowsocks-csharp/Controller/GfwListUpdater.cs +++ b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs @@ -1,100 +1,100 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Net; -using System.IO; -using Shadowsocks.Properties; -using SimpleJson; -using Shadowsocks.Util; -using Shadowsocks.Model; - -namespace Shadowsocks.Controller -{ - public class GFWListUpdater - { - private const string GFWLIST_URL = "https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt"; - - private static string PAC_FILE = PACServer.PAC_FILE; - - private static string USER_RULE_FILE = PACServer.USER_RULE_FILE; - - public event EventHandler UpdateCompleted; - - public event ErrorEventHandler Error; - - public class ResultEventArgs : EventArgs - { - public bool Success; - - public ResultEventArgs(bool success) - { - this.Success = success; - } - } - - private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) - { - try - { - List lines = ParseResult(e.Result); - if (File.Exists(USER_RULE_FILE)) - { - string local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8); - string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - foreach(string rule in rules) - { - if (rule.StartsWith("!") || rule.StartsWith("[")) - continue; - lines.Add(rule); - } - } - string abpContent = Utils.UnGzip(Resources.abp_js); - abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); - if (File.Exists(PAC_FILE)) - { - string original = File.ReadAllText(PAC_FILE, Encoding.UTF8); - if (original == abpContent) - { - UpdateCompleted(this, new ResultEventArgs(false)); - return; - } - } - File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8); - if (UpdateCompleted != null) - { - UpdateCompleted(this, new ResultEventArgs(true)); - } - } - catch (Exception ex) - { - if (Error != null) - { - Error(this, new ErrorEventArgs(ex)); - } - } - } - - public void UpdatePACFromGFWList(Configuration config) - { - WebClient http = new WebClient(); - http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort); - http.DownloadStringCompleted += http_DownloadStringCompleted; - http.DownloadStringAsync(new Uri(GFWLIST_URL)); - } - - public List ParseResult(string response) - { - byte[] bytes = Convert.FromBase64String(response); - string content = Encoding.ASCII.GetString(bytes); - string[] lines = content.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - List valid_lines = new List(lines.Length); - foreach (string line in lines) - { - if (line.StartsWith("!") || line.StartsWith("[")) - continue; - valid_lines.Add(line); - } - return valid_lines; - } - } +using System; +using System.Collections.Generic; +using System.Text; +using System.Net; +using System.IO; +using Shadowsocks.Properties; +using SimpleJson; +using Shadowsocks.Util; +using Shadowsocks.Model; + +namespace Shadowsocks.Controller +{ + public class GFWListUpdater + { + private const string GFWLIST_URL = "https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt"; + + private static string PAC_FILE = PACServer.PAC_FILE; + + private static string USER_RULE_FILE = PACServer.USER_RULE_FILE; + + public event EventHandler UpdateCompleted; + + public event ErrorEventHandler Error; + + public class ResultEventArgs : EventArgs + { + public bool Success; + + public ResultEventArgs(bool success) + { + this.Success = success; + } + } + + private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) + { + try + { + List lines = ParseResult(e.Result); + if (File.Exists(USER_RULE_FILE)) + { + string local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8); + string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + foreach(string rule in rules) + { + if (rule.StartsWith("!") || rule.StartsWith("[")) + continue; + lines.Add(rule); + } + } + string abpContent = Utils.UnGzip(Resources.abp_js); + abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); + if (File.Exists(PAC_FILE)) + { + string original = File.ReadAllText(PAC_FILE, Encoding.UTF8); + if (original == abpContent) + { + UpdateCompleted(this, new ResultEventArgs(false)); + return; + } + } + File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8); + if (UpdateCompleted != null) + { + UpdateCompleted(this, new ResultEventArgs(true)); + } + } + catch (Exception ex) + { + if (Error != null) + { + Error(this, new ErrorEventArgs(ex)); + } + } + } + + public void UpdatePACFromGFWList(Configuration config) + { + WebClient http = new WebClient(); + http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort); + http.DownloadStringCompleted += http_DownloadStringCompleted; + http.DownloadStringAsync(new Uri(GFWLIST_URL)); + } + + public List ParseResult(string response) + { + byte[] bytes = Convert.FromBase64String(response); + string content = Encoding.ASCII.GetString(bytes); + string[] lines = content.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + List valid_lines = new List(lines.Length); + foreach (string line in lines) + { + if (line.StartsWith("!") || line.StartsWith("[")) + continue; + valid_lines.Add(line); + } + return valid_lines; + } + } } \ No newline at end of file diff --git a/shadowsocks-csharp/Controller/Listener.cs b/shadowsocks-csharp/Controller/Service/Listener.cs old mode 100755 new mode 100644 similarity index 60% rename from shadowsocks-csharp/Controller/Listener.cs rename to shadowsocks-csharp/Controller/Service/Listener.cs index 9943e536..72dd8e67 --- a/shadowsocks-csharp/Controller/Listener.cs +++ b/shadowsocks-csharp/Controller/Service/Listener.cs @@ -1,163 +1,216 @@ -using Shadowsocks.Model; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; -using System.Text; - -namespace Shadowsocks.Controller -{ - public class Listener - { - public interface Service - { - bool Handle(byte[] firstPacket, int length, Socket socket); - } - - Configuration _config; - bool _shareOverLAN; - Socket _socket; - IList _services; - - public Listener(IList services) - { - this._services = services; - } - - private bool CheckIfPortInUse(int port) - { - IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); - IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners(); - - foreach (IPEndPoint endPoint in ipEndPoints) - { - if (endPoint.Port == port) - { - return true; - } - } - return false; - } - - public void Start(Configuration config) - { - this._config = config; - this._shareOverLAN = config.shareOverLan; - - if (CheckIfPortInUse(_config.localPort)) - throw new Exception(I18N.GetString("Port already in use")); - - try - { - // Create a TCP/IP socket. - _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - IPEndPoint localEndPoint = null; - if (_shareOverLAN) - { - localEndPoint = new IPEndPoint(IPAddress.Any, _config.localPort); - } - else - { - localEndPoint = new IPEndPoint(IPAddress.Loopback, _config.localPort); - } - - // Bind the socket to the local endpoint and listen for incoming connections. - _socket.Bind(localEndPoint); - _socket.Listen(1024); - - - // Start an asynchronous socket to listen for connections. - Console.WriteLine("Shadowsocks started"); - _socket.BeginAccept( - new AsyncCallback(AcceptCallback), - _socket); - } - catch (SocketException) - { - _socket.Close(); - throw; - } - } - - public void Stop() - { - if (_socket != null) - { - _socket.Close(); - _socket = null; - } - } - - public void AcceptCallback(IAsyncResult ar) - { - Socket listener = (Socket)ar.AsyncState; - try - { - Socket conn = listener.EndAccept(ar); - - byte[] buf = new byte[4096]; - object[] state = new object[] { - conn, - buf - }; - - conn.BeginReceive(buf, 0, buf.Length, 0, - new AsyncCallback(ReceiveCallback), state); - } - catch (ObjectDisposedException) - { - } - catch (Exception e) - { - Console.WriteLine(e); - } - finally - { - try - { - listener.BeginAccept( - new AsyncCallback(AcceptCallback), - listener); - } - catch (ObjectDisposedException) - { - // do nothing - } - catch (Exception e) - { - Logging.LogUsefulException(e); - } - } - } - - - private void ReceiveCallback(IAsyncResult ar) - { - object[] state = (object[])ar.AsyncState; - - Socket conn = (Socket)state[0]; - byte[] buf = (byte[])state[1]; - try - { - int bytesRead = conn.EndReceive(ar); - foreach (Service service in _services) - { - if (service.Handle(buf, bytesRead, conn)) - { - return; - } - } - // no service found for this - // shouldn't happen - conn.Close(); - } - catch (Exception e) - { - Console.WriteLine(e); - conn.Close(); - } - } - } -} +using Shadowsocks.Model; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Text; + +namespace Shadowsocks.Controller +{ + public class Listener + { + public interface Service + { + bool Handle(byte[] firstPacket, int length, Socket socket, object state); + } + + public class UDPState + { + public byte[] buffer = new byte[4096]; + public EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + } + + Configuration _config; + bool _shareOverLAN; + Socket _tcpSocket; + Socket _udpSocket; + IList _services; + + public Listener(IList services) + { + this._services = services; + } + + private bool CheckIfPortInUse(int port) + { + IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); + IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners(); + + foreach (IPEndPoint endPoint in ipEndPoints) + { + if (endPoint.Port == port) + { + return true; + } + } + return false; + } + + public void Start(Configuration config) + { + this._config = config; + this._shareOverLAN = config.shareOverLan; + + if (CheckIfPortInUse(_config.localPort)) + throw new Exception(I18N.GetString("Port already in use")); + + try + { + // Create a TCP/IP socket. + _tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + _tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + _udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + IPEndPoint localEndPoint = null; + if (_shareOverLAN) + { + localEndPoint = new IPEndPoint(IPAddress.Any, _config.localPort); + } + else + { + localEndPoint = new IPEndPoint(IPAddress.Loopback, _config.localPort); + } + + // Bind the socket to the local endpoint and listen for incoming connections. + _tcpSocket.Bind(localEndPoint); + _udpSocket.Bind(localEndPoint); + _tcpSocket.Listen(1024); + + // Start an asynchronous socket to listen for connections. + Console.WriteLine("Shadowsocks started"); + _tcpSocket.BeginAccept( + new AsyncCallback(AcceptCallback), + _tcpSocket); + UDPState udpState = new UDPState(); + _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); + } + catch (SocketException) + { + _tcpSocket.Close(); + throw; + } + } + + public void Stop() + { + if (_tcpSocket != null) + { + _tcpSocket.Close(); + _tcpSocket = null; + } + if (_udpSocket != null) + { + _udpSocket.Close(); + _udpSocket = null; + } + } + + public void RecvFromCallback(IAsyncResult ar) + { + UDPState state = (UDPState)ar.AsyncState; + try + { + int bytesRead = _udpSocket.EndReceiveFrom(ar, ref state.remoteEndPoint); + foreach (Service service in _services) + { + if (service.Handle(state.buffer, bytesRead, _udpSocket, state)) + { + break; + } + } + } + catch (ObjectDisposedException) + { + } + catch (Exception) + { + } + finally + { + try + { + _udpSocket.BeginReceiveFrom(state.buffer, 0, state.buffer.Length, 0, ref state.remoteEndPoint, new AsyncCallback(RecvFromCallback), state); + } + catch (ObjectDisposedException) + { + // do nothing + } + catch (Exception) + { + } + } + } + + public void AcceptCallback(IAsyncResult ar) + { + Socket listener = (Socket)ar.AsyncState; + try + { + Socket conn = listener.EndAccept(ar); + + byte[] buf = new byte[4096]; + object[] state = new object[] { + conn, + buf + }; + + conn.BeginReceive(buf, 0, buf.Length, 0, + new AsyncCallback(ReceiveCallback), state); + } + catch (ObjectDisposedException) + { + } + catch (Exception e) + { + Console.WriteLine(e); + } + finally + { + try + { + listener.BeginAccept( + new AsyncCallback(AcceptCallback), + listener); + } + catch (ObjectDisposedException) + { + // do nothing + } + catch (Exception e) + { + Logging.LogUsefulException(e); + } + } + } + + private void ReceiveCallback(IAsyncResult ar) + { + object[] state = (object[])ar.AsyncState; + + Socket conn = (Socket)state[0]; + byte[] buf = (byte[])state[1]; + try + { + int bytesRead = conn.EndReceive(ar); + foreach (Service service in _services) + { + if (service.Handle(buf, bytesRead, conn, null)) + { + return; + } + } + // no service found for this + if (conn.ProtocolType == ProtocolType.Tcp) + { + conn.Close(); + } + } + catch (Exception e) + { + Console.WriteLine(e); + conn.Close(); + } + } + } +} diff --git a/shadowsocks-csharp/Controller/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs old mode 100755 new mode 100644 similarity index 96% rename from shadowsocks-csharp/Controller/PACServer.cs rename to shadowsocks-csharp/Controller/Service/PACServer.cs index e193d9b4..f7b94e45 --- a/shadowsocks-csharp/Controller/PACServer.cs +++ b/shadowsocks-csharp/Controller/Service/PACServer.cs @@ -1,207 +1,210 @@ -using Shadowsocks.Model; -using Shadowsocks.Properties; -using Shadowsocks.Util; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Net; -using System.Net.Sockets; -using System.Text; - -namespace Shadowsocks.Controller -{ - class PACServer : Listener.Service - { - public static string PAC_FILE = "pac.txt"; - - public static string USER_RULE_FILE = "user-rule.txt"; - - FileSystemWatcher watcher; - private Configuration _config; - - public event EventHandler PACFileChanged; - - public PACServer() - { - this.WatchPacFile(); - } - - public void UpdateConfiguration(Configuration config) - { - this._config = config; - } - - public bool Handle(byte[] firstPacket, int length, Socket socket) - { - try - { - string request = Encoding.UTF8.GetString(firstPacket, 0, length); - string[] lines = request.Split('\r', '\n'); - bool hostMatch = false, pathMatch = false, useSocks = false; - foreach (string line in lines) - { - string[] kv = line.Split(new char[]{':'}, 2); - if (kv.Length == 2) - { - if (kv[0] == "Host") - { - if (kv[1].Trim() == ((IPEndPoint)socket.LocalEndPoint).ToString()) - { - hostMatch = true; - } - } - else if (kv[0] == "User-Agent") - { - // we need to drop connections when changing servers - /* if (kv[1].IndexOf("Chrome") >= 0) - { - useSocks = true; - } */ - } - } - else if (kv.Length == 1) - { - if (line.IndexOf("pac") >= 0) - { - pathMatch = true; - } - } - } - if (hostMatch && pathMatch) - { - SendResponse(firstPacket, length, socket, useSocks); - return true; - } - return false; - } - catch (ArgumentException) - { - return false; - } - } - - - public string TouchPACFile() - { - if (File.Exists(PAC_FILE)) - { - return PAC_FILE; - } - else - { - FileManager.UncompressFile(PAC_FILE, Resources.proxy_pac_txt); - return PAC_FILE; - } - } - - internal string TouchUserRuleFile() - { - if (File.Exists(USER_RULE_FILE)) - { - return USER_RULE_FILE; - } - else - { - File.WriteAllText(USER_RULE_FILE, Resources.user_rule); - return USER_RULE_FILE; - } - } - - private string GetPACContent() - { - if (File.Exists(PAC_FILE)) - { - return File.ReadAllText(PAC_FILE, Encoding.UTF8); - } - else - { - return Utils.UnGzip(Resources.proxy_pac_txt); - } - } - - public void SendResponse(byte[] firstPacket, int length, Socket socket, bool useSocks) - { - try - { - string pac = GetPACContent(); - - IPEndPoint localEndPoint = (IPEndPoint)socket.LocalEndPoint; - - string proxy = GetPACAddress(firstPacket, length, localEndPoint, useSocks); - - pac = pac.Replace("__PROXY__", proxy); - - string text = String.Format(@"HTTP/1.1 200 OK -Server: Shadowsocks -Content-Type: application/x-ns-proxy-autoconfig -Content-Length: {0} -Connection: Close - -", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac; - byte[] response = System.Text.Encoding.UTF8.GetBytes(text); - socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); - Util.Utils.ReleaseMemory(); - } - catch (Exception e) - { - Console.WriteLine(e); - socket.Close(); - } - } - - private void SendCallback(IAsyncResult ar) - { - Socket conn = (Socket)ar.AsyncState; - try - { - conn.Shutdown(SocketShutdown.Send); - } - catch - { } - } - - private void WatchPacFile() - { - if (watcher != null) - { - watcher.Dispose(); - } - watcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); - watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; - watcher.Filter = PAC_FILE; - watcher.Changed += Watcher_Changed; - watcher.Created += Watcher_Changed; - watcher.Deleted += Watcher_Changed; - watcher.Renamed += Watcher_Changed; - watcher.EnableRaisingEvents = true; - } - - private void Watcher_Changed(object sender, FileSystemEventArgs e) - { - if (PACFileChanged != null) - { - PACFileChanged(this, new EventArgs()); - } - } - - private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks) - { - //try - //{ - // string requestString = Encoding.UTF8.GetString(requestBuf); - // if (requestString.IndexOf("AppleWebKit") >= 0) - // { - // string address = "" + localEndPoint.Address + ":" + config.GetCurrentServer().local_port; - // proxy = "SOCKS5 " + address + "; SOCKS " + address + ";"; - // } - //} - //catch (Exception e) - //{ - // Console.WriteLine(e); - //} - return (useSocks ? "SOCKS5 " : "PROXY ") + localEndPoint.Address + ":" + this._config.localPort + ";"; - } - } -} +using Shadowsocks.Model; +using Shadowsocks.Properties; +using Shadowsocks.Util; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace Shadowsocks.Controller +{ + class PACServer : Listener.Service + { + public static string PAC_FILE = "pac.txt"; + + public static string USER_RULE_FILE = "user-rule.txt"; + + FileSystemWatcher watcher; + private Configuration _config; + + public event EventHandler PACFileChanged; + + public PACServer() + { + this.WatchPacFile(); + } + + public void UpdateConfiguration(Configuration config) + { + this._config = config; + } + + public bool Handle(byte[] firstPacket, int length, Socket socket, object state) + { + if (socket.ProtocolType != ProtocolType.Tcp) + { + return false; + } + try + { + string request = Encoding.UTF8.GetString(firstPacket, 0, length); + string[] lines = request.Split('\r', '\n'); + bool hostMatch = false, pathMatch = false, useSocks = false; + foreach (string line in lines) + { + string[] kv = line.Split(new char[]{':'}, 2); + if (kv.Length == 2) + { + if (kv[0] == "Host") + { + if (kv[1].Trim() == ((IPEndPoint)socket.LocalEndPoint).ToString()) + { + hostMatch = true; + } + } + else if (kv[0] == "User-Agent") + { + // we need to drop connections when changing servers + /* if (kv[1].IndexOf("Chrome") >= 0) + { + useSocks = true; + } */ + } + } + else if (kv.Length == 1) + { + if (line.IndexOf("pac") >= 0) + { + pathMatch = true; + } + } + } + if (hostMatch && pathMatch) + { + SendResponse(firstPacket, length, socket, useSocks); + return true; + } + return false; + } + catch (ArgumentException) + { + return false; + } + } + + public string TouchPACFile() + { + if (File.Exists(PAC_FILE)) + { + return PAC_FILE; + } + else + { + FileManager.UncompressFile(PAC_FILE, Resources.proxy_pac_txt); + return PAC_FILE; + } + } + + internal string TouchUserRuleFile() + { + if (File.Exists(USER_RULE_FILE)) + { + return USER_RULE_FILE; + } + else + { + File.WriteAllText(USER_RULE_FILE, Resources.user_rule); + return USER_RULE_FILE; + } + } + + private string GetPACContent() + { + if (File.Exists(PAC_FILE)) + { + return File.ReadAllText(PAC_FILE, Encoding.UTF8); + } + else + { + return Utils.UnGzip(Resources.proxy_pac_txt); + } + } + + public void SendResponse(byte[] firstPacket, int length, Socket socket, bool useSocks) + { + try + { + string pac = GetPACContent(); + + IPEndPoint localEndPoint = (IPEndPoint)socket.LocalEndPoint; + + string proxy = GetPACAddress(firstPacket, length, localEndPoint, useSocks); + + pac = pac.Replace("__PROXY__", proxy); + + string text = String.Format(@"HTTP/1.1 200 OK +Server: Shadowsocks +Content-Type: application/x-ns-proxy-autoconfig +Content-Length: {0} +Connection: Close + +", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac; + byte[] response = System.Text.Encoding.UTF8.GetBytes(text); + socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); + Util.Utils.ReleaseMemory(true); + } + catch (Exception e) + { + Console.WriteLine(e); + socket.Close(); + } + } + + private void SendCallback(IAsyncResult ar) + { + Socket conn = (Socket)ar.AsyncState; + try + { + conn.Shutdown(SocketShutdown.Send); + } + catch + { } + } + + private void WatchPacFile() + { + if (watcher != null) + { + watcher.Dispose(); + } + watcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); + watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; + watcher.Filter = PAC_FILE; + watcher.Changed += Watcher_Changed; + watcher.Created += Watcher_Changed; + watcher.Deleted += Watcher_Changed; + watcher.Renamed += Watcher_Changed; + watcher.EnableRaisingEvents = true; + } + + private void Watcher_Changed(object sender, FileSystemEventArgs e) + { + if (PACFileChanged != null) + { + PACFileChanged(this, new EventArgs()); + } + } + + private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks) + { + //try + //{ + // string requestString = Encoding.UTF8.GetString(requestBuf); + // if (requestString.IndexOf("AppleWebKit") >= 0) + // { + // string address = "" + localEndPoint.Address + ":" + config.GetCurrentServer().local_port; + // proxy = "SOCKS5 " + address + "; SOCKS " + address + ";"; + // } + //} + //catch (Exception e) + //{ + // Console.WriteLine(e); + //} + return (useSocks ? "SOCKS5 " : "PROXY ") + localEndPoint.Address + ":" + this._config.localPort + ";"; + } + } +} diff --git a/shadowsocks-csharp/Controller/PolipoRunner.cs b/shadowsocks-csharp/Controller/Service/PolipoRunner.cs old mode 100755 new mode 100644 similarity index 54% rename from shadowsocks-csharp/Controller/PolipoRunner.cs rename to shadowsocks-csharp/Controller/Service/PolipoRunner.cs index ae0b46f1..58a4f31a --- a/shadowsocks-csharp/Controller/PolipoRunner.cs +++ b/shadowsocks-csharp/Controller/Service/PolipoRunner.cs @@ -1,126 +1,177 @@ -using Shadowsocks.Model; -using Shadowsocks.Properties; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Text; -using System.Net.NetworkInformation; -using System.Net; - -namespace Shadowsocks.Controller -{ - class PolipoRunner - { - private Process _process; - private static string temppath; - private int _runningPort; - - static PolipoRunner() - { - temppath = Path.GetTempPath(); - try - { - FileManager.UncompressFile(temppath + "/ss_polipo.exe", Resources.polipo_exe); - } - catch (IOException e) - { - Logging.LogUsefulException(e); - } - } - - public int RunningPort - { - get - { - return _runningPort; - } - } - - public void Start(Configuration configuration) - { - Server server = configuration.GetCurrentServer(); - if (_process == null) - { - Process[] existingPolipo = Process.GetProcessesByName("ss_polipo"); - foreach (Process p in existingPolipo) - { - try - { - p.Kill(); - p.WaitForExit(); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - } - } - string polipoConfig = Resources.polipo_config; - _runningPort = this.GetFreePort(); - polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString()); - polipoConfig = polipoConfig.Replace("__POLIPO_BIND_PORT__", _runningPort.ToString()); - polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); - FileManager.ByteArrayToFile(temppath + "/polipo.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig)); - - _process = new Process(); - // Configure the process using the StartInfo properties. - _process.StartInfo.FileName = temppath + "/ss_polipo.exe"; - _process.StartInfo.Arguments = "-c \"" + temppath + "/polipo.conf\""; - _process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - _process.StartInfo.UseShellExecute = true; - _process.StartInfo.CreateNoWindow = true; - //_process.StartInfo.RedirectStandardOutput = true; - //_process.StartInfo.RedirectStandardError = true; - _process.Start(); - } - } - - public void Stop() - { - if (_process != null) - { - try - { - _process.Kill(); - _process.WaitForExit(); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - } - _process = null; - } - } - - private int GetFreePort() - { - int defaultPort = 8123; - try - { - IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties(); - IPEndPoint[] tcpEndPoints = properties.GetActiveTcpListeners(); - - List usedPorts = new List(); - foreach (IPEndPoint endPoint in IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()) - { - usedPorts.Add(endPoint.Port); - } - for (int port = defaultPort; port <= 65535; port++) - { - if (!usedPorts.Contains(port)) - { - return port; - } - } - } - catch (Exception e) - { - // in case access denied - Logging.LogUsefulException(e); - return defaultPort; - } - throw new Exception("No free port found."); - } - } -} +using Shadowsocks.Model; +using Shadowsocks.Properties; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Net.NetworkInformation; +using System.Net; +using System.Runtime.InteropServices; +using Shadowsocks.Util; + +namespace Shadowsocks.Controller +{ + class PolipoRunner + { + private Process _process; + private static string temppath; + private int _runningPort; + + static PolipoRunner() + { + temppath = Utils.GetTempPath(); + try + { + FileManager.UncompressFile(temppath + "/ss_privoxy.exe", Resources.privoxy_exe); + FileManager.UncompressFile(temppath + "/mgwz.dll", Resources.mgwz_dll); + } + catch (IOException e) + { + Logging.LogUsefulException(e); + } + } + + public int RunningPort + { + get + { + return _runningPort; + } + } + + public void Start(Configuration configuration) + { + Server server = configuration.GetCurrentServer(); + if (_process == null) + { + Process[] existingPolipo = Process.GetProcessesByName("ss_privoxy"); + foreach (Process p in existingPolipo) + { + try + { + p.Kill(); + p.WaitForExit(); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + } + string polipoConfig = Resources.privoxy_conf; + _runningPort = this.GetFreePort(); + polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString()); + polipoConfig = polipoConfig.Replace("__POLIPO_BIND_PORT__", _runningPort.ToString()); + polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); + FileManager.ByteArrayToFile(temppath + "/privoxy.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig)); + + if (!(temppath.EndsWith("\\") || temppath.EndsWith("/"))) { + temppath = temppath + "\\"; + } + _process = new Process(); + // Configure the process using the StartInfo properties. + _process.StartInfo.FileName = temppath + "ss_privoxy.exe"; + _process.StartInfo.Arguments = " \"" + temppath + "privoxy.conf\""; + _process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + _process.StartInfo.UseShellExecute = true; + _process.StartInfo.CreateNoWindow = true; + //_process.StartInfo.RedirectStandardOutput = true; + //_process.StartInfo.RedirectStandardError = true; + _process.Start(); + } + RefreshTrayArea(); + } + + public void Stop() + { + if (_process != null) + { + try + { + _process.Kill(); + _process.WaitForExit(); + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + _process = null; + } + RefreshTrayArea(); + } + + private int GetFreePort() + { + int defaultPort = 8123; + try + { + IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties(); + IPEndPoint[] tcpEndPoints = properties.GetActiveTcpListeners(); + + List usedPorts = new List(); + foreach (IPEndPoint endPoint in IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()) + { + usedPorts.Add(endPoint.Port); + } + for (int port = defaultPort; port <= 65535; port++) + { + if (!usedPorts.Contains(port)) + { + return port; + } + } + } + catch (Exception e) + { + // in case access denied + Logging.LogUsefulException(e); + return defaultPort; + } + throw new Exception("No free port found."); + } + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + } + [DllImport("user32.dll")] + public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + [DllImport("user32.dll")] + public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); + [DllImport("user32.dll")] + public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); + [DllImport("user32.dll")] + public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam); + + public void RefreshTrayArea() + { + IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null); + IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null); + IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null); + IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area"); + if (notificationAreaHandle == IntPtr.Zero) + { + notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area"); + IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null); + IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area"); + RefreshTrayArea(overflowNotificationAreaHandle); + } + RefreshTrayArea(notificationAreaHandle); + } + + private static void RefreshTrayArea(IntPtr windowHandle) + { + const uint wmMousemove = 0x0200; + RECT rect; + GetClientRect(windowHandle, out rect); + for (var x = 0; x < rect.right; x += 5) + for (var y = 0; y < rect.bottom; y += 5) + SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x); + } + } +} diff --git a/shadowsocks-csharp/Controller/PortForwarder.cs b/shadowsocks-csharp/Controller/Service/PortForwarder.cs old mode 100755 new mode 100644 similarity index 96% rename from shadowsocks-csharp/Controller/PortForwarder.cs rename to shadowsocks-csharp/Controller/Service/PortForwarder.cs index 1057143f..3cc3299c --- a/shadowsocks-csharp/Controller/PortForwarder.cs +++ b/shadowsocks-csharp/Controller/Service/PortForwarder.cs @@ -1,264 +1,267 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Sockets; -using System.Text; - -namespace Shadowsocks.Controller -{ - class PortForwarder : Listener.Service - { - int _targetPort; - - public PortForwarder(int targetPort) - { - this._targetPort = targetPort; - } - - public bool Handle(byte[] firstPacket, int length, Socket socket) - { - new Handler().Start(firstPacket, length, socket, this._targetPort); - return true; - } - - class Handler - { - private byte[] _firstPacket; - private int _firstPacketLength; - private Socket _local; - private Socket _remote; - private bool _closed = false; - private bool _localShutdown = false; - private bool _remoteShutdown = false; - public const int RecvSize = 16384; - // remote receive buffer - private byte[] remoteRecvBuffer = new byte[RecvSize]; - // connection receive buffer - private byte[] connetionRecvBuffer = new byte[RecvSize]; - - public void Start(byte[] firstPacket, int length, Socket socket, int targetPort) - { - this._firstPacket = firstPacket; - this._firstPacketLength = length; - this._local = socket; - try - { - // TODO async resolving - IPAddress ipAddress; - bool parsed = IPAddress.TryParse("127.0.0.1", out ipAddress); - IPEndPoint remoteEP = new IPEndPoint(ipAddress, targetPort); - - - _remote = new Socket(ipAddress.AddressFamily, - SocketType.Stream, ProtocolType.Tcp); - _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); - - // Connect to the remote endpoint. - _remote.BeginConnect(remoteEP, - new AsyncCallback(ConnectCallback), null); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void ConnectCallback(IAsyncResult ar) - { - if (_closed) - { - return; - } - try - { - _remote.EndConnect(ar); - HandshakeReceive(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void HandshakeReceive() - { - if (_closed) - { - return; - } - try - { - _remote.BeginSend(_firstPacket, 0, _firstPacketLength, 0, new AsyncCallback(StartPipe), null); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - - private void StartPipe(IAsyncResult ar) - { - if (_closed) - { - return; - } - try - { - _remote.EndSend(ar); - _remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeRemoteReceiveCallback), null); - _local.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeConnectionReceiveCallback), null); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void PipeRemoteReceiveCallback(IAsyncResult ar) - { - if (_closed) - { - return; - } - try - { - int bytesRead = _remote.EndReceive(ar); - - if (bytesRead > 0) - { - _local.BeginSend(remoteRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeConnectionSendCallback), null); - } - else - { - //Console.WriteLine("bytesRead: " + bytesRead.ToString()); - _local.Shutdown(SocketShutdown.Send); - _localShutdown = true; - CheckClose(); - } - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void PipeConnectionReceiveCallback(IAsyncResult ar) - { - if (_closed) - { - return; - } - try - { - int bytesRead = _local.EndReceive(ar); - - if (bytesRead > 0) - { - _remote.BeginSend(connetionRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeRemoteSendCallback), null); - } - else - { - _remote.Shutdown(SocketShutdown.Send); - _remoteShutdown = true; - CheckClose(); - } - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void PipeRemoteSendCallback(IAsyncResult ar) - { - if (_closed) - { - return; - } - try - { - _remote.EndSend(ar); - _local.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeConnectionReceiveCallback), null); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void PipeConnectionSendCallback(IAsyncResult ar) - { - if (_closed) - { - return; - } - try - { - _local.EndSend(ar); - _remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeRemoteReceiveCallback), null); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void CheckClose() - { - if (_localShutdown && _remoteShutdown) - { - this.Close(); - } - } - - public void Close() - { - lock (this) - { - if (_closed) - { - return; - } - _closed = true; - } - if (_local != null) - { - try - { - _local.Shutdown(SocketShutdown.Both); - _local.Close(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - } - } - if (_remote != null) - { - try - { - _remote.Shutdown(SocketShutdown.Both); - _remote.Close(); - } - catch (SocketException e) - { - Logging.LogUsefulException(e); - } - } - } - } - } -} +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace Shadowsocks.Controller +{ + class PortForwarder : Listener.Service + { + int _targetPort; + + public PortForwarder(int targetPort) + { + this._targetPort = targetPort; + } + + public bool Handle(byte[] firstPacket, int length, Socket socket, object state) + { + if (socket.ProtocolType != ProtocolType.Tcp) + { + return false; + } + new Handler().Start(firstPacket, length, socket, this._targetPort); + return true; + } + + class Handler + { + private byte[] _firstPacket; + private int _firstPacketLength; + private Socket _local; + private Socket _remote; + private bool _closed = false; + private bool _localShutdown = false; + private bool _remoteShutdown = false; + public const int RecvSize = 16384; + // remote receive buffer + private byte[] remoteRecvBuffer = new byte[RecvSize]; + // connection receive buffer + private byte[] connetionRecvBuffer = new byte[RecvSize]; + + public void Start(byte[] firstPacket, int length, Socket socket, int targetPort) + { + this._firstPacket = firstPacket; + this._firstPacketLength = length; + this._local = socket; + try + { + // TODO async resolving + IPAddress ipAddress; + bool parsed = IPAddress.TryParse("127.0.0.1", out ipAddress); + IPEndPoint remoteEP = new IPEndPoint(ipAddress, targetPort); + + + _remote = new Socket(ipAddress.AddressFamily, + SocketType.Stream, ProtocolType.Tcp); + _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + + // Connect to the remote endpoint. + _remote.BeginConnect(remoteEP, + new AsyncCallback(ConnectCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void ConnectCallback(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + _remote.EndConnect(ar); + HandshakeReceive(); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void HandshakeReceive() + { + if (_closed) + { + return; + } + try + { + _remote.BeginSend(_firstPacket, 0, _firstPacketLength, 0, new AsyncCallback(StartPipe), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void StartPipe(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + _remote.EndSend(ar); + _remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeRemoteReceiveCallback), null); + _local.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeConnectionReceiveCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeRemoteReceiveCallback(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + int bytesRead = _remote.EndReceive(ar); + + if (bytesRead > 0) + { + _local.BeginSend(remoteRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeConnectionSendCallback), null); + } + else + { + //Console.WriteLine("bytesRead: " + bytesRead.ToString()); + _local.Shutdown(SocketShutdown.Send); + _localShutdown = true; + CheckClose(); + } + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeConnectionReceiveCallback(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + int bytesRead = _local.EndReceive(ar); + + if (bytesRead > 0) + { + _remote.BeginSend(connetionRecvBuffer, 0, bytesRead, 0, new AsyncCallback(PipeRemoteSendCallback), null); + } + else + { + _remote.Shutdown(SocketShutdown.Send); + _remoteShutdown = true; + CheckClose(); + } + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeRemoteSendCallback(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + _remote.EndSend(ar); + _local.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeConnectionReceiveCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeConnectionSendCallback(IAsyncResult ar) + { + if (_closed) + { + return; + } + try + { + _local.EndSend(ar); + _remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeRemoteReceiveCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void CheckClose() + { + if (_localShutdown && _remoteShutdown) + { + this.Close(); + } + } + + public void Close() + { + lock (this) + { + if (_closed) + { + return; + } + _closed = true; + } + if (_local != null) + { + try + { + _local.Shutdown(SocketShutdown.Both); + _local.Close(); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + } + } + if (_remote != null) + { + try + { + _remote.Shutdown(SocketShutdown.Both); + _remote.Close(); + } + catch (SocketException e) + { + Logging.LogUsefulException(e); + } + } + } + } + } +} diff --git a/shadowsocks-csharp/Controller/Local.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs old mode 100755 new mode 100644 similarity index 54% rename from shadowsocks-csharp/Controller/Local.cs rename to shadowsocks-csharp/Controller/Service/TCPRelay.cs index 5489ea52..af2047b3 --- a/shadowsocks-csharp/Controller/Local.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -1,399 +1,650 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Net.Sockets; -using System.Net; -using Shadowsocks.Encryption; -using Shadowsocks.Model; - -namespace Shadowsocks.Controller -{ - - class Local : Listener.Service - { - private Configuration _config; - public Local(Configuration config) - { - this._config = config; - } - - public bool Handle(byte[] firstPacket, int length, Socket socket) - { - if (length < 2 || firstPacket[0] != 5) - { - return false; - } - socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); - Handler handler = new Handler(); - handler.connection = socket; - Server server = _config.GetCurrentServer(); - handler.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password); - handler.server = server; - - handler.Start(firstPacket, length); - return true; - } - } - - class Handler - { - //public Encryptor encryptor; - public IEncryptor encryptor; - public Server server; - // Client socket. - public Socket remote; - public Socket connection; - - private byte[] _firstPacket; - private int _firstPacketLength; - // Size of receive buffer. - public const int RecvSize = 16384; - public const int BufferSize = RecvSize + 32; - // remote receive buffer - private byte[] remoteRecvBuffer = new byte[RecvSize]; - // remote send buffer - private byte[] remoteSendBuffer = new byte[BufferSize]; - // connection receive buffer - private byte[] connetionRecvBuffer = new byte[RecvSize]; - // connection send buffer - private byte[] connetionSendBuffer = new byte[BufferSize]; - // Received data string. - - private bool connectionShutdown = false; - private bool remoteShutdown = false; - private bool closed = false; - - private object encryptionLock = new object(); - private object decryptionLock = new object(); - - public void Start(byte[] firstPacket, int length) - { - this._firstPacket = firstPacket; - this._firstPacketLength = length; - try - { - // TODO async resolving - IPAddress ipAddress; - bool parsed = IPAddress.TryParse(server.server, out ipAddress); - if (!parsed) - { - IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server); - ipAddress = ipHostInfo.AddressList[0]; - } - IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port); - - - remote = new Socket(ipAddress.AddressFamily, - SocketType.Stream, ProtocolType.Tcp); - remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); - - // Connect to the remote endpoint. - remote.BeginConnect(remoteEP, - new AsyncCallback(ConnectCallback), null); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void CheckClose() - { - if (connectionShutdown && remoteShutdown) - { - this.Close(); - } - } - - public void Close() - { - lock (this) - { - if (closed) - { - return; - } - closed = true; - } - if (connection != null) - { - try - { - connection.Shutdown(SocketShutdown.Both); - connection.Close(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - } - } - if (remote != null) - { - try - { - remote.Shutdown(SocketShutdown.Both); - remote.Close(); - } - catch (SocketException e) - { - Logging.LogUsefulException(e); - } - } - lock (encryptionLock) - { - lock (decryptionLock) - { - ((IDisposable)encryptor).Dispose(); - } - } - } - - private void ConnectCallback(IAsyncResult ar) - { - if (closed) - { - return; - } - try - { - // Complete the connection. - remote.EndConnect(ar); - - //Console.WriteLine("Socket connected to {0}", - // remote.RemoteEndPoint.ToString()); - - HandshakeReceive(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void HandshakeReceive() - { - if (closed) - { - return; - } - try - { - int bytesRead = _firstPacketLength; - - if (bytesRead > 1) - { - byte[] response = { 5, 0 }; - if (_firstPacket[0] != 5) - { - // reject socks 4 - response = new byte[] { 0, 91 }; - Console.WriteLine("socks 5 protocol error"); - } - connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); - } - else - { - this.Close(); - } - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void HandshakeSendCallback(IAsyncResult ar) - { - if (closed) - { - return; - } - try - { - connection.EndSend(ar); - - // +----+-----+-------+------+----------+----------+ - // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | - // +----+-----+-------+------+----------+----------+ - // | 1 | 1 | X'00' | 1 | Variable | 2 | - // +----+-----+-------+------+----------+----------+ - // Skip first 3 bytes - // TODO validate - connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, - new AsyncCallback(handshakeReceive2Callback), null); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void handshakeReceive2Callback(IAsyncResult ar) - { - if (closed) - { - return; - } - try - { - int bytesRead = connection.EndReceive(ar); - - if (bytesRead > 0) - { - byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; - connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(StartPipe), null); - } - else - { - Console.WriteLine("failed to recv data in handshakeReceive2Callback"); - this.Close(); - } - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - - private void StartPipe(IAsyncResult ar) - { - if (closed) - { - return; - } - try - { - connection.EndReceive(ar); - remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeRemoteReceiveCallback), null); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeConnectionReceiveCallback), null); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void PipeRemoteReceiveCallback(IAsyncResult ar) - { - if (closed) - { - return; - } - try - { - int bytesRead = remote.EndReceive(ar); - - if (bytesRead > 0) - { - int bytesToSend; - lock (decryptionLock) - { - if (closed) - { - return; - } - encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); - } - connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); - } - else - { - //Console.WriteLine("bytesRead: " + bytesRead.ToString()); - connection.Shutdown(SocketShutdown.Send); - connectionShutdown = true; - CheckClose(); - } - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void PipeConnectionReceiveCallback(IAsyncResult ar) - { - if (closed) - { - return; - } - try - { - int bytesRead = connection.EndReceive(ar); - - if (bytesRead > 0) - { - int bytesToSend; - lock (encryptionLock) - { - if (closed) - { - return; - } - encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); - } - remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); - } - else - { - remote.Shutdown(SocketShutdown.Send); - remoteShutdown = true; - CheckClose(); - } - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void PipeRemoteSendCallback(IAsyncResult ar) - { - if (closed) - { - return; - } - try - { - remote.EndSend(ar); - connection.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeConnectionReceiveCallback), null); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - - private void PipeConnectionSendCallback(IAsyncResult ar) - { - if (closed) - { - return; - } - try - { - connection.EndSend(ar); - remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeRemoteReceiveCallback), null); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - this.Close(); - } - } - } - -} +using System; +using System.Collections.Generic; +using System.Text; +using System.Net.Sockets; +using System.Net; +using Shadowsocks.Encryption; +using Shadowsocks.Model; +using Shadowsocks.Controller.Strategy; +using System.Timers; + +namespace Shadowsocks.Controller +{ + + class TCPRelay : Listener.Service + { + private ShadowsocksController _controller; + private DateTime _lastSweepTime; + + public ISet Handlers + { + get; set; + } + + public TCPRelay(ShadowsocksController controller) + { + this._controller = controller; + this.Handlers = new HashSet(); + this._lastSweepTime = DateTime.Now; + } + + public bool Handle(byte[] firstPacket, int length, Socket socket, object state) + { + if (socket.ProtocolType != ProtocolType.Tcp) + { + return false; + } + if (length < 2 || firstPacket[0] != 5) + { + return false; + } + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + Handler handler = new Handler(); + handler.connection = socket; + handler.controller = _controller; + handler.relay = this; + + handler.Start(firstPacket, length); + IList handlersToClose = new List(); + lock (this.Handlers) + { + this.Handlers.Add(handler); + Logging.Debug($"connections: {Handlers.Count}"); + DateTime now = DateTime.Now; + if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) + { + _lastSweepTime = now; + foreach (Handler handler1 in this.Handlers) + { + if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) + { + handlersToClose.Add(handler1); + } + } + } + } + foreach (Handler handler1 in handlersToClose) + { + Logging.Debug("Closing timed out connection"); + handler1.Close(); + } + return true; + } + } + + class Handler + { + //public Encryptor encryptor; + public IEncryptor encryptor; + public Server server; + // Client socket. + public Socket remote; + public Socket connection; + public ShadowsocksController controller; + public TCPRelay relay; + + public DateTime lastActivity; + + private int retryCount = 0; + private bool connected; + + private byte command; + private byte[] _firstPacket; + private int _firstPacketLength; + // Size of receive buffer. + public const int RecvSize = 8192; + public const int BufferSize = RecvSize + 32; + + private int totalRead = 0; + private int totalWrite = 0; + + // remote receive buffer + private byte[] remoteRecvBuffer = new byte[RecvSize]; + // remote send buffer + private byte[] remoteSendBuffer = new byte[BufferSize]; + // connection receive buffer + private byte[] connetionRecvBuffer = new byte[RecvSize]; + // connection send buffer + private byte[] connetionSendBuffer = new byte[BufferSize]; + // Received data string. + + private bool connectionShutdown = false; + private bool remoteShutdown = false; + private bool closed = false; + + private object encryptionLock = new object(); + private object decryptionLock = new object(); + + private DateTime _startConnectTime; + + public void CreateRemote() + { + Server server = controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)connection.RemoteEndPoint); + if (server == null || server.server == "") + { + throw new ArgumentException("No server configured"); + } + this.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password); + this.server = server; + } + + public void Start(byte[] firstPacket, int length) + { + this._firstPacket = firstPacket; + this._firstPacketLength = length; + this.HandshakeReceive(); + this.lastActivity = DateTime.Now; + } + + private void CheckClose() + { + if (connectionShutdown && remoteShutdown) + { + this.Close(); + } + } + + public void Close() + { + lock (relay.Handlers) + { + Logging.Debug($"connections: {relay.Handlers.Count}"); + relay.Handlers.Remove(this); + } + lock (this) + { + if (closed) + { + return; + } + closed = true; + } + if (connection != null) + { + try + { + connection.Shutdown(SocketShutdown.Both); + connection.Close(); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + } + } + if (remote != null) + { + try + { + remote.Shutdown(SocketShutdown.Both); + remote.Close(); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + } + } + lock (encryptionLock) + { + lock (decryptionLock) + { + if (encryptor != null) + { + ((IDisposable)encryptor).Dispose(); + } + } + } + } + + private void HandshakeReceive() + { + if (closed) + { + return; + } + try + { + int bytesRead = _firstPacketLength; + + if (bytesRead > 1) + { + byte[] response = { 5, 0 }; + if (_firstPacket[0] != 5) + { + // reject socks 4 + response = new byte[] { 0, 91 }; + Console.WriteLine("socks 5 protocol error"); + } + connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); + } + else + { + this.Close(); + } + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void HandshakeSendCallback(IAsyncResult ar) + { + if (closed) + { + return; + } + try + { + connection.EndSend(ar); + + // +----+-----+-------+------+----------+----------+ + // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + // +----+-----+-------+------+----------+----------+ + // | 1 | 1 | X'00' | 1 | Variable | 2 | + // +----+-----+-------+------+----------+----------+ + // Skip first 3 bytes + // TODO validate + connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, + new AsyncCallback(handshakeReceive2Callback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void handshakeReceive2Callback(IAsyncResult ar) + { + if (closed) + { + return; + } + try + { + int bytesRead = connection.EndReceive(ar); + + if (bytesRead >= 3) + { + command = connetionRecvBuffer[1]; + if (command == 1) + { + byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; + connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null); + } + else if (command == 3) + { + HandleUDPAssociate(); + } + } + else + { + Console.WriteLine("failed to recv data in handshakeReceive2Callback"); + this.Close(); + } + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void HandleUDPAssociate() + { + IPEndPoint endPoint = (IPEndPoint)connection.LocalEndPoint; + byte[] address = endPoint.Address.GetAddressBytes(); + int port = endPoint.Port; + byte[] response = new byte[4 + address.Length + 2]; + response[0] = 5; + if (endPoint.AddressFamily == AddressFamily.InterNetwork) + { + response[3] = 1; + } + else if (endPoint.AddressFamily == AddressFamily.InterNetworkV6) + { + response[3] = 4; + } + address.CopyTo(response, 4); + response[response.Length - 1] = (byte)(port & 0xFF); + response[response.Length - 2] = (byte)((port >> 8) & 0xFF); + connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ReadAll), true); + } + + private void ReadAll(IAsyncResult ar) + { + if (closed) + { + return; + } + try + { + if (ar.AsyncState != null) + { + connection.EndSend(ar); + connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(ReadAll), null); + } + else + { + int bytesRead = connection.EndReceive(ar); + if (bytesRead > 0) + { + connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(ReadAll), null); + } + else + { + this.Close(); + } + } + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void ResponseCallback(IAsyncResult ar) + { + try + { + connection.EndSend(ar); + + StartConnect(); + } + + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private class ServerTimer : Timer + { + public Server Server; + + public ServerTimer(int p) :base(p) + { + } + } + + private void StartConnect() + { + try + { + CreateRemote(); + + // TODO async resolving + IPAddress ipAddress; + bool parsed = IPAddress.TryParse(server.server, out ipAddress); + if (!parsed) + { + IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server); + ipAddress = ipHostInfo.AddressList[0]; + } + IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port); + + remote = new Socket(ipAddress.AddressFamily, + SocketType.Stream, ProtocolType.Tcp); + remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + + _startConnectTime = DateTime.Now; + ServerTimer connectTimer = new ServerTimer(3000); + connectTimer.AutoReset = false; + connectTimer.Elapsed += connectTimer_Elapsed; + connectTimer.Enabled = true; + connectTimer.Server = server; + + connected = false; + // Connect to the remote endpoint. + remote.BeginConnect(remoteEP, + new AsyncCallback(ConnectCallback), connectTimer); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void connectTimer_Elapsed(object sender, ElapsedEventArgs e) + { + if (connected) + { + return; + } + Server server = ((ServerTimer)sender).Server; + IStrategy strategy = controller.GetCurrentStrategy(); + if (strategy != null) + { + strategy.SetFailure(server); + } + Console.WriteLine(String.Format("{0} timed out", server.FriendlyName())); + remote.Close(); + RetryConnect(); + } + + private void RetryConnect() + { + if (retryCount < 4) + { + Logging.Debug("Connection failed, retrying"); + StartConnect(); + retryCount++; + } + else + { + this.Close(); + } + } + + private void ConnectCallback(IAsyncResult ar) + { + Server server = null; + if (closed) + { + return; + } + try + { + ServerTimer timer = (ServerTimer)ar.AsyncState; + server = timer.Server; + timer.Elapsed -= connectTimer_Elapsed; + timer.Enabled = false; + timer.Dispose(); + + // Complete the connection. + remote.EndConnect(ar); + + connected = true; + + //Console.WriteLine("Socket connected to {0}", + // remote.RemoteEndPoint.ToString()); + + var latency = DateTime.Now - _startConnectTime; + IStrategy strategy = controller.GetCurrentStrategy(); + if (strategy != null) + { + strategy.UpdateLatency(server, latency); + } + + StartPipe(); + } + catch (ArgumentException) + { + } + catch (Exception e) + { + if (server != null) + { + IStrategy strategy = controller.GetCurrentStrategy(); + if (strategy != null) + { + strategy.SetFailure(server); + } + } + Logging.LogUsefulException(e); + RetryConnect(); + } + } + + private void StartPipe() + { + if (closed) + { + return; + } + try + { + remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeRemoteReceiveCallback), null); + connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeConnectionReceiveCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeRemoteReceiveCallback(IAsyncResult ar) + { + if (closed) + { + return; + } + try + { + int bytesRead = remote.EndReceive(ar); + totalRead += bytesRead; + + if (bytesRead > 0) + { + this.lastActivity = DateTime.Now; + int bytesToSend; + lock (decryptionLock) + { + if (closed) + { + return; + } + encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); + } + connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); + + IStrategy strategy = controller.GetCurrentStrategy(); + if (strategy != null) + { + strategy.UpdateLastRead(this.server); + } + } + else + { + //Console.WriteLine("bytesRead: " + bytesRead.ToString()); + connection.Shutdown(SocketShutdown.Send); + connectionShutdown = true; + CheckClose(); + + if (totalRead == 0) + { + // closed before anything received, reports as failure + // disable this feature + // controller.GetCurrentStrategy().SetFailure(this.server); + } + } + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeConnectionReceiveCallback(IAsyncResult ar) + { + if (closed) + { + return; + } + try + { + int bytesRead = connection.EndReceive(ar); + totalWrite += bytesRead; + + if (bytesRead > 0) + { + int bytesToSend; + lock (encryptionLock) + { + if (closed) + { + return; + } + encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); + } + remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); + + IStrategy strategy = controller.GetCurrentStrategy(); + if (strategy != null) + { + strategy.UpdateLastWrite(this.server); + } + } + else + { + remote.Shutdown(SocketShutdown.Send); + remoteShutdown = true; + CheckClose(); + } + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeRemoteSendCallback(IAsyncResult ar) + { + if (closed) + { + return; + } + try + { + remote.EndSend(ar); + connection.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeConnectionReceiveCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + + private void PipeConnectionSendCallback(IAsyncResult ar) + { + if (closed) + { + return; + } + try + { + connection.EndSend(ar); + remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0, + new AsyncCallback(PipeRemoteReceiveCallback), null); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + this.Close(); + } + } + } +} diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs new file mode 100644 index 00000000..119307aa --- /dev/null +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Shadowsocks.Encryption; +using Shadowsocks.Model; +using System.Net.Sockets; +using System.Net; +using System.Runtime.CompilerServices; +using Shadowsocks.Controller.Strategy; + +namespace Shadowsocks.Controller +{ + class UDPRelay : Listener.Service + { + private ShadowsocksController _controller; + private LRUCache _cache; + public UDPRelay(ShadowsocksController controller) + { + this._controller = controller; + this._cache = new LRUCache(512); // todo: choose a smart number + } + + public bool Handle(byte[] firstPacket, int length, Socket socket, object state) + { + if (socket.ProtocolType != ProtocolType.Udp) + { + return false; + } + if (length < 4) + { + return false; + } + Listener.UDPState udpState = (Listener.UDPState)state; + IPEndPoint remoteEndPoint = (IPEndPoint)udpState.remoteEndPoint; + UDPHandler handler = _cache.get(remoteEndPoint); + if (handler == null) + { + handler = new UDPHandler(socket, _controller.GetAServer(IStrategyCallerType.UDP, remoteEndPoint), remoteEndPoint); + _cache.add(remoteEndPoint, handler); + } + handler.Send(firstPacket, length); + handler.Receive(); + return true; + } + + public class UDPHandler + { + private Socket _local; + private Socket _remote; + + private Server _server; + private byte[] _buffer = new byte[1500]; + + private IPEndPoint _localEndPoint; + private IPEndPoint _remoteEndPoint; + + public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint) + { + _local = local; + _server = server; + _localEndPoint = localEndPoint; + + // TODO async resolving + IPAddress ipAddress; + bool parsed = IPAddress.TryParse(server.server, out ipAddress); + if (!parsed) + { + IPHostEntry ipHostInfo = Dns.GetHostEntry(server.server); + ipAddress = ipHostInfo.AddressList[0]; + } + _remoteEndPoint = new IPEndPoint(ipAddress, server.server_port); + _remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + + } + public void Send(byte[] data, int length) + { + IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); + byte[] dataIn = new byte[length - 3]; + Array.Copy(data, 3, dataIn, 0, length - 3); + byte[] dataOut = new byte[length - 3 + 16]; + int outlen; + encryptor.Encrypt(dataIn, dataIn.Length, dataOut, out outlen); + _remote.SendTo(dataOut, _remoteEndPoint); + } + public void Receive() + { + EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + _remote.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null); + } + public void RecvFromCallback(IAsyncResult ar) + { + try + { + EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + int bytesRead = _remote.EndReceiveFrom(ar, ref remoteEndPoint); + + byte[] dataOut = new byte[bytesRead]; + int outlen; + + IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); + encryptor.Decrypt(_buffer, bytesRead, dataOut, out outlen); + + byte[] sendBuf = new byte[outlen + 3]; + Array.Copy(dataOut, 0, sendBuf, 3, outlen); + + _local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint); + Receive(); + } + catch (ObjectDisposedException) + { + // TODO: handle the ObjectDisposedException + } + catch (Exception) + { + // TODO: need more think about handle other Exceptions, or should remove this catch(). + } + finally + { + } + } + public void Close() + { + try + { + _remote.Close(); + } + catch (ObjectDisposedException) + { + // TODO: handle the ObjectDisposedException + } + catch (Exception) + { + // TODO: need more think about handle other Exceptions, or should remove this catch(). + } + finally + { + } + } + } + } + + + // cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054 + class LRUCache where V : UDPRelay.UDPHandler + { + private int capacity; + private Dictionary>> cacheMap = new Dictionary>>(); + private LinkedList> lruList = new LinkedList>(); + + public LRUCache(int capacity) + { + this.capacity = capacity; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public V get(K key) + { + LinkedListNode> node; + if (cacheMap.TryGetValue(key, out node)) + { + V value = node.Value.value; + lruList.Remove(node); + lruList.AddLast(node); + return value; + } + return default(V); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void add(K key, V val) + { + if (cacheMap.Count >= capacity) + { + RemoveFirst(); + } + + LRUCacheItem cacheItem = new LRUCacheItem(key, val); + LinkedListNode> node = new LinkedListNode>(cacheItem); + lruList.AddLast(node); + cacheMap.Add(key, node); + } + + private void RemoveFirst() + { + // Remove from LRUPriority + LinkedListNode> node = lruList.First; + lruList.RemoveFirst(); + + // Remove from cache + cacheMap.Remove(node.Value.key); + node.Value.value.Close(); + } + } + + class LRUCacheItem + { + public LRUCacheItem(K k, V v) + { + key = k; + value = v; + } + public K key; + public V value; + } +} diff --git a/shadowsocks-csharp/Controller/UpdateChecker.cs b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs old mode 100755 new mode 100644 similarity index 67% rename from shadowsocks-csharp/Controller/UpdateChecker.cs rename to shadowsocks-csharp/Controller/Service/UpdateChecker.cs index 55b27ee1..392a79a0 --- a/shadowsocks-csharp/Controller/UpdateChecker.cs +++ b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs @@ -1,159 +1,137 @@ -using Shadowsocks.Model; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Net; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml; - -namespace Shadowsocks.Controller -{ - public class UpdateChecker - { - private const string UpdateURL = "https://sourceforge.net/api/file/index/project-id/1817190/path/dist/mtime/desc/limit/10/rss"; - - public string LatestVersionNumber; - public string LatestVersionURL; - public event EventHandler NewVersionFound; - - public const string Version = "2.3.1"; - - public void CheckUpdate(Configuration config) - { - // TODO test failures - WebClient http = new WebClient(); - http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort); - http.DownloadStringCompleted += http_DownloadStringCompleted; - http.DownloadStringAsync(new Uri(UpdateURL)); - } - - public static int CompareVersion(string l, string r) - { - var ls = l.Split('.'); - var rs = r.Split('.'); - for (int i = 0; i < Math.Max(ls.Length, rs.Length); i++) - { - int lp = (i < ls.Length) ? int.Parse(ls[i]) : 0; - int rp = (i < rs.Length) ? int.Parse(rs[i]) : 0; - if (lp != rp) - { - return lp - rp; - } - } - return 0; - } - - public class VersionComparer : IComparer - { - // Calls CaseInsensitiveComparer.Compare with the parameters reversed. - public int Compare(string x, string y) - { - return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y)); - } - - } - - private static string ParseVersionFromURL(string url) - { - Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase); - if (match.Success) - { - if (match.Groups.Count == 2) - { - return match.Groups[1].Value; - } - } - return null; - } - - private void SortVersions(List versions) - { - versions.Sort(new VersionComparer()); - } - - private bool IsNewVersion(string url) - { - if (url.IndexOf("prerelease") >= 0) - { - return false; - } - // check dotnet 4.0 - AssemblyName[] references = Assembly.GetExecutingAssembly().GetReferencedAssemblies(); - Version dotNetVersion = Environment.Version; - foreach (AssemblyName reference in references) - { - if (reference.Name == "mscorlib") - { - dotNetVersion = reference.Version; - } - } - if (dotNetVersion.Major >= 4) - { - if (url.IndexOf("dotnet4.0") < 0) - { - return false; - } - } - else - { - if (url.IndexOf("dotnet4.0") >= 0) - { - return false; - } - } - string version = ParseVersionFromURL(url); - if (version == null) - { - return false; - } - string currentVersion = Version; - - return CompareVersion(version, currentVersion) > 0; - } - - private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) - { - try - { - string response = e.Result; - - XmlDocument xmlDoc = new XmlDocument(); - xmlDoc.LoadXml(response); - XmlNodeList elements = xmlDoc.GetElementsByTagName("media:content"); - List versions = new List(); - foreach (XmlNode el in elements) - { - foreach (XmlAttribute attr in el.Attributes) - { - if (attr.Name == "url") - { - if (IsNewVersion(attr.Value)) - { - versions.Add(attr.Value); - } - } - } - } - if (versions.Count == 0) - { - return; - } - // sort versions - SortVersions(versions); - LatestVersionURL = versions[versions.Count - 1]; - LatestVersionNumber = ParseVersionFromURL(LatestVersionURL); - if (NewVersionFound != null) - { - NewVersionFound(this, new EventArgs()); - } - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - return; - } - } - } -} +using Shadowsocks.Model; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using SimpleJson; + +namespace Shadowsocks.Controller +{ + public class UpdateChecker + { + private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-windows/releases"; + + public string LatestVersionNumber; + public string LatestVersionURL; + public event EventHandler NewVersionFound; + + public const string Version = "2.5.6"; + + public void CheckUpdate(Configuration config) + { + // TODO test failures + WebClient http = new WebClient(); + http.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36"); + http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort); + http.DownloadStringCompleted += http_DownloadStringCompleted; + http.DownloadStringAsync(new Uri(UpdateURL)); + } + + public static int CompareVersion(string l, string r) + { + var ls = l.Split('.'); + var rs = r.Split('.'); + for (int i = 0; i < Math.Max(ls.Length, rs.Length); i++) + { + int lp = (i < ls.Length) ? int.Parse(ls[i]) : 0; + int rp = (i < rs.Length) ? int.Parse(rs[i]) : 0; + if (lp != rp) + { + return lp - rp; + } + } + return 0; + } + + public class VersionComparer : IComparer + { + // Calls CaseInsensitiveComparer.Compare with the parameters reversed. + public int Compare(string x, string y) + { + return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y)); + } + } + + private static string ParseVersionFromURL(string url) + { + Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase); + if (match.Success) + { + if (match.Groups.Count == 2) + { + return match.Groups[1].Value; + } + } + return null; + } + + private void SortVersions(List versions) + { + versions.Sort(new VersionComparer()); + } + + private bool IsNewVersion(string url) + { + if (url.IndexOf("prerelease") >= 0) + { + return false; + } + string version = ParseVersionFromURL(url); + if (version == null) + { + return false; + } + string currentVersion = Version; + + return CompareVersion(version, currentVersion) > 0; + } + + private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) + { + try + { + string response = e.Result; + + JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result); + + List versions = new List(); + foreach (JsonObject release in result) + { + if ((bool)release["prerelease"]) + { + continue; + } + foreach (JsonObject asset in (JsonArray)release["assets"]) + { + string url = (string)asset["browser_download_url"]; + if (IsNewVersion(url)) + { + versions.Add(url); + } + } + } + + if (versions.Count == 0) + { + return; + } + // sort versions + SortVersions(versions); + LatestVersionURL = versions[versions.Count - 1]; + LatestVersionNumber = ParseVersionFromURL(LatestVersionURL); + if (NewVersionFound != null) + { + NewVersionFound(this, new EventArgs()); + } + } + catch (Exception ex) + { + Logging.Debug(ex.ToString()); + return; + } + } + } +} diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index c9408462..2e2f5528 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Text; using System.Threading; using System.Net.Sockets; +using Shadowsocks.Controller.Strategy; +using System.Net; namespace Shadowsocks.Controller { @@ -20,8 +22,10 @@ namespace Shadowsocks.Controller private Listener _listener; private PACServer _pacServer; private Configuration _config; + private StrategyManager _strategyManager; private PolipoRunner polipoRunner; private GFWListUpdater gfwListUpdater; + private AvailabilityStatistics _availabilityStatics; private bool stopped = false; private bool _systemProxyIsDirty = false; @@ -49,6 +53,8 @@ namespace Shadowsocks.Controller public ShadowsocksController() { _config = Configuration.Load(); + _strategyManager = new StrategyManager(this); + StartReleasingMemory(); } public void Start() @@ -70,11 +76,48 @@ namespace Shadowsocks.Controller } // always return copy - public Configuration GetConfiguration() + public Configuration GetConfigurationCopy() { return Configuration.Load(); } + // always return current instance + public Configuration GetCurrentConfiguration() + { + return _config; + } + + public IList GetStrategies() + { + return _strategyManager.GetStrategies(); + } + + public IStrategy GetCurrentStrategy() + { + foreach (var strategy in _strategyManager.GetStrategies()) + { + if (strategy.ID == this._config.strategy) + { + return strategy; + } + } + return null; + } + + public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint) + { + IStrategy strategy = GetCurrentStrategy(); + if (strategy != null) + { + return strategy.GetAServer(type, localIPEndPoint); + } + if (_config.index < 0) + { + _config.index = 0; + } + return GetCurrentServer(); + } + public void SaveServers(List servers, int localPort) { _config.configs = servers; @@ -134,36 +177,15 @@ namespace Shadowsocks.Controller public void SelectServerIndex(int index) { _config.index = index; + _config.strategy = null; SaveConfig(_config); } - public void SelectServerIndexTemp(int index) + public void SelectStrategy(string strategyID) { - _config.index = index; - - if (polipoRunner == null) - polipoRunner = new PolipoRunner(); - - if (_listener != null) - _listener.Stop(); - - try - { - Local local = new Local(_config); - List services = new List(); - services.Add(local); - _listener = new Listener(services); - _listener.Start(_config); - } - catch (Exception e) - { - var se = e as SocketException; - if (se?.SocketErrorCode == SocketError.AccessDenied) - e = new Exception(I18N.GetString("Port already in use"), e); - Logging.LogUsefulException(e); - ReportError(e); - } - Util.Utils.ReleaseMemory(); + _config.index = -1; + _config.strategy = strategyID; + SaveConfig(_config); } public void Stop() @@ -208,6 +230,11 @@ namespace Shadowsocks.Controller public string GetQRCodeForCurrentServer() { Server server = GetCurrentServer(); + return GetQRCode(server); + } + + public static string GetQRCode(Server server) + { string parts = server.method + ":" + server.password + "@" + server.server + ":" + server.server_port; string base64 = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(parts)); return "ss://" + base64; @@ -221,6 +248,16 @@ namespace Shadowsocks.Controller } } + public void ToggleAvailabilityStatistics(bool enabled) + { + if (_availabilityStatics != null) + { + _availabilityStatics.Set(enabled); + _config.availabilityStatistics = enabled; + SaveConfig(_config); + } + } + public void SavePACUrl(string pacUrl) { _config.pacUrl = pacUrl; @@ -270,6 +307,12 @@ namespace Shadowsocks.Controller _listener.Stop(); } + if (_availabilityStatics == null) + { + _availabilityStatics = new AvailabilityStatistics(); + _availabilityStatics.UpdateConfiguration(_config); + } + // don't put polipoRunner.Start() before pacServer.Stop() // or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1 // though UseShellExecute is set to true now @@ -277,11 +320,19 @@ namespace Shadowsocks.Controller polipoRunner.Stop(); try { + var strategy = GetCurrentStrategy(); + if (strategy != null) + { + strategy.ReloadServers(); + } + polipoRunner.Start(_config); - Local local = new Local(_config); + TCPRelay tcpRelay = new TCPRelay(this); + UDPRelay udpRelay = new UDPRelay(this); List services = new List(); - services.Add(local); + services.Add(tcpRelay); + services.Add(udpRelay); services.Add(_pacServer); services.Add(new PortForwarder(polipoRunner.RunningPort)); _listener = new Listener(services); @@ -309,17 +360,15 @@ namespace Shadowsocks.Controller } UpdateSystemProxy(); - Util.Utils.ReleaseMemory(); + Util.Utils.ReleaseMemory(true); } - protected void SaveConfig(Configuration newConfig) { Configuration.Save(newConfig); Reload(); } - private void UpdateSystemProxy() { if (_config.enabled) @@ -366,7 +415,7 @@ namespace Shadowsocks.Controller { while (true) { - Util.Utils.ReleaseMemory(); + Util.Utils.ReleaseMemory(false); Thread.Sleep(30 * 1000); } } diff --git a/shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs b/shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs new file mode 100644 index 00000000..7345fdd9 --- /dev/null +++ b/shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs @@ -0,0 +1,71 @@ +using Shadowsocks.Controller; +using Shadowsocks.Model; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Shadowsocks.Controller.Strategy +{ + class BalancingStrategy : IStrategy + { + ShadowsocksController _controller; + Random _random; + + public BalancingStrategy(ShadowsocksController controller) + { + _controller = controller; + _random = new Random(); + } + + public string Name + { + get { return I18N.GetString("Load Balance"); } + } + + public string ID + { + get { return "com.shadowsocks.strategy.balancing"; } + } + + public void ReloadServers() + { + // do nothing + } + + public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint) + { + var configs = _controller.GetCurrentConfiguration().configs; + int index; + if (type == IStrategyCallerType.TCP) + { + index = _random.Next(); + } + else + { + index = localIPEndPoint.GetHashCode(); + } + return configs[index % configs.Count]; + } + + public void UpdateLatency(Model.Server server, TimeSpan latency) + { + // do nothing + } + + public void UpdateLastRead(Model.Server server) + { + // do nothing + } + + public void UpdateLastWrite(Model.Server server) + { + // do nothing + } + + public void SetFailure(Model.Server server) + { + // do nothing + } + } +} diff --git a/shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs b/shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs new file mode 100644 index 00000000..7049eb78 --- /dev/null +++ b/shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs @@ -0,0 +1,185 @@ +using Shadowsocks.Model; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Shadowsocks.Controller.Strategy +{ + class HighAvailabilityStrategy : IStrategy + { + protected ServerStatus _currentServer; + protected Dictionary _serverStatus; + ShadowsocksController _controller; + Random _random; + + public class ServerStatus + { + // time interval between SYN and SYN+ACK + public TimeSpan latency; + public DateTime lastTimeDetectLatency; + + // last time anything received + public DateTime lastRead; + + // last time anything sent + public DateTime lastWrite; + + // connection refused or closed before anything received + public DateTime lastFailure; + + public Server server; + + public double score; + } + + public HighAvailabilityStrategy(ShadowsocksController controller) + { + _controller = controller; + _random = new Random(); + _serverStatus = new Dictionary(); + } + + public string Name + { + get { return I18N.GetString("High Availability"); } + } + + public string ID + { + get { return "com.shadowsocks.strategy.ha"; } + } + + public void ReloadServers() + { + // make a copy to avoid locking + var newServerStatus = new Dictionary(_serverStatus); + + foreach (var server in _controller.GetCurrentConfiguration().configs) + { + if (!newServerStatus.ContainsKey(server)) + { + var status = new ServerStatus(); + status.server = server; + status.lastFailure = DateTime.MinValue; + status.lastRead = DateTime.Now; + status.lastWrite = DateTime.Now; + status.latency = new TimeSpan(0, 0, 0, 0, 10); + status.lastTimeDetectLatency = DateTime.Now; + newServerStatus[server] = status; + } + else + { + // update settings for existing server + newServerStatus[server].server = server; + } + } + _serverStatus = newServerStatus; + + ChooseNewServer(); + } + + public Server GetAServer(IStrategyCallerType type, System.Net.IPEndPoint localIPEndPoint) + { + if (type == IStrategyCallerType.TCP) + { + ChooseNewServer(); + } + if (_currentServer == null) + { + return null; + } + return _currentServer.server; + } + + /** + * once failed, try after 5 min + * and (last write - last read) < 5s + * and (now - last read) < 5s // means not stuck + * and latency < 200ms, try after 30s + */ + public void ChooseNewServer() + { + ServerStatus oldServer = _currentServer; + List servers = new List(_serverStatus.Values); + DateTime now = DateTime.Now; + foreach (var status in servers) + { + // all of failure, latency, (lastread - lastwrite) normalized to 1000, then + // 100 * failure - 2 * latency - 0.5 * (lastread - lastwrite) + status.score = + 100 * 1000 * Math.Min(5 * 60, (now - status.lastFailure).TotalSeconds) + -2 * 5 * (Math.Min(2000, status.latency.TotalMilliseconds) / (1 + (now - status.lastTimeDetectLatency).TotalSeconds / 30 / 10) + + -0.5 * 200 * Math.Min(5, (status.lastRead - status.lastWrite).TotalSeconds)); + Logging.Debug(String.Format("server: {0} latency:{1} score: {2}", status.server.FriendlyName(), status.latency, status.score)); + } + ServerStatus max = null; + foreach (var status in servers) + { + if (max == null) + { + max = status; + } + else + { + if (status.score >= max.score) + { + max = status; + } + } + } + if (max != null) + { + if (_currentServer == null || max.score - _currentServer.score > 200) + { + _currentServer = max; + Console.WriteLine("HA switching to server: {0}", _currentServer.server.FriendlyName()); + } + } + } + + public void UpdateLatency(Model.Server server, TimeSpan latency) + { + Logging.Debug(String.Format("latency: {0} {1}", server.FriendlyName(), latency)); + + ServerStatus status; + if (_serverStatus.TryGetValue(server, out status)) + { + status.latency = latency; + status.lastTimeDetectLatency = DateTime.Now; + } + } + + public void UpdateLastRead(Model.Server server) + { + Logging.Debug(String.Format("last read: {0}", server.FriendlyName())); + + ServerStatus status; + if (_serverStatus.TryGetValue(server, out status)) + { + status.lastRead = DateTime.Now; + } + } + + public void UpdateLastWrite(Model.Server server) + { + Logging.Debug(String.Format("last write: {0}", server.FriendlyName())); + + ServerStatus status; + if (_serverStatus.TryGetValue(server, out status)) + { + status.lastWrite = DateTime.Now; + } + } + + public void SetFailure(Model.Server server) + { + Logging.Debug(String.Format("failure: {0}", server.FriendlyName())); + + ServerStatus status; + if (_serverStatus.TryGetValue(server, out status)) + { + status.lastFailure = DateTime.Now; + } + } + } +} diff --git a/shadowsocks-csharp/Controller/Strategy/IStrategy.cs b/shadowsocks-csharp/Controller/Strategy/IStrategy.cs new file mode 100644 index 00000000..53271bfb --- /dev/null +++ b/shadowsocks-csharp/Controller/Strategy/IStrategy.cs @@ -0,0 +1,56 @@ +using Shadowsocks.Model; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Shadowsocks.Controller.Strategy +{ + public enum IStrategyCallerType + { + TCP, + UDP + } + + /* + * IStrategy + * + * Subclasses must be thread-safe + */ + public interface IStrategy + { + string Name { get; } + + string ID { get; } + + /* + * Called when servers need to be reloaded, i.e. new configuration saved + */ + void ReloadServers(); + + /* + * Get a new server to use in TCPRelay or UDPRelay + */ + Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint); + + /* + * TCPRelay will call this when latency of a server detected + */ + void UpdateLatency(Server server, TimeSpan latency); + + /* + * TCPRelay will call this when reading from a server + */ + void UpdateLastRead(Server server); + + /* + * TCPRelay will call this when writing to a server + */ + void UpdateLastWrite(Server server); + + /* + * TCPRelay will call this when fatal failure detected + */ + void SetFailure(Server server); + } +} diff --git a/shadowsocks-csharp/Controller/Strategy/SimplyChooseByStatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/SimplyChooseByStatisticsStrategy.cs new file mode 100644 index 00000000..0cdfcfc5 --- /dev/null +++ b/shadowsocks-csharp/Controller/Strategy/SimplyChooseByStatisticsStrategy.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using Shadowsocks.Model; +using System.IO; +using System.Net.NetworkInformation; +using System.Threading; + +namespace Shadowsocks.Controller.Strategy +{ + class SimplyChooseByStatisticsStrategy : IStrategy + { + private ShadowsocksController _controller; + private Server _currentServer; + private Timer timer; + private Dictionary statistics; + private static readonly int CachedInterval = 30 * 60 * 1000; //choose a new server every 30 minutes + + public SimplyChooseByStatisticsStrategy(ShadowsocksController controller) + { + _controller = controller; + var servers = controller.GetCurrentConfiguration().configs; + int randomIndex = new Random().Next() % servers.Count(); + _currentServer = servers[randomIndex]; //choose a server randomly at first + timer = new Timer(ReloadStatisticsAndChooseAServer); + } + + private void ReloadStatisticsAndChooseAServer(object obj) + { + Logging.Debug("Reloading statistics and choose a new server...."); + List servers = _controller.GetCurrentConfiguration().configs; + LoadStatistics(); + ChooseNewServer(servers); + } + + /* + return a dict: + { + 'ServerFriendlyName1':StatisticsData, + 'ServerFriendlyName2':... + } + */ + private void LoadStatistics() + { + try + { + var path = AvailabilityStatistics.AvailabilityStatisticsFile; + Logging.Debug(string.Format("loading statistics from{0}", path)); + statistics = (from l in File.ReadAllLines(path) + .Skip(1) + let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) + let rawData = new + { + ServerName = strings[1], + IPStatus = strings[2], + RoundtripTime = int.Parse(strings[3]) + } + group rawData by rawData.ServerName into server + select new + { + ServerName = server.Key, + data = new StatisticsData + { + SuccessTimes = server.Count(data => IPStatus.Success.ToString().Equals(data.IPStatus)), + TimedOutTimes = server.Count(data => IPStatus.TimedOut.ToString().Equals(data.IPStatus)), + AverageResponse = Convert.ToInt32(server.Average(data => data.RoundtripTime)), + MinResponse = server.Min(data => data.RoundtripTime), + MaxResponse = server.Max(data => data.RoundtripTime) + } + }).ToDictionary(server => server.ServerName, server => server.data); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + } + } + + //return the score by data + //server with highest score will be choosen + private static double GetScore(StatisticsData data) + { + return (double)data.SuccessTimes / (data.SuccessTimes + data.TimedOutTimes); //simply choose min package loss + } + + private class StatisticsData + { + public int SuccessTimes; + public int TimedOutTimes; + public int AverageResponse; + public int MinResponse; + public int MaxResponse; + } + + private void ChooseNewServer(List servers) + { + if (statistics == null) + { + return; + } + try + { + var bestResult = (from server in servers + let name = server.FriendlyName() + where statistics.ContainsKey(name) + select new + { + server, + score = GetScore(statistics[name]) + } + ).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); + + if (_controller.GetCurrentStrategy().ID == ID && _currentServer != bestResult.server) //output when enabled + { + Console.WriteLine("Switch to server: {0} by package loss:{1}", bestResult.server.FriendlyName(), 1 - bestResult.score); + } + _currentServer = bestResult.server; + } + catch (Exception e) + { + Logging.LogUsefulException(e); + } + } + + public string ID + { + get { return "com.shadowsocks.strategy.scbs"; } + } + + public string Name + { + get { return I18N.GetString("Choose By Total Package Loss"); } + } + + public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint) + { + var oldServer = _currentServer; + if (oldServer == null) + { + ChooseNewServer(_controller.GetCurrentConfiguration().configs); + } + if (oldServer != _currentServer) + { + } + return _currentServer; //current server cached for CachedInterval + } + + public void ReloadServers() + { + ChooseNewServer(_controller.GetCurrentConfiguration().configs); + timer?.Change(0, CachedInterval); + } + + public void SetFailure(Server server) + { + Logging.Debug(String.Format("failure: {0}", server.FriendlyName())); + } + + public void UpdateLastRead(Server server) + { + //TODO: combine this part of data with ICMP statics + } + + public void UpdateLastWrite(Server server) + { + //TODO: combine this part of data with ICMP statics + } + + public void UpdateLatency(Server server, TimeSpan latency) + { + //TODO: combine this part of data with ICMP statics + } + + } +} diff --git a/shadowsocks-csharp/Controller/Strategy/StrategyManager.cs b/shadowsocks-csharp/Controller/Strategy/StrategyManager.cs new file mode 100644 index 00000000..3d7baf2a --- /dev/null +++ b/shadowsocks-csharp/Controller/Strategy/StrategyManager.cs @@ -0,0 +1,24 @@ +using Shadowsocks.Controller; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Shadowsocks.Controller.Strategy +{ + class StrategyManager + { + List _strategies; + public StrategyManager(ShadowsocksController controller) + { + _strategies = new List(); + _strategies.Add(new BalancingStrategy(controller)); + _strategies.Add(new HighAvailabilityStrategy(controller)); + _strategies.Add(new SimplyChooseByStatisticsStrategy(controller)); + // TODO: load DLL plugins + } + public IList GetStrategies() + { + return _strategies; + } + } +} diff --git a/shadowsocks-csharp/Controller/AutoStartup.cs b/shadowsocks-csharp/Controller/System/AutoStartup.cs similarity index 96% rename from shadowsocks-csharp/Controller/AutoStartup.cs rename to shadowsocks-csharp/Controller/System/AutoStartup.cs index f406e87c..2063323d 100644 --- a/shadowsocks-csharp/Controller/AutoStartup.cs +++ b/shadowsocks-csharp/Controller/System/AutoStartup.cs @@ -1,55 +1,55 @@ -using System; -using System.Windows.Forms; -using Microsoft.Win32; - -namespace Shadowsocks.Controller -{ - class AutoStartup - { - public static bool Set(bool enabled) - { - try - { - string path = Application.ExecutablePath; - RegistryKey runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); - if (enabled) - { - runKey.SetValue("Shadowsocks", path); - } - else - { - runKey.DeleteValue("Shadowsocks"); - } - runKey.Close(); - return true; - } - catch (Exception e) - { - Logging.LogUsefulException(e); - return false; - } - } - - public static bool Check() - { - try - { - string path = Application.ExecutablePath; - RegistryKey runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run"); - string[] runList = runKey.GetValueNames(); - runKey.Close(); - foreach (string item in runList) - { - if (item.Equals("Shadowsocks")) - return true; - } - return false; - } - catch (Exception e) - { - Logging.LogUsefulException(e); - return false; - } - } - } -} +using System; +using System.Windows.Forms; +using Microsoft.Win32; + +namespace Shadowsocks.Controller +{ + class AutoStartup + { + public static bool Set(bool enabled) + { + try + { + string path = Application.ExecutablePath; + RegistryKey runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); + if (enabled) + { + runKey.SetValue("Shadowsocks", path); + } + else + { + runKey.DeleteValue("Shadowsocks"); + } + runKey.Close(); + return true; + } + catch (Exception e) + { + Logging.LogUsefulException(e); + return false; + } + } + + public static bool Check() + { + try + { + string path = Application.ExecutablePath; + RegistryKey runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run"); + string[] runList = runKey.GetValueNames(); + runKey.Close(); + foreach (string item in runList) + { + if (item.Equals("Shadowsocks")) + return true; + } + return false; + } + catch (Exception e) + { + Logging.LogUsefulException(e); + return false; + } + } + } +} diff --git a/shadowsocks-csharp/Controller/SystemProxy.cs b/shadowsocks-csharp/Controller/System/SystemProxy.cs old mode 100755 new mode 100644 similarity index 74% rename from shadowsocks-csharp/Controller/SystemProxy.cs rename to shadowsocks-csharp/Controller/System/SystemProxy.cs index 5fe8a244..4c259a4a --- a/shadowsocks-csharp/Controller/SystemProxy.cs +++ b/shadowsocks-csharp/Controller/System/SystemProxy.cs @@ -1,110 +1,138 @@ -using System.Windows.Forms; -using Microsoft.Win32; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; -using System.IO; -using Shadowsocks.Model; - -namespace Shadowsocks.Controller -{ - public class SystemProxy - { - - [DllImport("wininet.dll")] - public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength); - public const int INTERNET_OPTION_SETTINGS_CHANGED = 39; - public const int INTERNET_OPTION_REFRESH = 37; - static bool _settingsReturn, _refreshReturn; - - public static void NotifyIE() - { - // These lines implement the Interface in the beginning of program - // They cause the OS to refresh the settings, causing IP to realy update - _settingsReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0); - _refreshReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0); - } - - public static void Update(Configuration config, bool forceDisable) - { - bool global = config.global; - bool enabled = config.enabled; - if (forceDisable) - { - enabled = false; - } - try - { - RegistryKey registry = - Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", - true); - if (enabled) - { - if (global) - { - registry.SetValue("ProxyEnable", 1); - registry.SetValue("ProxyServer", "127.0.0.1:" + config.localPort.ToString()); - registry.SetValue("AutoConfigURL", ""); - } - else - { - string pacUrl; - if (config.useOnlinePac && !string.IsNullOrEmpty(config.pacUrl)) - pacUrl = config.pacUrl; - else - pacUrl = "http://127.0.0.1:" + config.localPort.ToString() + "/pac?t=" + GetTimestamp(DateTime.Now); - registry.SetValue("ProxyEnable", 0); - registry.SetValue("ProxyServer", ""); - registry.SetValue("AutoConfigURL", pacUrl); - } - } - else - { - registry.SetValue("ProxyEnable", 0); - registry.SetValue("ProxyServer", ""); - registry.SetValue("AutoConfigURL", ""); - } - SystemProxy.NotifyIE(); - //Must Notify IE first, or the connections do not chanage - CopyProxySettingFromLan(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - // TODO this should be moved into views - MessageBox.Show(I18N.GetString("Failed to update registry")); - } - } - - private static void CopyProxySettingFromLan() - { - RegistryKey registry = - Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Connections", - true); - var defaultValue = registry.GetValue("DefaultConnectionSettings"); - try - { - var connections = registry.GetValueNames(); - foreach (String each in connections) - { - if (!(each.Equals("DefaultConnectionSettings") - || each.Equals("LAN Connection") - || each.Equals("SavedLegacySettings"))) - { - //set all the connections's proxy as the lan - registry.SetValue(each, defaultValue); - } - } - SystemProxy.NotifyIE(); - } catch (IOException e) { - Logging.LogUsefulException(e); - } - } - - private static String GetTimestamp(DateTime value) - { - return value.ToString("yyyyMMddHHmmssffff"); - } - } -} +using System.Windows.Forms; +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using System.IO; +using Shadowsocks.Model; + +namespace Shadowsocks.Controller +{ + public class SystemProxy + { + + [DllImport("wininet.dll")] + public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength); + public const int INTERNET_OPTION_SETTINGS_CHANGED = 39; + public const int INTERNET_OPTION_REFRESH = 37; + static bool _settingsReturn, _refreshReturn; + + public static void NotifyIE() + { + // These lines implement the Interface in the beginning of program + // They cause the OS to refresh the settings, causing IP to realy update + _settingsReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0); + _refreshReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0); + } + + public static void Update(Configuration config, bool forceDisable) + { + bool global = config.global; + bool enabled = config.enabled; + if (forceDisable) + { + enabled = false; + } + try + { + RegistryKey registry = + Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + true); + if (enabled) + { + if (global) + { + registry.SetValue("ProxyEnable", 1); + registry.SetValue("ProxyServer", "127.0.0.1:" + config.localPort.ToString()); + registry.SetValue("AutoConfigURL", ""); + } + else + { + string pacUrl; + if (config.useOnlinePac && !string.IsNullOrEmpty(config.pacUrl)) + pacUrl = config.pacUrl; + else + pacUrl = "http://127.0.0.1:" + config.localPort.ToString() + "/pac?t=" + GetTimestamp(DateTime.Now); + registry.SetValue("ProxyEnable", 0); + var readProxyServer = registry.GetValue("ProxyServer"); + registry.SetValue("ProxyServer", ""); + registry.SetValue("AutoConfigURL", pacUrl); + } + } + else + { + registry.SetValue("ProxyEnable", 0); + registry.SetValue("ProxyServer", ""); + registry.SetValue("AutoConfigURL", ""); + } + //Set AutoDetectProxy Off + IEAutoDetectProxy(false); + SystemProxy.NotifyIE(); + //Must Notify IE first, or the connections do not chanage + CopyProxySettingFromLan(); + } + catch (Exception e) + { + Logging.LogUsefulException(e); + // TODO this should be moved into views + MessageBox.Show(I18N.GetString("Failed to update registry")); + } + } + + private static void CopyProxySettingFromLan() + { + RegistryKey registry = + Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Connections", + true); + var defaultValue = registry.GetValue("DefaultConnectionSettings"); + try + { + var connections = registry.GetValueNames(); + foreach (String each in connections) + { + if (!(each.Equals("DefaultConnectionSettings") + || each.Equals("LAN Connection") + || each.Equals("SavedLegacySettings"))) + { + //set all the connections's proxy as the lan + registry.SetValue(each, defaultValue); + } + } + SystemProxy.NotifyIE(); + } catch (IOException e) { + Logging.LogUsefulException(e); + } + } + + private static String GetTimestamp(DateTime value) + { + return value.ToString("yyyyMMddHHmmssffff"); + } + + /// + /// Checks or unchecks the IE Options Connection setting of "Automatically detect Proxy" + /// + /// Provide 'true' if you want to check the 'Automatically detect Proxy' check box. To uncheck, pass 'false' + private static void IEAutoDetectProxy(bool set) + { + RegistryKey registry = + Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Connections", + true); + byte[] defConnection = (byte[])registry.GetValue("DefaultConnectionSettings"); + byte[] savedLegacySetting = (byte[])registry.GetValue("SavedLegacySettings"); + if (set) + { + defConnection[8] = Convert.ToByte(defConnection[8] & 8); + savedLegacySetting[8] = Convert.ToByte(savedLegacySetting[8] & 8); + } + else + { + defConnection[8] = Convert.ToByte(defConnection[8] & ~8); + savedLegacySetting[8] = Convert.ToByte(savedLegacySetting[8] & ~8); + } + registry.SetValue("DefaultConnectionSettings", defConnection); + registry.SetValue("SavedLegacySettings", savedLegacySetting); + } + } +} diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index 49b73c6f..81022f99 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -19,10 +19,14 @@ Update Local PAC from GFWList=从 GFWList 更新本地 PAC Edit User Rule for GFWList...=编辑 GFWList 的用户规则... Show QRCode...=显示二维码... Scan QRCode from Screen...=扫描屏幕上的二维码... +Availability Statistics=统计可用性 Show Logs...=显示日志... About...=关于... Quit=退出 Edit Servers=编辑服务器 +Load Balance=负载均衡 +High Availability=高可用 +Choose By Total Package Loss=累计丢包率 # Config Form @@ -38,6 +42,19 @@ Remarks=备注 OK=确定 Cancel=取消 New server=未配置的服务器 +Move &Up=上移(&U) +Move D&own=下移(&O) + +# Log Form + +&File=文件(&F) +&Open Location=在资源管理器中打开(&O) +E&xit=退出(&X) +&Clean logs=清空(&C) +&Font=字体(&F) +&Wrap text=自动换行(&W) +&Top most=置顶(&T) +Log Viewer=日志查看器 # QRCode Form @@ -58,6 +75,7 @@ Please add at least one server=请添加至少一个服务器 Server IP can not be blank=服务器 IP 不能为空 Password can not be blank=密码不能为空 Port out of range=端口超出范围 +Port can't be 8123=端口不能为 8123 Shadowsocks {0} Update Found=Shadowsocks {0} 更新 Click here to download=点击这里下载 Shadowsocks is here=Shadowsocks 在这里 @@ -68,29 +86,10 @@ Failed to update PAC file =更新 PAC 文件失败 PAC updated=更新 PAC 成功 No updates found. Please report to GFWList if you have problems with it.=未发现更新。如有问题请提交给 GFWList。 No QRCode found. Try to zoom in or move it to the center of the screen.=未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置 +Shadowsocks is already running.=Shadowsocks 已经在运行。 +Find Shadowsocks icon in your notify tray.=请在任务栏里寻找 Shadowsocks 图标。 +If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同时启动多个,可以另外复制一份到别的目录。 Failed to decode QRCode=无法解析二维码 Failed to update registry=无法修改注册表 System Proxy On: =系统代理已启用: Running: Port {0}=正在运行:端口 {0} - - -# Pingform -UsableTest...=可用性测试... -UsableTest=服务器可用性测试 -SvcAddr=地址 -Location=物理地址 -Max=最大 -Min=最小 -Average=平均 -IP=IP地址 -Speed=下载速度 -TestSpeed=测速 -Pinging=正在获取 -PingFail=获取失败 -FailTime=失败次数 -CurrentStatus:=当前状态: -Ready=准备就绪 -DoSomething=我们正在处理一些事情 -Wrong=遇到一些问题 -Unknow=未知 -Seems your server is down, r u sure apply this server?=看起来你的服务器不可用,确定要使用这个服务器吗? \ No newline at end of file diff --git a/shadowsocks-csharp/Data/mgwz.dll.gz b/shadowsocks-csharp/Data/mgwz.dll.gz new file mode 100644 index 0000000000000000000000000000000000000000..139725f37fca93a8b16380e52a5945e8d2b5c030 GIT binary patch literal 48551 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 literal 0 KcmV+b0RR6000031 diff --git a/shadowsocks-csharp/Data/polipo.exe.gz b/shadowsocks-csharp/Data/polipo.exe.gz deleted file mode 100755 index c7038e1247bde4b3ccc2443e71222bbb8a6ffb6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 KcmV+b0RR6000031 literal 65830 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

r#grVgua=(tYSo|B{#P%H(f_b6KR}y);9UI2 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®JLWa7v@&cJ;dUaF7jd#HbK5 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

D`$*GTsCf% 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 literal 0 KcmV+b0RR6000031 diff --git a/shadowsocks-csharp/Data/privoxy_conf.txt b/shadowsocks-csharp/Data/privoxy_conf.txt new file mode 100644 index 00000000..cf5e794f --- /dev/null +++ b/shadowsocks-csharp/Data/privoxy_conf.txt @@ -0,0 +1,5 @@ +listen-address __POLIPO_BIND_IP__:8123 +show-on-task-bar 0 +activity-animation 0 +forward-socks5 / 127.0.0.1:__SOCKS_PORT__ . +hide-console \ No newline at end of file diff --git a/shadowsocks-csharp/Encryption/IVEncryptor.cs b/shadowsocks-csharp/Encryption/IVEncryptor.cs index 0a59a0df..32948d6b 100755 --- a/shadowsocks-csharp/Encryption/IVEncryptor.cs +++ b/shadowsocks-csharp/Encryption/IVEncryptor.cs @@ -26,7 +26,6 @@ namespace Shadowsocks.Encryption protected int keyLen; protected int ivLen; - public IVEncryptor(string method, string password) : base(method, password) { diff --git a/shadowsocks-csharp/Encryption/PolarSSL.cs b/shadowsocks-csharp/Encryption/PolarSSL.cs index 93b662a8..42ce5bf7 100755 --- a/shadowsocks-csharp/Encryption/PolarSSL.cs +++ b/shadowsocks-csharp/Encryption/PolarSSL.cs @@ -1,5 +1,6 @@ using Shadowsocks.Controller; using Shadowsocks.Properties; +using Shadowsocks.Util; using System; using System.Collections.Generic; using System.IO; @@ -18,7 +19,7 @@ namespace Shadowsocks.Encryption static PolarSSL() { - string tempPath = Path.GetTempPath(); + string tempPath = Utils.GetTempPath(); string dllPath = tempPath + "/libsscrypto.dll"; try { @@ -49,7 +50,6 @@ namespace Shadowsocks.Encryption [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public extern static int aes_crypt_cfb128(IntPtr ctx, int mode, int length, ref int iv_off, byte[] iv, byte[] input, byte[] output); - public const int ARC4_CTX_SIZE = 264; [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] diff --git a/shadowsocks-csharp/Encryption/Sodium.cs b/shadowsocks-csharp/Encryption/Sodium.cs index 9bc72d32..564aaeda 100755 --- a/shadowsocks-csharp/Encryption/Sodium.cs +++ b/shadowsocks-csharp/Encryption/Sodium.cs @@ -1,5 +1,6 @@ using Shadowsocks.Controller; using Shadowsocks.Properties; +using Shadowsocks.Util; using System; using System.Collections.Generic; using System.IO; @@ -14,7 +15,7 @@ namespace Shadowsocks.Encryption static Sodium() { - string tempPath = Path.GetTempPath(); + string tempPath = Utils.GetTempPath(); string dllPath = tempPath + "/libsscrypto.dll"; try { diff --git a/shadowsocks-csharp/Encryption/SodiumEncryptor.cs b/shadowsocks-csharp/Encryption/SodiumEncryptor.cs index 5d6165eb..af51d0ac 100755 --- a/shadowsocks-csharp/Encryption/SodiumEncryptor.cs +++ b/shadowsocks-csharp/Encryption/SodiumEncryptor.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading; namespace Shadowsocks.Encryption { @@ -12,19 +13,17 @@ namespace Shadowsocks.Encryption const int SODIUM_BLOCK_SIZE = 64; + static byte[] sodiumBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE]; + protected int _encryptBytesRemaining; protected int _decryptBytesRemaining; protected ulong _encryptIC; protected ulong _decryptIC; - protected byte[] _encryptBuf; - protected byte[] _decryptBuf; public SodiumEncryptor(string method, string password) : base(method, password) { InitKey(method, password); - _encryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE]; - _decryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE]; } private static Dictionary _ciphers = new Dictionary { @@ -47,48 +46,51 @@ namespace Shadowsocks.Encryption // TODO write a unidirection cipher so we don't have to if if if int bytesRemaining; ulong ic; - byte[] sodiumBuf; byte[] iv; - if (isCipher) - { - bytesRemaining = _encryptBytesRemaining; - ic = _encryptIC; - sodiumBuf = _encryptBuf; - iv = _encryptIV; - } - else - { - bytesRemaining = _decryptBytesRemaining; - ic = _decryptIC; - sodiumBuf = _decryptBuf; - iv = _decryptIV; - } - int padding = bytesRemaining; - Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length); - switch (_cipher) + // I'm tired. just add a big lock + // let's optimize for RAM instead of CPU + lock(sodiumBuf) { - case CIPHER_SALSA20: - Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); - break; - case CIPHER_CHACHA20: - Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); - break; - } - Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length); - padding += length; - ic += (ulong)padding / SODIUM_BLOCK_SIZE; - bytesRemaining = padding % SODIUM_BLOCK_SIZE; + if (isCipher) + { + bytesRemaining = _encryptBytesRemaining; + ic = _encryptIC; + iv = _encryptIV; + } + else + { + bytesRemaining = _decryptBytesRemaining; + ic = _decryptIC; + iv = _decryptIV; + } + int padding = bytesRemaining; + Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length); - if (isCipher) - { - _encryptBytesRemaining = bytesRemaining; - _encryptIC = ic; - } - else - { - _decryptBytesRemaining = bytesRemaining; - _decryptIC = ic; + switch (_cipher) + { + case CIPHER_SALSA20: + Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); + break; + case CIPHER_CHACHA20: + Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); + break; + } + Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length); + padding += length; + ic += (ulong)padding / SODIUM_BLOCK_SIZE; + bytesRemaining = padding % SODIUM_BLOCK_SIZE; + + if (isCipher) + { + _encryptBytesRemaining = bytesRemaining; + _encryptIC = ic; + } + else + { + _decryptBytesRemaining = bytesRemaining; + _decryptIC = ic; + } } } diff --git a/shadowsocks-csharp/Encryption/TableEncryptor.cs b/shadowsocks-csharp/Encryption/TableEncryptor.cs index 3dd526d4..db0a7db0 100644 --- a/shadowsocks-csharp/Encryption/TableEncryptor.cs +++ b/shadowsocks-csharp/Encryption/TableEncryptor.cs @@ -41,7 +41,6 @@ namespace Shadowsocks.Encryption outlength = length; } - public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) { byte[] result = new byte[length]; @@ -100,7 +99,6 @@ namespace Shadowsocks.Encryption return sorted; } - public override void Dispose() { } diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index b78358e8..1ccba56c 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -11,6 +11,9 @@ namespace Shadowsocks.Model public class Configuration { public List configs; + + // when strategy is set, index is ignored + public string strategy; public int index; public bool global; public bool enabled; @@ -19,6 +22,7 @@ namespace Shadowsocks.Model public int localPort; public string pacUrl; public bool useOnlinePac; + public bool availabilityStatistics; private static string CONFIG_FILE = "gui-config.json"; @@ -52,6 +56,13 @@ namespace Shadowsocks.Model { config.localPort = 1080; } + if (config.index == -1) + { + if (config.strategy == null) + { + config.index = 0; + } + } return config; } catch (Exception e) @@ -79,9 +90,16 @@ namespace Shadowsocks.Model { config.index = config.configs.Count - 1; } - if (config.index < 0) + if (config.index < -1) + { + config.index = -1; + } + if (config.index == -1) { - config.index = 0; + if (config.strategy == null) + { + config.index = 0; + } } config.isDefault = false; try @@ -120,6 +138,15 @@ namespace Shadowsocks.Model } } + public static void CheckLocalPort(int port) + { + CheckPort(port); + if (port == 8123) + { + throw new ArgumentException(I18N.GetString("Port can't be 8123")); + } + } + private static void CheckPassword(string password) { if (string.IsNullOrEmpty(password)) diff --git a/shadowsocks-csharp/Model/Server.cs b/shadowsocks-csharp/Model/Server.cs index 4acc3a0e..24dd1162 100755 --- a/shadowsocks-csharp/Model/Server.cs +++ b/shadowsocks-csharp/Model/Server.cs @@ -18,6 +18,17 @@ namespace Shadowsocks.Model public string method; public string remarks; + public override int GetHashCode() + { + return server.GetHashCode() ^ server_port; + } + + public override bool Equals(object obj) + { + Server o2 = (Server)obj; + return this.server == o2.server && this.server_port == o2.server_port; + } + public string FriendlyName() { if (string.IsNullOrEmpty(server)) diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index 8ca485d7..cb40cb23 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -18,8 +18,8 @@ namespace Shadowsocks [STAThread] static void Main() { - Util.Utils.ReleaseMemory(); - using (Mutex mutex = new Mutex(false, "Global\\" + "71981632-A427-497F-AB91-241CD227EC1F")) + Util.Utils.ReleaseMemory(true); + using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode())) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); @@ -31,7 +31,9 @@ namespace Shadowsocks { Process oldProcess = oldProcesses[0]; } - MessageBox.Show("Shadowsocks is already running.\n\nFind Shadowsocks icon in your notify tray."); + MessageBox.Show(I18N.GetString("Find Shadowsocks icon in your notify tray.") + "\n" + + I18N.GetString("If you want to start multiple Shadowsocks, make a copy in another directory."), + I18N.GetString("Shadowsocks is already running.")); return; } Directory.SetCurrentDirectory(Application.StartupPath); diff --git a/shadowsocks-csharp/Properties/Resources.Designer.cs b/shadowsocks-csharp/Properties/Resources.Designer.cs index 9d7942c5..198e9f48 100755 --- a/shadowsocks-csharp/Properties/Resources.Designer.cs +++ b/shadowsocks-csharp/Properties/Resources.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18408 +// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -113,29 +113,34 @@ namespace Shadowsocks.Properties { } ///

- /// Looks up a localized string similar to proxyAddress = "__POLIPO_BIND_IP__" - ///proxyPort = 8123 - /// - ///socksParentProxy = "127.0.0.1:__SOCKS_PORT__" - ///socksProxyType = socks5 - ///diskCacheRoot = "" - ///localDocumentRoot = "" - /// - ///allowedPorts = 1-65535 - ///tunnelAllowedPorts = 1-65535. + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] mgwz_dll { + get { + object obj = ResourceManager.GetObject("mgwz_dll", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized string similar to listen-address __POLIPO_BIND_IP__:8123 + ///show-on-task-bar 0 + ///activity-animation 0 + ///forward-socks5 / 127.0.0.1:__SOCKS_PORT__ . + ///hide-console. /// - internal static string polipo_config { + internal static string privoxy_conf { get { - return ResourceManager.GetString("polipo_config", resourceCulture); + return ResourceManager.GetString("privoxy_conf", resourceCulture); } } /// /// Looks up a localized resource of type System.Byte[]. /// - internal static byte[] polipo_exe { + internal static byte[] privoxy_exe { get { - object obj = ResourceManager.GetObject("polipo_exe", resourceCulture); + object obj = ResourceManager.GetObject("privoxy_exe", resourceCulture); return ((byte[])(obj)); } } diff --git a/shadowsocks-csharp/Properties/Resources.resx b/shadowsocks-csharp/Properties/Resources.resx index aa851e6b..aa0c2f63 100755 --- a/shadowsocks-csharp/Properties/Resources.resx +++ b/shadowsocks-csharp/Properties/Resources.resx @@ -112,12 +112,12 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + ..\Data\abp.js.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 @@ -127,11 +127,14 @@ ..\data\libsscrypto.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ..\Data\polipo_config.txt;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312 + + ..\data\mgwz.dll.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ..\Data\polipo.exe.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\data\privoxy_conf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + ..\data\privoxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Data\proxy.pac.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index 0f674687..1858ee90 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -1,27 +1,35 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Drawing; using System.IO; using System.IO.Compression; -using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; -using Shadowsocks._3rd.ProxySocket; namespace Shadowsocks.Util { public class Utils { - public static Shadowsocks._3rd.QQWry qqwry; - public static Font GetFont() + // return path to store temporary files + public static string GetTempPath() { - var fs = FontFamily.Families; - var ret = fs.FirstOrDefault(f => f.Name == "Microsoft YaHei UI Light") ?? fs.FirstOrDefault(f => f.Name == "Microsoft YaHei UI"); - return ret == null ? SystemFonts.MessageBoxFont : new Font(ret,9f,FontStyle.Regular); + if (File.Exists(Application.StartupPath + "\\shadowsocks_portable_mode.txt")) + { + try + { + Directory.CreateDirectory(Application.StartupPath + "\\temp"); + } catch (Exception e) + { + Console.WriteLine(e); + } + // don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log + return Application.StartupPath + "\\temp"; + } + return Path.GetTempPath(); } - public static void ReleaseMemory() + + public static void ReleaseMemory(bool removePages) { // release any unused pages // making the numbers look good in task manager @@ -31,8 +39,29 @@ namespace Shadowsocks.Util // which is part of user experience GC.Collect(GC.MaxGeneration); GC.WaitForPendingFinalizers(); - SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, - (UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF); + if (removePages) + { + // as some users have pointed out + // removing pages from working set will cause some IO + // which lowered user experience for another group of users + // + // so we do 2 more things here to satisfy them: + // 1. only remove pages once when configuration is changed + // 2. add more comments here to tell users that calling + // this function will not be more frequent than + // IM apps writing chat logs, or web browsers writing cache files + // if they're so concerned about their disk, they should + // uninstall all IM apps and web browsers + // + // please open an issue if you're worried about anything else in your computer + // no matter it's GPU performance, monitor contrast, audio fidelity + // or anything else in the task manager + // we'll do as much as we can to help you + // + // just kidding + SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, + (UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF); + } } public static string UnGzip(byte[] buf) diff --git a/shadowsocks-csharp/View/ConfigForm.Designer.cs b/shadowsocks-csharp/View/ConfigForm.Designer.cs index 83dae64a..f8b5940a 100755 --- a/shadowsocks-csharp/View/ConfigForm.Designer.cs +++ b/shadowsocks-csharp/View/ConfigForm.Designer.cs @@ -47,6 +47,9 @@ this.ServerGroupBox = new System.Windows.Forms.GroupBox(); this.ServersListBox = new System.Windows.Forms.ListBox(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.tableLayoutPanel6 = new System.Windows.Forms.TableLayoutPanel(); + this.MoveDownButton = new System.Windows.Forms.Button(); + this.MoveUpButton = new System.Windows.Forms.Button(); this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel(); this.ProxyPortTextBox = new System.Windows.Forms.TextBox(); this.ProxyPortLabel = new System.Windows.Forms.Label(); @@ -55,6 +58,7 @@ this.tableLayoutPanel1.SuspendLayout(); this.ServerGroupBox.SuspendLayout(); this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel6.SuspendLayout(); this.tableLayoutPanel5.SuspendLayout(); this.tableLayoutPanel3.SuspendLayout(); this.tableLayoutPanel4.SuspendLayout(); @@ -184,7 +188,7 @@ // // EncryptionSelect // - this.EncryptionSelect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.EncryptionSelect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.EncryptionSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.EncryptionSelect.FormattingEnabled = true; @@ -281,6 +285,7 @@ // this.ServersListBox.FormattingEnabled = true; this.ServersListBox.IntegralHeight = false; + this.ServersListBox.ItemHeight = 12; this.ServersListBox.Location = new System.Drawing.Point(0, 0); this.ServersListBox.Margin = new System.Windows.Forms.Padding(0); this.ServersListBox.Name = "ServersListBox"; @@ -295,6 +300,7 @@ this.tableLayoutPanel2.ColumnCount = 2; this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel6, 0, 2); this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel5, 1, 1); this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 2); this.tableLayoutPanel2.Controls.Add(this.ServersListBox, 0, 0); @@ -310,6 +316,48 @@ this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 238); this.tableLayoutPanel2.TabIndex = 7; // + // tableLayoutPanel6 + // + this.tableLayoutPanel6.AutoSize = true; + this.tableLayoutPanel6.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.tableLayoutPanel6.ColumnCount = 2; + this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel6.Controls.Add(this.MoveDownButton, 1, 0); + this.tableLayoutPanel6.Controls.Add(this.MoveUpButton, 0, 0); + this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Top; + this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 211); + this.tableLayoutPanel6.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel6.Name = "tableLayoutPanel6"; + this.tableLayoutPanel6.RowCount = 1; + this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel6.Size = new System.Drawing.Size(166, 32); + this.tableLayoutPanel6.TabIndex = 10; + // + // MoveDownButton + // + this.MoveDownButton.Dock = System.Windows.Forms.DockStyle.Right; + this.MoveDownButton.Location = new System.Drawing.Point(86, 6); + this.MoveDownButton.Margin = new System.Windows.Forms.Padding(3, 6, 0, 3); + this.MoveDownButton.Name = "MoveDownButton"; + this.MoveDownButton.Size = new System.Drawing.Size(80, 23); + this.MoveDownButton.TabIndex = 7; + this.MoveDownButton.Text = "Move D&own"; + this.MoveDownButton.UseVisualStyleBackColor = true; + this.MoveDownButton.Click += new System.EventHandler(this.MoveDownButton_Click); + // + // MoveUpButton + // + this.MoveUpButton.Dock = System.Windows.Forms.DockStyle.Left; + this.MoveUpButton.Location = new System.Drawing.Point(0, 6); + this.MoveUpButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3); + this.MoveUpButton.Name = "MoveUpButton"; + this.MoveUpButton.Size = new System.Drawing.Size(80, 23); + this.MoveUpButton.TabIndex = 6; + this.MoveUpButton.Text = "Move &Up"; + this.MoveUpButton.UseVisualStyleBackColor = true; + this.MoveUpButton.Click += new System.EventHandler(this.MoveUpButton_Click); + // // tableLayoutPanel5 // this.tableLayoutPanel5.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) @@ -418,6 +466,7 @@ this.ServerGroupBox.PerformLayout(); this.tableLayoutPanel2.ResumeLayout(false); this.tableLayoutPanel2.PerformLayout(); + this.tableLayoutPanel6.ResumeLayout(false); this.tableLayoutPanel5.ResumeLayout(false); this.tableLayoutPanel5.PerformLayout(); this.tableLayoutPanel3.ResumeLayout(false); @@ -453,6 +502,9 @@ private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5; private System.Windows.Forms.TextBox ProxyPortTextBox; private System.Windows.Forms.Label ProxyPortLabel; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6; + private System.Windows.Forms.Button MoveDownButton; + private System.Windows.Forms.Button MoveUpButton; } } diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs index 62bfe145..d7d5c1f3 100755 --- a/shadowsocks-csharp/View/ConfigForm.cs +++ b/shadowsocks-csharp/View/ConfigForm.cs @@ -18,16 +18,15 @@ namespace Shadowsocks.View // this is a copy of configuration that we are working on private Configuration _modifiedConfiguration; - private int _oldSelectedIndex = -1; + private int _lastSelectedIndex = -1; public ConfigForm(ShadowsocksController controller) { - //this.Font = System.Drawing.SystemFonts.MessageBoxFont; - Font = Util.Utils.GetFont(); + this.Font = System.Drawing.SystemFonts.MessageBoxFont; InitializeComponent(); // a dirty hack - this.ServersListBox.Dock = DockStyle.Fill; + this.ServersListBox.Dock = System.Windows.Forms.DockStyle.Fill; this.PerformLayout(); UpdateTexts(); @@ -52,6 +51,8 @@ namespace Shadowsocks.View ServerGroupBox.Text = I18N.GetString("Server"); OKButton.Text = I18N.GetString("OK"); MyCancelButton.Text = I18N.GetString("Cancel"); + MoveUpButton.Text = I18N.GetString("Move &Up"); + MoveDownButton.Text = I18N.GetString("Move D&own"); this.Text = I18N.GetString("Edit Servers"); } @@ -59,7 +60,7 @@ namespace Shadowsocks.View { LoadCurrentConfiguration(); } - + private void ShowWindow() { this.Opacity = 1; @@ -71,7 +72,7 @@ namespace Shadowsocks.View { try { - if (_oldSelectedIndex == -1 || _oldSelectedIndex >= _modifiedConfiguration.configs.Count) + if (_lastSelectedIndex == -1 || _lastSelectedIndex >= _modifiedConfiguration.configs.Count) { return true; } @@ -85,10 +86,10 @@ namespace Shadowsocks.View }; int localPort = int.Parse(ProxyPortTextBox.Text); Configuration.CheckServer(server); - Configuration.CheckPort(localPort); - _modifiedConfiguration.configs[_oldSelectedIndex] = server; + Configuration.CheckLocalPort(localPort); + _modifiedConfiguration.configs[_lastSelectedIndex] = server; _modifiedConfiguration.localPort = localPort; - + return true; } catch (FormatException) @@ -114,12 +115,6 @@ namespace Shadowsocks.View ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString(); EncryptionSelect.Text = server.method ?? "aes-256-cfb"; RemarksTextBox.Text = server.remarks; - ServerGroupBox.Visible = true; - //IPTextBox.Focus(); - } - else - { - ServerGroupBox.Visible = false; } } @@ -134,10 +129,15 @@ namespace Shadowsocks.View private void LoadCurrentConfiguration() { - _modifiedConfiguration = controller.GetConfiguration(); + _modifiedConfiguration = controller.GetConfigurationCopy(); LoadConfiguration(_modifiedConfiguration); - _oldSelectedIndex = _modifiedConfiguration.index; - ServersListBox.SelectedIndex = _modifiedConfiguration.index; + _lastSelectedIndex = _modifiedConfiguration.index; + if (_lastSelectedIndex < 0) + { + _lastSelectedIndex = 0; + } + ServersListBox.SelectedIndex = _lastSelectedIndex; + UpdateMoveUpAndDownButton(); LoadSelectedServer(); } @@ -148,7 +148,11 @@ namespace Shadowsocks.View private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e) { - if (_oldSelectedIndex == ServersListBox.SelectedIndex) + if (!ServersListBox.CanSelect) + { + return; + } + if (_lastSelectedIndex == ServersListBox.SelectedIndex) { // we are moving back to oldSelectedIndex or doing a force move return; @@ -156,11 +160,13 @@ namespace Shadowsocks.View if (!SaveOldSelectedServer()) { // why this won't cause stack overflow? - ServersListBox.SelectedIndex = _oldSelectedIndex; + ServersListBox.SelectedIndex = _lastSelectedIndex; return; } + ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName(); + UpdateMoveUpAndDownButton(); LoadSelectedServer(); - _oldSelectedIndex = ServersListBox.SelectedIndex; + _lastSelectedIndex = ServersListBox.SelectedIndex; } private void AddButton_Click(object sender, EventArgs e) @@ -173,29 +179,30 @@ namespace Shadowsocks.View _modifiedConfiguration.configs.Add(server); LoadConfiguration(_modifiedConfiguration); ServersListBox.SelectedIndex = _modifiedConfiguration.configs.Count - 1; - _oldSelectedIndex = ServersListBox.SelectedIndex; + _lastSelectedIndex = ServersListBox.SelectedIndex; } private void DeleteButton_Click(object sender, EventArgs e) { - _oldSelectedIndex = ServersListBox.SelectedIndex; - if (_oldSelectedIndex >= 0 && _oldSelectedIndex < _modifiedConfiguration.configs.Count) + _lastSelectedIndex = ServersListBox.SelectedIndex; + if (_lastSelectedIndex >= 0 && _lastSelectedIndex < _modifiedConfiguration.configs.Count) { - _modifiedConfiguration.configs.RemoveAt(_oldSelectedIndex); + _modifiedConfiguration.configs.RemoveAt(_lastSelectedIndex); } - if (_oldSelectedIndex >= _modifiedConfiguration.configs.Count) + if (_lastSelectedIndex >= _modifiedConfiguration.configs.Count) { // can be -1 - _oldSelectedIndex = _modifiedConfiguration.configs.Count - 1; + _lastSelectedIndex = _modifiedConfiguration.configs.Count - 1; } - ServersListBox.SelectedIndex = _oldSelectedIndex; + ServersListBox.SelectedIndex = _lastSelectedIndex; LoadConfiguration(_modifiedConfiguration); - ServersListBox.SelectedIndex = _oldSelectedIndex; + ServersListBox.SelectedIndex = _lastSelectedIndex; LoadSelectedServer(); } private void OKButton_Click(object sender, EventArgs e) { + Server server = controller.GetCurrentServer(); if (!SaveOldSelectedServer()) { return; @@ -206,6 +213,7 @@ namespace Shadowsocks.View return; } controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort); + controller.SelectServerIndex(_modifiedConfiguration.configs.IndexOf(server)); this.Close(); } @@ -224,5 +232,70 @@ namespace Shadowsocks.View controller.ConfigChanged -= controller_ConfigChanged; } + private void MoveConfigItem(int step) + { + int index = ServersListBox.SelectedIndex; + Server server = _modifiedConfiguration.configs[index]; + object item = ServersListBox.SelectedItem; + + _modifiedConfiguration.configs.Remove(server); + _modifiedConfiguration.configs.Insert(index + step, server); + _modifiedConfiguration.index += step; + + ServersListBox.BeginUpdate(); + ServersListBox.Enabled = false; + _lastSelectedIndex = index + step; + ServersListBox.Items.Remove(item); + ServersListBox.Items.Insert(index + step, item); + ServersListBox.Enabled = true; + ServersListBox.SelectedIndex = index + step; + ServersListBox.EndUpdate(); + + UpdateMoveUpAndDownButton(); + } + + private void UpdateMoveUpAndDownButton() + { + if (ServersListBox.SelectedIndex == 0) + { + MoveUpButton.Enabled = false; + } + else + { + MoveUpButton.Enabled = true; + } + if (ServersListBox.SelectedIndex == ServersListBox.Items.Count - 1) + { + MoveDownButton.Enabled = false; + } + else + { + MoveDownButton.Enabled = true; + } + } + + private void MoveUpButton_Click(object sender, EventArgs e) + { + if (!SaveOldSelectedServer()) + { + return; + } + if (ServersListBox.SelectedIndex > 0) + { + MoveConfigItem(-1); // -1 means move backward + } + } + + private void MoveDownButton_Click(object sender, EventArgs e) + { + if (!SaveOldSelectedServer()) + { + return; + } + if (ServersListBox.SelectedIndex < ServersListBox.Items.Count - 1) + { + MoveConfigItem(+1); // +1 means move forward + } + } } } diff --git a/shadowsocks-csharp/View/ConfigForm.resx b/shadowsocks-csharp/View/ConfigForm.resx index ff31a6db..c7e0d4bd 100755 --- a/shadowsocks-csharp/View/ConfigForm.resx +++ b/shadowsocks-csharp/View/ConfigForm.resx @@ -112,9 +112,9 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 \ No newline at end of file diff --git a/shadowsocks-csharp/View/LogForm.Designer.cs b/shadowsocks-csharp/View/LogForm.Designer.cs new file mode 100644 index 00000000..1e8ef104 --- /dev/null +++ b/shadowsocks-csharp/View/LogForm.Designer.cs @@ -0,0 +1,199 @@ +namespace Shadowsocks.View +{ + partial class LogForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.LogMessageTextBox = new System.Windows.Forms.TextBox(); + this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); + this.mainMenu1 = new System.Windows.Forms.MainMenu(this.components); + this.FileMenuItem = new System.Windows.Forms.MenuItem(); + this.OpenLocationMenuItem = new System.Windows.Forms.MenuItem(); + this.ExitMenuItem = new System.Windows.Forms.MenuItem(); + this.panel1 = new System.Windows.Forms.Panel(); + this.ChangeFontButton = new System.Windows.Forms.Button(); + this.CleanLogsButton = new System.Windows.Forms.Button(); + this.WrapTextCheckBox = new System.Windows.Forms.CheckBox(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.TopMostCheckBox = new System.Windows.Forms.CheckBox(); + this.panel1.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // LogMessageTextBox + // + this.LogMessageTextBox.BackColor = System.Drawing.Color.Black; + this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.LogMessageTextBox.ForeColor = System.Drawing.Color.White; + this.LogMessageTextBox.Location = new System.Drawing.Point(3, 43); + this.LogMessageTextBox.MaxLength = 2147483647; + this.LogMessageTextBox.Multiline = true; + this.LogMessageTextBox.Name = "LogMessageTextBox"; + this.LogMessageTextBox.ReadOnly = true; + this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.LogMessageTextBox.Size = new System.Drawing.Size(541, 307); + this.LogMessageTextBox.TabIndex = 0; + this.LogMessageTextBox.WordWrap = false; + // + // contextMenuStrip1 + // + this.contextMenuStrip1.Name = "contextMenuStrip1"; + this.contextMenuStrip1.Size = new System.Drawing.Size(61, 4); + // + // mainMenu1 + // + this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.FileMenuItem}); + // + // FileMenuItem + // + this.FileMenuItem.Index = 0; + this.FileMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.OpenLocationMenuItem, + this.ExitMenuItem}); + this.FileMenuItem.Text = "&File"; + // + // OpenLocationMenuItem + // + this.OpenLocationMenuItem.Index = 0; + this.OpenLocationMenuItem.Text = "&Open Location"; + this.OpenLocationMenuItem.Click += new System.EventHandler(this.OpenLocationMenuItem_Click); + // + // ExitMenuItem + // + this.ExitMenuItem.Index = 1; + this.ExitMenuItem.Text = "E&xit"; + this.ExitMenuItem.Click += new System.EventHandler(this.ExitMenuItem_Click); + // + // panel1 + // + this.panel1.Controls.Add(this.TopMostCheckBox); + this.panel1.Controls.Add(this.ChangeFontButton); + this.panel1.Controls.Add(this.CleanLogsButton); + this.panel1.Controls.Add(this.WrapTextCheckBox); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(3, 3); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(541, 34); + this.panel1.TabIndex = 1; + // + // ChangeFontButton + // + this.ChangeFontButton.Location = new System.Drawing.Point(107, 4); + this.ChangeFontButton.Name = "ChangeFontButton"; + this.ChangeFontButton.Size = new System.Drawing.Size(75, 23); + this.ChangeFontButton.TabIndex = 2; + this.ChangeFontButton.Text = "&Font"; + this.ChangeFontButton.UseVisualStyleBackColor = true; + this.ChangeFontButton.Click += new System.EventHandler(this.ChangeFontButton_Click); + // + // CleanLogsButton + // + this.CleanLogsButton.Location = new System.Drawing.Point(9, 4); + this.CleanLogsButton.Name = "CleanLogsButton"; + this.CleanLogsButton.Size = new System.Drawing.Size(75, 23); + this.CleanLogsButton.TabIndex = 1; + this.CleanLogsButton.Text = "&Clean logs"; + this.CleanLogsButton.UseVisualStyleBackColor = true; + this.CleanLogsButton.Click += new System.EventHandler(this.CleanLogsButton_Click); + // + // WrapTextCheckBox + // + this.WrapTextCheckBox.AutoSize = true; + this.WrapTextCheckBox.Location = new System.Drawing.Point(209, 9); + this.WrapTextCheckBox.Name = "WrapTextCheckBox"; + this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 16); + this.WrapTextCheckBox.TabIndex = 0; + this.WrapTextCheckBox.Text = "&Wrap text"; + this.WrapTextCheckBox.UseVisualStyleBackColor = true; + this.WrapTextCheckBox.CheckedChanged += new System.EventHandler(this.WrapTextCheckBox_CheckedChanged); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.LogMessageTextBox, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.Size = new System.Drawing.Size(547, 353); + this.tableLayoutPanel1.TabIndex = 2; + // + // TopMostCheckBox + // + this.TopMostCheckBox.AutoSize = true; + this.TopMostCheckBox.Location = new System.Drawing.Point(311, 9); + this.TopMostCheckBox.Name = "TopMostCheckBox"; + this.TopMostCheckBox.Size = new System.Drawing.Size(72, 16); + this.TopMostCheckBox.TabIndex = 3; + this.TopMostCheckBox.Text = "&Top most"; + this.TopMostCheckBox.UseVisualStyleBackColor = true; + this.TopMostCheckBox.CheckedChanged += new System.EventHandler(this.TopMostCheckBox_CheckedChanged); + // + // LogForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(547, 353); + this.Controls.Add(this.tableLayoutPanel1); + this.Menu = this.mainMenu1; + this.Name = "LogForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Log Viewer"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LogForm_FormClosing); + this.Load += new System.EventHandler(this.LogForm_Load); + this.Shown += new System.EventHandler(this.LogForm_Shown); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TextBox LogMessageTextBox; + private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; + private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.MenuItem FileMenuItem; + private System.Windows.Forms.MenuItem OpenLocationMenuItem; + private System.Windows.Forms.MenuItem ExitMenuItem; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.CheckBox WrapTextCheckBox; + private System.Windows.Forms.Button CleanLogsButton; + private System.Windows.Forms.Button ChangeFontButton; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.CheckBox TopMostCheckBox; + } +} \ No newline at end of file diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs new file mode 100644 index 00000000..ac0fbbff --- /dev/null +++ b/shadowsocks-csharp/View/LogForm.cs @@ -0,0 +1,150 @@ +using Shadowsocks.Controller; +using Shadowsocks.Properties; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace Shadowsocks.View +{ + public partial class LogForm : Form + { + long lastOffset; + string filename; + Timer timer; + const int BACK_OFFSET = 65536; + + public LogForm(string filename) + { + this.filename = filename; + InitializeComponent(); + this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); + + UpdateTexts(); + } + + private void UpdateTexts() + { + FileMenuItem.Text = I18N.GetString("&File"); + OpenLocationMenuItem.Text = I18N.GetString("&Open Location"); + ExitMenuItem.Text = I18N.GetString("E&xit"); + CleanLogsButton.Text = I18N.GetString("&Clean logs"); + ChangeFontButton.Text = I18N.GetString("&Font"); + WrapTextCheckBox.Text = I18N.GetString("&Wrap text"); + TopMostCheckBox.Text = I18N.GetString("&Top most"); + this.Text = I18N.GetString("Log Viewer"); + } + + private void Timer_Tick(object sender, EventArgs e) + { + UpdateContent(); + } + + private void InitContent() + { + using (StreamReader reader = new StreamReader(new FileStream(filename, + FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) + { + if (reader.BaseStream.Length > BACK_OFFSET) + { + reader.BaseStream.Seek(-BACK_OFFSET, SeekOrigin.End); + reader.ReadLine(); + } + + string line = ""; + while ((line = reader.ReadLine()) != null) + LogMessageTextBox.AppendText(line + "\r\n"); + + LogMessageTextBox.ScrollToCaret(); + + lastOffset = reader.BaseStream.Position; + } + } + + private void UpdateContent() + { + using (StreamReader reader = new StreamReader(new FileStream(filename, + FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) + { + reader.BaseStream.Seek(lastOffset, SeekOrigin.Begin); + + string line = ""; + bool changed = false; + while ((line = reader.ReadLine()) != null) + { + changed = true; + LogMessageTextBox.AppendText(line + "\r\n"); + } + + if (changed) + { + LogMessageTextBox.ScrollToCaret(); + } + + lastOffset = reader.BaseStream.Position; + } + } + + private void LogForm_Load(object sender, EventArgs e) + { + InitContent(); + timer = new Timer(); + timer.Interval = 300; + timer.Tick += Timer_Tick; + timer.Start(); + } + + private void LogForm_FormClosing(object sender, FormClosingEventArgs e) + { + timer.Stop(); + } + + private void OpenLocationMenuItem_Click(object sender, EventArgs e) + { + string argument = "/select, \"" + filename + "\""; + Console.WriteLine(argument); + System.Diagnostics.Process.Start("explorer.exe", argument); + } + + private void ExitMenuItem_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void LogForm_Shown(object sender, EventArgs e) + { + LogMessageTextBox.ScrollToCaret(); + } + + private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e) + { + LogMessageTextBox.WordWrap = WrapTextCheckBox.Checked; + LogMessageTextBox.ScrollToCaret(); + } + + private void CleanLogsButton_Click(object sender, EventArgs e) + { + LogMessageTextBox.Clear(); + } + + private void ChangeFontButton_Click(object sender, EventArgs e) + { + FontDialog fd = new FontDialog(); + fd.Font = LogMessageTextBox.Font; + if (fd.ShowDialog() == DialogResult.OK) + { + LogMessageTextBox.Font = fd.Font; + } + } + + private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e) + { + this.TopMost = TopMostCheckBox.Checked; + } + } +} diff --git a/shadowsocks-csharp/View/PingForm.resx b/shadowsocks-csharp/View/LogForm.resx similarity index 78% rename from shadowsocks-csharp/View/PingForm.resx rename to shadowsocks-csharp/View/LogForm.resx index c18db4ca..5d2dcea1 100644 --- a/shadowsocks-csharp/View/PingForm.resx +++ b/shadowsocks-csharp/View/LogForm.resx @@ -117,31 +117,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - + 17, 17 + + 172, 17 + \ No newline at end of file diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index 4fcc6c6f..c8a4c0eb 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -18,7 +18,7 @@ namespace Shadowsocks.View // yes this is just a menu view controller // when config form is closed, it moves away from RAM // and it should just do anything related to the config form - + private ShadowsocksController controller; private UpdateChecker updateChecker; @@ -29,6 +29,7 @@ namespace Shadowsocks.View private MenuItem enableItem; private MenuItem modeItem; private MenuItem AutoStartupItem; + private MenuItem AvailabilityStatistics; private MenuItem ShareOverLANItem; private MenuItem SeperatorItem; private MenuItem ConfigItem; @@ -42,9 +43,6 @@ namespace Shadowsocks.View private MenuItem editGFWUserRuleItem; private MenuItem editOnlinePACItem; private ConfigForm configForm; - - private PingForm pingForm; - private string _urlToOpen; public MenuViewController(ShadowsocksController controller) @@ -74,9 +72,9 @@ namespace Shadowsocks.View LoadCurrentConfiguration(); - updateChecker.CheckUpdate(controller.GetConfiguration()); + updateChecker.CheckUpdate(controller.GetConfigurationCopy()); - if (controller.GetConfiguration().isDefault) + if (controller.GetConfigurationCopy().isDefault) { _isFirstRun = true; ShowConfigForm(); @@ -109,7 +107,7 @@ namespace Shadowsocks.View { icon = Resources.ss24; } - Configuration config = controller.GetConfiguration(); + Configuration config = controller.GetConfigurationCopy(); bool enabled = config.enabled; bool global = config.global; if (!enabled) @@ -127,20 +125,29 @@ namespace Shadowsocks.View } _notifyIcon.Icon = Icon.FromHandle(icon.GetHicon()); + string serverInfo = null; + if (controller.GetCurrentStrategy() != null) + { + serverInfo = controller.GetCurrentStrategy().Name; + } + else + { + serverInfo = config.GetCurrentServer().FriendlyName(); + } // we want to show more details but notify icon title is limited to 63 characters string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" + (enabled ? I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) : String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running - + "\n" + config.GetCurrentServer().FriendlyName(); + + "\n" + serverInfo; _notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length)); } + private MenuItem CreateMenuItem(string text, EventHandler click) { return new MenuItem(I18N.GetString(text), click); } - private MenuItem CreateMenuGroup(string text, MenuItem[] items) { return new MenuItem(I18N.GetString(text), items); @@ -171,10 +178,9 @@ namespace Shadowsocks.View }), new MenuItem("-"), this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), + this.AvailabilityStatistics = CreateMenuItem("Availability Statistics", new EventHandler(this.AvailabilityStatisticsItem_Click)), this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), new MenuItem("-"), - CreateMenuItem("UsableTest...", new EventHandler(this.PingForm_Click)), - new MenuItem("-"), CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)), new MenuItem("-"), @@ -190,18 +196,18 @@ namespace Shadowsocks.View private void controller_EnableStatusChanged(object sender, EventArgs e) { - enableItem.Checked = controller.GetConfiguration().enabled; + enableItem.Checked = controller.GetConfigurationCopy().enabled; modeItem.Enabled = enableItem.Checked; } void controller_ShareOverLANStatusChanged(object sender, EventArgs e) { - ShareOverLANItem.Checked = controller.GetConfiguration().shareOverLan; + ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan; } void controller_EnableGlobalChanged(object sender, EventArgs e) { - globalModeItem.Checked = controller.GetConfiguration().global; + globalModeItem.Checked = controller.GetConfigurationCopy().global; PACModeItem.Checked = !globalModeItem.Checked; } @@ -248,7 +254,7 @@ namespace Shadowsocks.View private void LoadCurrentConfiguration() { - Configuration config = controller.GetConfiguration(); + Configuration config = controller.GetConfigurationCopy(); UpdateServersMenu(); enableItem.Checked = config.enabled; modeItem.Enabled = config.enabled; @@ -256,6 +262,7 @@ namespace Shadowsocks.View PACModeItem.Checked = !config.global; ShareOverLANItem.Checked = config.shareOverLan; AutoStartupItem.Checked = AutoStartup.Check(); + AvailabilityStatistics.Checked = config.availabilityStatistics; onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; localPACItem.Checked = !onlinePACItem.Checked; UpdatePACItemsEnabledStatus(); @@ -268,44 +275,34 @@ namespace Shadowsocks.View { items.RemoveAt(0); } - - Configuration configuration = controller.GetConfiguration(); - for (int i = 0; i < configuration.configs.Count; i++) + int i = 0; + foreach (var strategy in controller.GetStrategies()) + { + MenuItem item = new MenuItem(strategy.Name); + item.Tag = strategy.ID; + item.Click += AStrategyItem_Click; + items.Add(i, item); + i++; + } + int strategyCount = i; + Configuration configuration = controller.GetConfigurationCopy(); + foreach (var server in configuration.configs) { - Server server = configuration.configs[i]; MenuItem item = new MenuItem(server.FriendlyName()); - item.Tag = i; + item.Tag = i - strategyCount; item.Click += AServerItem_Click; items.Add(i, item); + i++; } - if (configuration.index >= 0 && configuration.index < configuration.configs.Count) + foreach (MenuItem item in items) { - items[configuration.index].Checked = true; - } - } + if (item.Tag != null && (item.Tag.ToString() == configuration.index.ToString() || item.Tag.ToString() == configuration.strategy)) + { + item.Checked = true; + } - private void ShowPingForm() - { - if (pingForm != null) - { - pingForm.Activate(); } - else - { - pingForm = new PingForm(controller); - pingForm.Show(); - pingForm.FormClosed += pingForm_FormClosed; - } - } - private void PingForm_Click(object sender, EventArgs e) - { - ShowPingForm(); - } - void pingForm_FormClosed(object sender, FormClosedEventArgs e) - { - pingForm = null; - Util.Utils.ReleaseMemory(); } private void ShowConfigForm() @@ -325,7 +322,7 @@ namespace Shadowsocks.View void configForm_FormClosed(object sender, FormClosedEventArgs e) { configForm = null; - Util.Utils.ReleaseMemory(); + Util.Utils.ReleaseMemory(true); ShowFirstTimeBalloon(); } @@ -355,7 +352,7 @@ namespace Shadowsocks.View private void AboutItem_Click(object sender, EventArgs e) { - Process.Start("https://github.com/shadowsocks/shadowsocks-csharp"); + Process.Start("https://github.com/shadowsocks/shadowsocks-windows"); } private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e) @@ -408,11 +405,17 @@ namespace Shadowsocks.View controller.SelectServerIndex((int)item.Tag); } + private void AStrategyItem_Click(object sender, EventArgs e) + { + MenuItem item = (MenuItem)sender; + controller.SelectStrategy((string)item.Tag); + } + private void ShowLogItem_Click(object sender, EventArgs e) { string argument = Logging.LogFile; - System.Diagnostics.Process.Start("notepad.exe", argument); + new LogForm(argument).Show(); } private void QRCodeItem_Click(object sender, EventArgs e) @@ -517,12 +520,17 @@ namespace Shadowsocks.View Process.Start(_urlToOpen); } - private void AutoStartupItem_Click(object sender, EventArgs e) { - AutoStartupItem.Checked = !AutoStartupItem.Checked; - if (!AutoStartup.Set(AutoStartupItem.Checked)) { - MessageBox.Show(I18N.GetString("Failed to update registry")); - } - } + private void AutoStartupItem_Click(object sender, EventArgs e) { + AutoStartupItem.Checked = !AutoStartupItem.Checked; + if (!AutoStartup.Set(AutoStartupItem.Checked)) { + MessageBox.Show(I18N.GetString("Failed to update registry")); + } + } + + private void AvailabilityStatisticsItem_Click(object sender, EventArgs e) { + AvailabilityStatistics.Checked = !AvailabilityStatistics.Checked; + controller.ToggleAvailabilityStatistics(AvailabilityStatistics.Checked); + } private void LocalPACItem_Click(object sender, EventArgs e) { @@ -539,11 +547,11 @@ namespace Shadowsocks.View { if (!onlinePACItem.Checked) { - if (String.IsNullOrEmpty(controller.GetConfiguration().pacUrl)) + if (String.IsNullOrEmpty(controller.GetConfigurationCopy().pacUrl)) { UpdateOnlinePACURLItem_Click(sender, e); } - if (!String.IsNullOrEmpty(controller.GetConfiguration().pacUrl)) + if (!String.IsNullOrEmpty(controller.GetConfigurationCopy().pacUrl)) { localPACItem.Checked = false; onlinePACItem.Checked = true; @@ -555,7 +563,7 @@ namespace Shadowsocks.View private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e) { - string origPacUrl = controller.GetConfiguration().pacUrl; + string origPacUrl = controller.GetConfigurationCopy().pacUrl; string pacUrl = Microsoft.VisualBasic.Interaction.InputBox( I18N.GetString("Please input PAC Url"), I18N.GetString("Edit Online PAC URL"), diff --git a/shadowsocks-csharp/View/PingForm.Designer.cs b/shadowsocks-csharp/View/PingForm.Designer.cs deleted file mode 100644 index 6f5e2a4c..00000000 --- a/shadowsocks-csharp/View/PingForm.Designer.cs +++ /dev/null @@ -1,237 +0,0 @@ -using Microsoft.VisualBasic; - -namespace Shadowsocks.View -{ - sealed partial class PingForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.dgvMain = new System.Windows.Forms.DataGridView(); - this.SvcAddr = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.IP = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Remarks = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Location = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Max = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Min = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Average = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.FailTime = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.Speed = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.TestSpeed = new System.Windows.Forms.DataGridViewLinkColumn(); - this.statusStrip1 = new System.Windows.Forms.StatusStrip(); - this.tssStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); - this.tssStatus = new System.Windows.Forms.ToolStripStatusLabel(); - this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); - this.pBar = new System.Windows.Forms.ToolStripProgressBar(); - ((System.ComponentModel.ISupportInitialize)(this.dgvMain)).BeginInit(); - this.statusStrip1.SuspendLayout(); - this.SuspendLayout(); - // - // dgvMain - // - this.dgvMain.AllowUserToAddRows = false; - this.dgvMain.AllowUserToDeleteRows = false; - this.dgvMain.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - this.dgvMain.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { - this.SvcAddr, - this.IP, - this.Remarks, - this.Location, - this.Max, - this.Min, - this.Average, - this.FailTime, - this.Speed, - this.TestSpeed}); - this.dgvMain.Dock = System.Windows.Forms.DockStyle.Fill; - this.dgvMain.Location = new System.Drawing.Point(0, 0); - this.dgvMain.Name = "dgvMain"; - this.dgvMain.ReadOnly = true; - this.dgvMain.RowTemplate.Height = 23; - this.dgvMain.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - this.dgvMain.Size = new System.Drawing.Size(878, 507); - this.dgvMain.TabIndex = 0; - this.dgvMain.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvMain_CellClick); - this.dgvMain.CellMouseDoubleClick += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dgvMain_CellMouseDoubleClick); - // - // SvcAddr - // - this.SvcAddr.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.SvcAddr.Frozen = true; - this.SvcAddr.HeaderText = "Address"; - this.SvcAddr.Name = "SvcAddr"; - this.SvcAddr.ReadOnly = true; - this.SvcAddr.Width = 72; - // - // IP - // - this.IP.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.IP.HeaderText = "IP"; - this.IP.Name = "IP"; - this.IP.ReadOnly = true; - this.IP.Width = 42; - // - // Remarks - // - this.Remarks.HeaderText = "Remark"; - this.Remarks.Name = "Remarks"; - this.Remarks.ReadOnly = true; - this.Remarks.Width = 80; - // - // Location - // - this.Location.HeaderText = "Location"; - this.Location.MinimumWidth = 70; - this.Location.Name = "Location"; - this.Location.ReadOnly = true; - this.Location.Width = 150; - // - // Max - // - this.Max.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.Max.HeaderText = "Max"; - this.Max.Name = "Max"; - this.Max.ReadOnly = true; - this.Max.Width = 48; - // - // Min - // - this.Min.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.Min.HeaderText = "Min"; - this.Min.Name = "Min"; - this.Min.ReadOnly = true; - this.Min.Width = 48; - // - // Average - // - this.Average.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.Average.HeaderText = "Average"; - this.Average.Name = "Average"; - this.Average.ReadOnly = true; - this.Average.Width = 72; - // - // FailTime - // - this.FailTime.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.FailTime.HeaderText = "FailTime"; - this.FailTime.Name = "FailTime"; - this.FailTime.ReadOnly = true; - this.FailTime.Width = 78; - // - // Speed - // - this.Speed.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.Speed.HeaderText = "Speed"; - this.Speed.Name = "Speed"; - this.Speed.ReadOnly = true; - this.Speed.Width = 60; - // - // TestSpeed - // - this.TestSpeed.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; - this.TestSpeed.HeaderText = "TestSpeed"; - this.TestSpeed.Name = "TestSpeed"; - this.TestSpeed.ReadOnly = true; - this.TestSpeed.Width = 65; - // - // statusStrip1 - // - this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.tssStatusLabel, - this.tssStatus, - this.toolStripStatusLabel1, - this.pBar}); - this.statusStrip1.Location = new System.Drawing.Point(0, 485); - this.statusStrip1.Name = "statusStrip1"; - this.statusStrip1.Size = new System.Drawing.Size(878, 22); - this.statusStrip1.TabIndex = 1; - this.statusStrip1.Text = "statusStrip1"; - // - // tssStatusLabel - // - this.tssStatusLabel.Name = "tssStatusLabel"; - this.tssStatusLabel.Size = new System.Drawing.Size(89, 17); - this.tssStatusLabel.Text = "CurrentStatus:"; - // - // tssStatus - // - this.tssStatus.Name = "tssStatus"; - this.tssStatus.Size = new System.Drawing.Size(55, 17); - this.tssStatus.Text = "Unknow"; - // - // toolStripStatusLabel1 - // - this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; - this.toolStripStatusLabel1.Size = new System.Drawing.Size(719, 17); - this.toolStripStatusLabel1.Spring = true; - // - // pBar - // - this.pBar.Name = "pBar"; - this.pBar.Size = new System.Drawing.Size(100, 16); - this.pBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; - this.pBar.Visible = false; - // - // PingForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.AutoSize = true; - this.ClientSize = new System.Drawing.Size(878, 507); - this.Controls.Add(this.statusStrip1); - this.Controls.Add(this.dgvMain); - this.Name = "PingForm"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "PingForm"; - ((System.ComponentModel.ISupportInitialize)(this.dgvMain)).EndInit(); - this.statusStrip1.ResumeLayout(false); - this.statusStrip1.PerformLayout(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.DataGridView dgvMain; - private System.Windows.Forms.DataGridViewTextBoxColumn Local; - private System.Windows.Forms.StatusStrip statusStrip1; - private System.Windows.Forms.ToolStripStatusLabel tssStatusLabel; - private System.Windows.Forms.ToolStripStatusLabel tssStatus; - private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1; - private System.Windows.Forms.ToolStripProgressBar pBar; - private System.Windows.Forms.DataGridViewTextBoxColumn SvcAddr; - private System.Windows.Forms.DataGridViewTextBoxColumn IP; - private System.Windows.Forms.DataGridViewTextBoxColumn Remarks; - private System.Windows.Forms.DataGridViewTextBoxColumn Location; - private System.Windows.Forms.DataGridViewTextBoxColumn Max; - private System.Windows.Forms.DataGridViewTextBoxColumn Min; - private System.Windows.Forms.DataGridViewTextBoxColumn Average; - private System.Windows.Forms.DataGridViewTextBoxColumn FailTime; - private System.Windows.Forms.DataGridViewTextBoxColumn Speed; - private System.Windows.Forms.DataGridViewLinkColumn TestSpeed; - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/View/PingForm.cs b/shadowsocks-csharp/View/PingForm.cs deleted file mode 100644 index 7037cf41..00000000 --- a/shadowsocks-csharp/View/PingForm.cs +++ /dev/null @@ -1,325 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Net.NetworkInformation; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Windows.Forms; -using Shadowsocks.Controller; -using Shadowsocks.Model; -using Shadowsocks.Properties; -using Shadowsocks._3rd; -using Shadowsocks._3rd.ProxySocket; - -namespace Shadowsocks.View -{ - public sealed partial class PingForm : Form - { - private readonly ShadowsocksController controller; - - #region Delegate - private delegate void selectMinMax(); - - private void SelectMinMax() - { - if (dgvMain.InvokeRequired) - { - var invoke = new selectMinMax(SelectMinMax); - Invoke(invoke); - } - else - { - var q = dgvMain.Rows.Cast().Where(row => (int)row.Cells["Average"].Value != 9999).ToArray(); - int max = q.Max(x => Convert.ToInt32(x.Cells["Average"].Value)); - int min = q.Min(x => Convert.ToInt32(x.Cells["Average"].Value)); - foreach (DataGridViewRow row in dgvMain.Rows) - { - if ((int)row.Cells["Average"].Value == min) row.DefaultCellStyle.ForeColor = Color.Green; - if ((int)row.Cells["Average"].Value == max) row.DefaultCellStyle.ForeColor = Color.Red; - } - } - } - - private delegate void changeStatus(string val); - private void ChangeStatus(string val) - { - if (statusStrip1.InvokeRequired) - { - var invoke = new changeStatus(ChangeStatus); - Invoke(invoke, val); - } - else - { - if (val == I18N.GetString("Ready")) - { - pBar.Visible = false; - dgvMain.Enabled = true; - tssStatus.Text = I18N.GetString("Ready"); - tssStatus.ForeColor = Color.Green; - } - else if (val == I18N.GetString("Wrong")) - { - pBar.Visible = false; - dgvMain.Enabled = true; - tssStatus.Text = I18N.GetString("Wrong"); - tssStatus.ForeColor = Color.Red; - } - else - { - pBar.Visible = true; - dgvMain.Enabled = false; - tssStatus.Text = val; - tssStatus.ForeColor = Color.Blue; - } - } - } - private delegate void modifyRow(int rowID, string ip,string loc, int max, int min, int avg, int failtime); - private void ModifyRow(int rowID, string ip,string loc, int max, int min, int avg, int failtime) - { - if (dgvMain.InvokeRequired) - { - var del = new modifyRow(ModifyRow); - Invoke(del, new object[] { rowID, ip,loc, max, min, avg, failtime }); - } - else - { - dgvMain.Rows[rowID].Cells[1].Value = string.IsNullOrEmpty(ip) ? I18N.GetString("PingFail") : ip; - dgvMain.Rows[rowID].Cells[4].Value = max; - dgvMain.Rows[rowID].Cells[5].Value = min; - dgvMain.Rows[rowID].Cells[6].Value = avg; - dgvMain.Rows[rowID].Cells[7].Value = failtime; - if (!string.IsNullOrEmpty(loc)) dgvMain.Rows[rowID].Cells[3].Value = loc; - } - } - - private delegate void modifyRowSpeed(int rowID,string ip, string loc,string speed); - - private void ModifyRowSpeed(int rowID, string ip, string loc, string speed) - { - if (dgvMain.InvokeRequired) - { - var del = new modifyRowSpeed(ModifyRowSpeed); - Invoke(del, new object[] {rowID, ip, loc, speed}); - } - else - { - dgvMain.Rows[rowID].Cells[1].Value = string.IsNullOrEmpty(ip) ? I18N.GetString("PingFail") : ip; - if (!string.IsNullOrEmpty(loc)) dgvMain.Rows[rowID].Cells[3].Value = loc; - if (!string.IsNullOrEmpty(speed)) dgvMain.Rows[rowID].Cells[8].Value = speed; - } - } - - #endregion - - #region Method - private void Ping(object r) - { - var row = r as DataGridViewRow; - if (row == null) return; - var addr = ""; - var result = new List(); - var failTime = 0; - - using (var ping = new Ping()) - { - for (var times = 0; times < 4; times++) - { - try - { - var reply = ping.Send((string)row.Cells[0].Value, 2000); - if (reply == null) { failTime++; continue;} - addr = reply.Address.ToString(); - if (reply.Status == IPStatus.Success) - { - result.Add(reply.RoundtripTime); - } - else - { - failTime++; - } - } - catch - { - failTime++; - } - } - } - var location = ""; - if (Util.Utils.qqwry != null & addr != "") - { - var v = Util.Utils.qqwry.SearchIPLocation(addr); - location = (v.country + v.area.Replace("CZ88.NET", "")); - } - if(result.Count == 0) - ModifyRow(row.Index, addr,location, 9999, 9999, 9999, 10); - else - ModifyRow(row.Index, addr,location, (int)result.Max(), (int)result.Min(), (int)result.Average(), failTime); - } - - private void Go(object rc) - { - var rows = rc as DataGridViewRowCollection; - if (rows != null) - { - foreach (DataGridViewRow row in rows) - Ping(row); - SelectMinMax(); - ChangeStatus(I18N.GetString("Ready")); - } - else - { - ChangeStatus(I18N.GetString("Wrong")); - } - } - - private void Test(object index) - { - try - { - var rowIndex = (int)index; - string ip, location; - float speed; - GetIPAddress(rowIndex, out ip, out location, out speed); - ModifyRowSpeed(rowIndex, ip, location, speed.ToString("N0") + "KB/s"); - ChangeStatus(I18N.GetString("Ready")); - } - catch - { - ChangeStatus(I18N.GetString("Wrong")); - } - } - - private void GetIPAddress(int svc, out string addr, out string stat, out float speed) - { - var currentIndex = controller.GetConfiguration().index; - controller.SelectServerIndexTemp(svc); - var webClient = new SocksWebClient { ProxyDetails = new ProxyDetails(controller.GetConfiguration().localPort)}; - - try - { - //get location - var regx1 = new Regex(@"\d+\.\d+\.\d+\.\d+"); - var regx2 = new Regex(@"来自:(.*?)\<"); - var response = webClient.DownloadString(@"http://1111.ip138.com/ic.asp"); - - var mc1 = regx1.Match(response); - addr = mc1.Success ? mc1.Value : I18N.GetString("Unknow"); - - var mc2 = regx2.Match(response); - stat = mc2.Success ? mc2.Groups[1].Value : I18N.GetString("…(⊙_⊙;)…"); - } - catch - { - addr = I18N.GetString("Unknow"); - stat = I18N.GetString("…(⊙_⊙;)…"); - } - - try - { - //speed test - var sw = System.Diagnostics.Stopwatch.StartNew(); - var dl = webClient.DownloadData("https://dl.google.com/tag/s/appguid%3D%7B8A69D345-D564-463C-AFF1-A69D9E530F96%7D%26iid%3D%7B7B1E2CBF-95F1-5FDD-C836-E5930E3E51CD%7D%26lang%3Den%26browser%3D4%26usagestats%3D0%26appname%3DGoogle%2520Chrome%26needsadmin%3Dprefers%26installdataindex%3Ddefaultbrowser/update2/installers/ChromeSetup.exe");//http://dl.google.com/googletalk/googletalk-setup.exe - sw.Stop(); - var len = dl.Length / 1024f; - var sec = sw.Elapsed.Milliseconds / 1000f; - speed = len / sec; - } - catch - { - speed = 0; - } - webClient.Dispose(); - controller.SelectServerIndexTemp(currentIndex); - } - #endregion - public PingForm(ShadowsocksController sc) - { - InitializeComponent(); - - var qqwryPath = Environment.CurrentDirectory + "\\qqwry.dat"; - if (Util.Utils.qqwry == null && File.Exists(qqwryPath)) Util.Utils.qqwry = new QQWry(qqwryPath); - - controller = sc; - - Font = Util.Utils.GetFont(); - - PerformLayout(); - - UpdateTexts(); - - Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); - - LoadConfiguration(controller.GetConfiguration()); - - if (dgvMain.Rows.Count <= 5) - { - foreach (var row in dgvMain.Rows) - { - var t = new Thread(Ping) { IsBackground = true }; - t.Start(row); - } - } - else - { - ChangeStatus(I18N.GetString("DoSomething")); - var t = new Thread(Go) { IsBackground = true }; - t.Start(dgvMain.Rows); - } - - } - - private void UpdateTexts() - { - foreach (DataGridViewColumn col in dgvMain.Columns) - col.HeaderText = I18N.GetString(col.Name); - Text = I18N.GetString("UsableTest"); - tssStatusLabel.Text = I18N.GetString("CurrentStatus:"); - tssStatus.Text = I18N.GetString("Ready"); - } - - - private void LoadConfiguration(Configuration configuration) - { - dgvMain.Rows.Clear(); - foreach (Server server in configuration.configs) - { - int index = dgvMain.Rows.Add(); - dgvMain.Rows[index].Cells[0].Value = server.server; - dgvMain.Rows[index].Cells[1].Value = I18N.GetString("Pinging"); - dgvMain.Rows[index].Cells[2].Value = server.remarks; - dgvMain.Rows[index].Cells[9].Value = I18N.GetString("TestSpeed"); - } - var row = dgvMain.Rows[configuration.index]; - dgvMain.ClearSelection(); - dgvMain.CurrentCell = row.Cells[0]; - row.Selected = true; - } - - - private void dgvMain_CellClick(object sender, DataGridViewCellEventArgs e) - { - if (dgvMain.Columns[e.ColumnIndex].Name != "TestSpeed") return; - ChangeStatus(I18N.GetString("DoSomething")); - var t = new Thread(Test) {IsBackground = true}; - t.Start(e.RowIndex); - //10sec time out because WebClient dont have Timeout property - var tm = new System.Timers.Timer(10000); - tm.Elapsed += (ss, ee) =>{ tm.Stop(); t.Abort(); }; - tm.Start(); - } - - private void dgvMain_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e) - { - if (e.RowIndex < 0) return; - if ((int) dgvMain.Rows[e.RowIndex].Cells[4].Value == 9999) - { if (MessageBox.Show(I18N.GetString("Seems your server is down, r u sure apply this server?"), "", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) return; } - controller.SelectServerIndex(e.RowIndex); - Close(); - } - } -} diff --git a/shadowsocks-csharp/View/QRCodeForm.Designer.cs b/shadowsocks-csharp/View/QRCodeForm.Designer.cs index ca0a0006..b1cfc65d 100755 --- a/shadowsocks-csharp/View/QRCodeForm.Designer.cs +++ b/shadowsocks-csharp/View/QRCodeForm.Designer.cs @@ -29,6 +29,7 @@ private void InitializeComponent() { this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.listBox1 = new System.Windows.Forms.ListBox(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.SuspendLayout(); // @@ -42,6 +43,19 @@ this.pictureBox1.TabIndex = 1; this.pictureBox1.TabStop = false; // + // listBox1 + // + this.listBox1.DisplayMember = "Value"; + this.listBox1.FormattingEnabled = true; + this.listBox1.ItemHeight = 12; + this.listBox1.Location = new System.Drawing.Point(224, 10); + this.listBox1.Name = "listBox1"; + this.listBox1.ScrollAlwaysVisible = true; + this.listBox1.Size = new System.Drawing.Size(227, 208); + this.listBox1.TabIndex = 2; + this.listBox1.ValueMember = "Key"; + this.listBox1.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged); + // // QRCodeForm // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); @@ -49,7 +63,8 @@ this.AutoSize = true; this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.BackColor = System.Drawing.Color.White; - this.ClientSize = new System.Drawing.Size(338, 274); + this.ClientSize = new System.Drawing.Size(457, 228); + this.Controls.Add(this.listBox1); this.Controls.Add(this.pictureBox1); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.MaximizeBox = false; @@ -67,5 +82,6 @@ #endregion private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.ListBox listBox1; } } \ No newline at end of file diff --git a/shadowsocks-csharp/View/QRCodeForm.cs b/shadowsocks-csharp/View/QRCodeForm.cs index 2741d4ff..41c8ae24 100755 --- a/shadowsocks-csharp/View/QRCodeForm.cs +++ b/shadowsocks-csharp/View/QRCodeForm.cs @@ -8,8 +8,10 @@ using System.Data; using System.Drawing; using System.IO; using System.IO.Compression; +using System.Linq; using System.Text; using System.Windows.Forms; +using Shadowsocks.Model; namespace Shadowsocks.View { @@ -30,8 +32,8 @@ namespace Shadowsocks.View string qrText = ssconfig; QRCode code = ZXing.QrCode.Internal.Encoder.encode(qrText, ErrorCorrectionLevel.M); ByteMatrix m = code.Matrix; - int blockSize = Math.Max(pictureBox1.Height / m.Height, 1); - Bitmap drawArea = new Bitmap((m.Width * blockSize), (m.Height * blockSize)); + int blockSize = Math.Max(pictureBox1.Height/m.Height, 1); + Bitmap drawArea = new Bitmap((m.Width*blockSize), (m.Height*blockSize)); using (Graphics g = Graphics.FromImage(drawArea)) { g.Clear(Color.White); @@ -43,7 +45,7 @@ namespace Shadowsocks.View { if (m[row, col] != 0) { - g.FillRectangle(b, blockSize * row, blockSize * col, blockSize, blockSize); + g.FillRectangle(b, blockSize*row, blockSize*col, blockSize, blockSize); } } } @@ -54,7 +56,20 @@ namespace Shadowsocks.View private void QRCodeForm_Load(object sender, EventArgs e) { - GenQR(code); + var servers = Configuration.Load(); + var serverDatas = servers.configs.Select( + server => + new KeyValuePair(ShadowsocksController.GetQRCode(server), server.FriendlyName()) + ).ToList(); + listBox1.DataSource = serverDatas; + + var selectIndex = serverDatas.FindIndex(serverData => serverData.Key.StartsWith(code)); + if (selectIndex >= 0) listBox1.SetSelected(selectIndex, true); + } + + private void listBox1_SelectedIndexChanged(object sender, EventArgs e) + { + GenQR((sender as ListBox)?.SelectedValue.ToString()); } } } diff --git a/shadowsocks-csharp/View/QRCodeForm.resx b/shadowsocks-csharp/View/QRCodeForm.resx index 5ea0895e..29dcb1b3 100755 --- a/shadowsocks-csharp/View/QRCodeForm.resx +++ b/shadowsocks-csharp/View/QRCodeForm.resx @@ -112,9 +112,9 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 \ No newline at end of file diff --git a/shadowsocks-csharp/View/QRCodeSplashForm.cs b/shadowsocks-csharp/View/QRCodeSplashForm.cs index ac9828e4..3bf48888 100755 --- a/shadowsocks-csharp/View/QRCodeSplashForm.cs +++ b/shadowsocks-csharp/View/QRCodeSplashForm.cs @@ -242,7 +242,6 @@ namespace Shadowsocks.View SetBitmap(bitmap, 255); } - /// Changes the current bitmap with a custom opacity level. Here is where all happens! public void SetBitmap(Bitmap bitmap, byte opacity) { @@ -288,7 +287,6 @@ namespace Shadowsocks.View } } - protected override CreateParams CreateParams { get diff --git a/shadowsocks-csharp/app.config b/shadowsocks-csharp/app.config index a7ba1069..867ff468 100755 --- a/shadowsocks-csharp/app.config +++ b/shadowsocks-csharp/app.config @@ -1,19 +1,6 @@ - + - - - - - - - - - - - - - - - - + + + diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index fc6239c2..8200aa07 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -1,70 +1,75 @@  - Debug AnyCPU - {F58374A5-9AFB-430A-AF20-C509D3DCED3F} + 9.0.21022 + 2.0 + {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} WinExe Properties Shadowsocks Shadowsocks v4.0 512 + + + shadowsocks.ico + false + + + + + 3.5 + Client + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 1 + 1.0.0.%2a + false + true - - x86 + true + bin\x86\Debug\ + TRACE;DEBUG full - false - bin\Debug\ - DEBUG;TRACE + x86 prompt - 4 + ManagedMinimumRules.ruleset + false - - x86 - pdbonly - true - bin\Release\ + + bin\x86\Release\ TRACE + true + pdbonly + x86 prompt - 4 + ManagedMinimumRules.ruleset + false + true - shadowsocks.ico + app.manifest - - - - - - - + - - - - - - - - - - - - - Component - - - @@ -111,38 +116,45 @@ + - + + + + - + - - + - - - - - - + + + - - + + - - + + + True + True + Resources.resx + + + + Form @@ -150,13 +162,19 @@ ConfigForm.cs - - + + + + + + + Form - - PingForm.cs + + LogForm.cs + Form @@ -166,42 +184,73 @@ Form + + ConfigForm.cs + Designer + ResXFileCodeGenerator - Resources.Designer.cs Designer + Resources.Designer.cs - - True - Resources.resx - True - - - ConfigForm.cs - - - PingForm.cs + + LogForm.cs QRCodeForm.cs - + + Designer + - + + + + + + - + - - - - + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + false + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + true + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + +