102 Commits
v2.2 ... v3.2

Author SHA1 Message Date
arraykeys@gmail.com
8bafb88bc4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-16 20:25:10 +08:00
arraykeys@gmail.com
dc82b94c6b Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-16 20:10:22 +08:00
arraykeys@gmail.com
cf6043b0de Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-16 20:05:16 +08:00
arraykeys@gmail.com
ce1095d6de Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-16 20:00:16 +08:00
arraykeys@gmail.com
9f08170cd3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-14 16:52:06 +08:00
arraykeys@gmail.com
5c66f5f5d2 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-12 12:35:43 +08:00
arraykeys@gmail.com
1a9c3244a3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-11 16:54:27 +08:00
arraykeys@gmail.com
00688bbf33 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-11 15:00:06 +08:00
arraykeys@gmail.com
787cc56ed4 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-11 14:59:32 +08:00
arraykeys@gmail.com
3984083e23 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 13:17:55 +08:00
arraykeys@gmail.com
a91790b16d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 13:14:22 +08:00
arraykeys@gmail.com
b2549e8d48 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 13:13:31 +08:00
arraykeys@gmail.com
768e5dd6c0 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 11:07:57 +08:00
arraykeys@gmail.com
7899f1ec00 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 10:12:24 +08:00
arraykeys@gmail.com
ef93946fb1 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-10 10:08:53 +08:00
arraykeys@gmail.com
675061fd63 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 20:15:24 +08:00
arraykeys@gmail.com
4b212eee0d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:54:15 +08:00
arraykeys@gmail.com
0f81d5e503 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:24:35 +08:00
arraykeys@gmail.com
6616f4f860 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:23:53 +08:00
arraykeys@gmail.com
357a8745de Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:23:23 +08:00
arraykeys@gmail.com
cde72c04df Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:20:39 +08:00
arraykeys@gmail.com
a8767b0e15 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 16:07:05 +08:00
arraykeys@gmail.com
785762ceb9 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:59:25 +08:00
arraykeys@gmail.com
7383aa0973 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:57:49 +08:00
arraykeys@gmail.com
7d992082fa Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:47:10 +08:00
arraykeys@gmail.com
1d41eadd0b Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:46:29 +08:00
arraykeys@gmail.com
7b247384ec Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:33:09 +08:00
arraykeys@gmail.com
f270885a4d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-10-09 15:11:34 +08:00
arraykeys
5fa000f7e6 no message 2017-10-07 17:31:31 +08:00
arraykeys
ae56bb1edd no message 2017-10-04 12:44:51 +08:00
arraykeys
644ec6891d no message 2017-10-02 15:58:12 +08:00
arraykeys@gmail.com
db729915ad Merge branch 'master' of git@github.com:snail007/goproxy.git 2017-09-30 19:01:02 +08:00
arraykeys@gmail.com
69fcc7d12e Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-30 19:00:46 +08:00
snail007
e90852a401 Update README.md 2017-09-29 14:47:32 +08:00
snail007
71b9940916 Update README.md 2017-09-29 12:55:51 +08:00
snail007
175272744d Update README.md 2017-09-29 12:54:40 +08:00
arraykeys@gmail.com
635e107d66 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-28 16:27:50 +08:00
arraykeys@gmail.com
44dff9a3a9 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-28 16:18:25 +08:00
arraykeys@gmail.com
22298fbb97 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-28 16:00:46 +08:00
arraykeys@gmail.com
7221bb36b3 Merge branch 'master' of git@github.com:snail007/goproxy.git 2017-09-28 15:50:52 +08:00
arraykeys@gmail.com
5b3a5908a3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-28 15:50:31 +08:00
snail007
9bbb930323 Update README.md 2017-09-27 19:35:29 +08:00
arraykeys@gmail.com
54ce7dbde8 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 18:24:02 +08:00
arraykeys@gmail.com
92fb704ad1 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 18:19:34 +08:00
arraykeys@gmail.com
b9b79c2a65 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 17:58:11 +08:00
arraykeys@gmail.com
b4178a0f78 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 17:53:49 +08:00
arraykeys@gmail.com
92b2986889 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 17:52:57 +08:00
arraykeys@gmail.com
2d1a7faafb Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 17:48:50 +08:00
arraykeys@gmail.com
a9ad777757 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 17:45:49 +08:00
arraykeys@gmail.com
60cb8815a0 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 17:43:21 +08:00
arraykeys@gmail.com
b7d1085500 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 17:35:29 +08:00
arraykeys@gmail.com
0449464b3f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 15:59:09 +08:00
arraykeys@gmail.com
7b00570f55 Merge branch 'master' of git@github.com:snail007/goproxy.git 2017-09-27 15:58:17 +08:00
arraykeys@gmail.com
919a5f9c60 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 15:57:57 +08:00
snail007
c957e14733 Saved README.md with Dillinger.io 2017-09-27 15:56:36 +08:00
arraykeys@gmail.com
c68e15ca3d Merge branch 'master' of git@github.com:snail007/goproxy.git
# Conflicts:
#	README.md

Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
2017-09-27 15:55:15 +08:00
arraykeys@gmail.com
4578148ab9 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 15:54:33 +08:00
snail007
0503ee9a96 Update README.md 2017-09-27 15:51:25 +08:00
snail007
1baafc08cc Update README.md 2017-09-27 15:50:44 +08:00
snail007
39daac6de4 Update README.md 2017-09-27 15:48:35 +08:00
snail007
03d2b1777b Saved README.md with Dillinger.io [skip ci] 2017-09-27 15:47:35 +08:00
arraykeys@gmail.com
22aeb7aaf3 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-27 14:22:24 +08:00
arraykeys@gmail.com
f985e24109 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-25 18:55:12 +08:00
arraykeys@gmail.com
a99898729f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-25 15:52:36 +08:00
arraykeys@gmail.com
3f7b57740d Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-25 15:31:07 +08:00
arraykeys@gmail.com
b17c09aa1e Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-22 19:07:31 +08:00
snail007
9e2849f967 Update main.go 2017-09-20 20:43:07 +08:00
snail007
e665ef0d4b Update main.go 2017-09-20 20:39:30 +08:00
snail007
f514e54d65 Update main.go 2017-09-20 20:29:07 +08:00
arraykeys@gmail.com
30b088a13f Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-20 19:35:36 +08:00
arraykeys@gmail.com
4ce5a461f4 Merge branch 'master' of git@github.com:snail007/goproxy.git 2017-09-20 19:28:56 +08:00
arraykeys@gmail.com
aff38118e5 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-20 19:28:36 +08:00
snail007
31d096e808 Update README.md 2017-09-19 17:30:57 +08:00
snail007
62d9c10baf Update README.md 2017-09-19 17:20:30 +08:00
arraykeys@gmail.com
09242575d8 Merge branch 'master' of git@github.com:snail007/goproxy.git 2017-09-19 17:16:37 +08:00
arraykeys@gmail.com
763652cb01 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-19 17:16:16 +08:00
snail007
4a2f606f3a Update README.md 2017-09-19 17:12:15 +08:00
snail007
9ce4ce77d0 Update README.md 2017-09-19 16:21:59 +08:00
snail007
b1d88ac2d4 Update README.md 2017-09-19 16:21:41 +08:00
snail007
3345b3a570 Update README.md 2017-09-19 16:20:57 +08:00
snail007
a48a87f17b Update README.md 2017-09-19 16:20:39 +08:00
snail007
a702691460 Update README.md 2017-09-19 16:20:10 +08:00
snail007
d5491cb7ef Update README.md 2017-09-19 16:19:50 +08:00
snail007
67e48b4003 Update README.md 2017-09-19 16:19:34 +08:00
snail007
cc3bafa07c Update README.md 2017-09-19 16:19:18 +08:00
snail007
ce67266f5f Update README.md 2017-09-19 16:19:06 +08:00
snail007
a9b51bb6c3 Update README.md 2017-09-19 16:18:41 +08:00
snail007
cb48a912e9 Update README.md 2017-09-19 16:18:05 +08:00
snail007
01d4afe9ef Update README.md 2017-09-19 16:17:12 +08:00
snail007
3a49ba7e0e Update README.md 2017-09-19 16:16:51 +08:00
snail007
b04675d62f Update README.md 2017-09-19 16:15:37 +08:00
arraykeys@gmail.com
6bd396a70b Merge branch 'master' of git@github.com:snail007/goproxy.git 2017-09-19 16:14:02 +08:00
arraykeys@gmail.com
455d0006d7 Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com> 2017-09-19 16:13:51 +08:00
snail007
ebb5f5d229 Update README.md 2017-09-19 16:10:03 +08:00
snail007
73ad912cd1 Create LICENSE 2017-09-19 16:06:42 +08:00
snail007
b559cf0500 Delete LICENSE 2017-09-19 16:06:13 +08:00
snail007
ae3f69a5be Update README.md 2017-09-19 16:04:33 +08:00
snail007
8a2e470f0a Update README.md 2017-09-19 16:03:35 +08:00
snail007
6d9134aa25 Update README.md 2017-09-19 16:02:28 +08:00
snail007
ec7adea194 Update README.md 2017-09-19 15:58:18 +08:00
snail007
a77b7fda7b Update README.md 2017-09-19 15:57:59 +08:00
snail007
55d78f678d Update README.md 2017-09-19 15:56:33 +08:00
37 changed files with 10312 additions and 127 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
proxy
*.exe
*.exe~
.*
release-*
proxy.crt
proxy.key

46
CHANGELOG Normal file
View File

@ -0,0 +1,46 @@
proxy更新日志
v3.2
1.内网穿透功能server端-r参数增加了协议和key设置.
2.手册增加了对-r参数的详细说明.
3.修复了普通模式也检查证书文件的bug.
4.增加了Socks5支持,目前只支持TCP协议,不支持UDP协议.
5.Socks5上级代理支持ssh中转,linux服务器不需要任何服务端,本地一个proxy即可开心上网.
6.http(s)代理增加了ssh中转支持,linux服务器不需要任何服务端,本地一个proxy即可开心上网.
v3.1
1.优化了内网穿透功能,bridge,client和server只需要启动一个即可。
server端启动的时候可以指定client端要暴露的一个或者多个端口。
2.修复了重复解析命令行参数的问题。
3.手册增加了微信接口本地开发的示例。
4.增加了配置文件使用说明.
v3.0
1.此次更新不兼容2.x版本,重构了全部代码,架构更合理,利于功能模块的增加与维护。
2.增加了代理死循环检查,增强了安全性。
3.增加了反向代理模式(即:内网穿透),支持TCP和UDP两种协议,可以把任何局域网的机器A所在网络的任何端。
暴露到任何局域网的机器B的本地端口或暴露到任何公网VPS上。
4.正向代理增加了UDP模式支持。
v2.2
1.增加了强制使用上级代理参数always.可以使所有流量都走上级代理。
2.增加了定时检查网络是否正常,可以在本地网络不稳定的时候修复连接池状态,提升代理访问体验。
3.http代理增加了对ipv6地址的支持。
v2.1
1.增加了http basic验证功能,可以对http代理协议设置basic验证,用户名和密码支持来自文件或者命令行。
2.优化了域名检查方法,避免空连接的出现。
3.修复了连接上级代理超时参数传递错误导致超时过大的问题。
4.增加了连接池状态监测,如果上级代理或者网络出现问题,会及时重新初始化连接池,防止大量无效连接,降低浏览体验。
5.增加了对系统kill信号的捕获,可以在收到系统kill信号之后执行清理释放连接的操作.避免出现大量CLOSE_WAIT。
v2.0
1.增加了连接池功能,大幅提高了通过上级代理访问的速度。
2.HTTP代理模式,优化了请求URL的获取逻辑,可以支持:http,https,websocke。
3.增加了TCP代理模式,支持是否加密通讯。
4.优化了链接关闭逻辑,避免出现大量CLOSE_WAIT。
5.增加了黑白名单机制,更自由快速的访问。
6.优化了网站Block机制检测,判断更准确。
v1.0
1.始发版本,可以代理http,https。

687
LICENSE
View File

@ -1,21 +1,674 @@
MIT License
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (c) 2017 snail007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
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:
Preamble
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
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.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. 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
them 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 prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. 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.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey 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;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If 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 convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU 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 that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
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.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
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.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
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
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
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 3 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, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
This program 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, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU 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. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

486
README.md
View File

@ -1,66 +1,440 @@
# goproxy
[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy/)
[![license](https://img.shields.io/github/license/snail007/goproxy.svg?style=plastic)]()
[![download_count](https://img.shields.io/github/downloads/snail007/goproxy/total.svg?style=plastic)](https://github.com/snail007/goproxy/releases)
[![download](https://img.shields.io/github/release/snail007/goproxy.svg?style=plastic)](https://github.com/snail007/goproxy/releases)
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/logo.jpg?raw=true" width="200"/>
Proxy是golang实现的高性能http,https,websocket,tcp,udp代理服务器,支持正向代理和内网穿透.
---
[![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy/) [![license](https://img.shields.io/github/license/snail007/goproxy.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy/total.svg?style=plastic)](https://github.com/snail007/goproxy/releases) [![download](https://img.shields.io/github/release/snail007/goproxy.svg?style=plastic)](https://github.com/snail007/goproxy/releases)
### Features
- 链式代理,程序本身可以作为一级代理,如果设置了上级代理那么可以作为二级代理,乃至N级代理.
- 通讯加密,如果程序不是一级代理,而且上级代理也是本程序,那么可以加密和上级代理之间的通讯,采用底层tls高强度加密,安全无特征.
- 智能HTTP代理,会自动判断访问的网站是否屏蔽,如果被屏蔽那么就会使用上级代理(前提是配置了上级代理)访问网站;如果访问的网站没有被屏蔽,为了加速访问,代理会直接访问网站,不使用上级代理.
- 域名黑白名单,更加自由的控制网站的访问方式。
- 跨平台性,无论你是widows,linux,还是mac,甚至是树莓派,都可以很好的运行proxy.
- 多协议支持,支持HTTP(S),TCP,UDP,Websocket,SOCKS5代理.
- 支持内网穿透,协议支持TCP和UDP.
- HTTP(S),SOCKS5代理支持SSH中转,上级Linux服务器不需要任何服务端,本地一个proxy即可开心上网.
### Why need these?
- 当由于安全因素或者限制,我们不能顺畅的访问我们在其它地方的服务,我们可以通过多个相连的proxy节点建立起一个安全的隧道,顺畅的访问我们的服务.
- 微信接口本地开发,方便调试.
- 远程访问内网机器.
- 和小伙伴一起玩局域网游戏.
- 以前只能在局域网玩的,现在可以在任何地方玩.
- 替代圣剑内网通显IP内网通花生壳之类的工具.
- ...  
# 30秒简介
proxy是golang实现的高性能http,https,websocket,tcp代理服务器.程序本身可以作为一级代理,如果设置了上级代理那么可以作为二级代理,乃至N级代理.如果程序不是一级代理,而且上级代理也是本程序,那么可以加密和上级代理之间的通讯,采用底层tls高强度加密,安全无特征.代理时会自动判断访问的网站是否屏蔽,如果被屏蔽那么就会使用上级代理(前提是配置了上级代理)访问网站;如果访问的网站没有被屏蔽,为了加速访问,代理会直接访问网站,不使用上级代理.
[图文教程](docs/faststart.md)
# 快速使用:
### 手册目录
本页是最新v3.2手册,其他版本手册请点击下面链接查看.
- [v3.1手册](https://github.com/snail007/goproxy/tree/v3.1)
- [v3.0手册](https://github.com/snail007/goproxy/tree/v3.0)
- [v2.x手册](https://github.com/snail007/goproxy/tree/v2.2)
### Fast Start
提示:所有操作需要root权限.
0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.
#curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash
**0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.**
```shell
curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash
```
安装完成,配置目录是/etc/proxy,更详细的使用方法参考下面的进一步了解.
如果你的vps不是linux64位系统,请按照下面的半自动步骤安装:
1.登录你的VPS,下载守护进程monexec,选择合适你的版本,vps一般选择"linux_amd64.tar.gz"的即可.
下载地址:https://github.com/reddec/monexec/releases
如果安装失败或者你的vps不是linux64位系统,请按照下面的半自动步骤安装:
**1.登录你的VPS,下载守护进程monexec,选择合适你的版本,vps一般选择"linux_amd64.tar.gz"的即可.**
下载地址:https://github.com/reddec/monexec/releases
比如下载到/root/proxy/
执行:
#mkdir /root/proxy/
#cd /root/proxy/
#wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
2.下载proxy
下载地址:https://github.com/snail007/goproxy/releases
#cd /root/proxy/
#wget https://github.com/snail007/goproxy/releases/download/v2.0/proxy-linux-amd64.tar.gz
3.下载自动安装脚本
#cd /root/proxy/
#wget https://raw.githubusercontent.com/snail007/goproxy/master/install.sh
#chmod +x install.sh
#./install.sh
```shell
mkdir /root/proxy/
cd /root/proxy/
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
```
**2.下载proxy**
下载地址:https://github.com/snail007/goproxy/releases
```shell
cd /root/proxy/
wget https://github.com/snail007/goproxy/releases/download/v3.1fix/proxy-linux-amd64.tar.gz
```
**3.下载自动安装脚本**
```shell
cd /root/proxy/
wget https://raw.githubusercontent.com/snail007/goproxy/master/install.sh
chmod +x install.sh
./install.sh
```
## 使用教程
**提示**
接下来的教程,默认系统是linux,程序是proxy所有操作需要root权限
如果你的是windows,请使用windows版本的proxy.exe即可.
**使用配置文件**
接下来的教程都是通过命令行参数介绍使用方法,也可以通过读取配置文件获取参数.
具体格式是通过@符号指定配置文件,例如:./proxy @configfile.txt
configfile.txt里面的格式是,第一行是子命令名称,第二行开始一行一个:参数的长格式=参数值,前后不能有空格和双引号.
参数的长格式都是--开头的,短格式参数都是-开头,如果你不知道某个短格式参数对应长格式参数,查看帮助命令即可.
比如configfile.txt内容如下:
```shell
http
--local-type=tcp
--local=:33080
```
### 0.生成加密通讯需要的证书文件
http,tcp,udp代理过程会和上级通讯,为了安全我们采用加密通讯,当然可以选择不加密通信通讯,本教程所有和上级通讯都采用加密,需要证书文件.
在linux上并安装了openssl命令可以直接通过下面的命令生成证书和key文件.
`./proxy keygen`
默认会在当前程序目录下面生成证书文件proxy.crt和key文件proxy.key。
### 1.HTTP代理
**1.1.普通HTTP代理**
`./proxy http -t tcp -p "0.0.0.0:38080"`
**1.2.普通二级HTTP代理**
使用本地端口8090,假设上级HTTP代理是`22.22.22.22:8080`
`./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
默认关闭了连接池,如果要加快访问速度,-L可以开启连接池,10就是连接池大小,0为关闭,
开启连接池在网络不好的情况下,稳定不是很好.
`./proxy http -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -L 10`
我们还可以指定网站域名的黑白名单文件,一行一个域名,怕匹配规则是最右批评匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理.
`./proxy http -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
**1.3.HTTP二级代理(加密)**
一级HTTP代理(VPS,IP:22.22.22.22)
`./proxy http -t tls -p ":38080" -C proxy.crt -K proxy.key`
二级HTTP代理(本地Linux)
`./proxy http -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
那么访问本地的8080端口就是访问VPS上面的代理端口38080.
二级HTTP代理(本地windows)
`./proxy.exe http -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
然后设置你的windos系统中需要通过代理上网的程序的代理为http模式地址为127.0.0.1端口为8080,程序即可通过加密通道通过vps上网。
**1.4.HTTP三级代理(加密)**
一级HTTP代理VPS_01,IP:22.22.22.22
`./proxy http -t tls -p ":38080" -C proxy.crt -K proxy.key`
二级HTTP代理VPS_02,IP:33.33.33.33
`./proxy http -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
三级HTTP代理(本地)
`./proxy http -t tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地的8080端口就是访问一级HTTP代理上面的代理端口38080.
**1.5.Basic认证**
对于代理HTTP协议我们可以basic进行Basic认证,认证的用户名和密码可以在命令行指定
`./proxy http -t tcp -p ":33080" -a "user1:pass1" -a "user2:pass2"`
多个用户,重复-a参数即可.
也可以放在文件中,格式是一行一个"用户名:密码",然后用-F指定.
`./proxy http -t tcp -p ":33080" -F auth-file.txt`
如果没有-a或-F参数,就是关闭Basic认证.
**1.6.HTTP代理流量强制走上级HTTP代理**
默认情况下,proxy会智能判断一个网站域名是否无法访问,如果无法访问才走上级HTTP代理.通过--always可以使全部HTTP代理流量强制走上级HTTP代理.
`./proxy http --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
**1.7.HTTP(S)通过SSH中转**
说明:ssh中转的原理是利用了ssh的转发功能,就是你连接上ssh之后,可以通过ssh代理访问目标地址.
假设有:vps
- IP是2.2.2.2, ssh端口是22, ssh用户名是:user, ssh用户密码是:demo
- 用户user的ssh私钥名称是user.key
# 进一步了解:
1、作为普通一级代理。
默认监听0.0.0.0:33080端口可以使用-p修改端口-i修改绑定ip。
默认情况
./proxy
指定ip和端口
./proxy -i 192.168.1.100 -p 60080
***1.7.1 ssh用户名和密码的方式***
本地HTTP(S)代理28080端口,执行:
`./proxy http -T ssh -P "2.2.2.2:22" -u user -A demo -t tcp -p ":28080"`
***1.7.2 ssh用户名和密钥的方式***
本地HTTP(S)代理28080端口,执行:
`./proxy http -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"`
2、作为普通二级代理。
可以通过-P指定上级代理格式是IP:端口
./proxy -P "192.168.1.100:60080" -p 33080
**1.8.查看帮助**
`./proxy help http`
3、作为加密一级代理。
加密模式的一级代理需要和加密的二级代理配合。
加密模式需要证书和key文件在linux上并安装了openssl命令可以直接通过下面的命令生成证书和key文件。
./proxy keygen
会在当前目录下面生成一个证书文件proxy.crt和key文件proxy.key。
比如在你的vps上运行加密一级代理使用参数-x即可默认会使用程序相同目录下面的证书文件proxy.crt和key文件proxy.key。
./proxy -x
或者使用-c和-k指定证书和key文件,ip和端口。
./proxy -x -c "proxy.crt" -k "proxy.key" -p 58080
### 2.TCP代理
**2.1.普通一级TCP代理**
本地执行:
`./proxy tcp -p ":33080" -T tcp -P "192.168.22.33:22" -L 0`
那么访问本地33080端口就是访问192.168.22.33的22端口.
**2.2.普通二级TCP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp -p ":33080" -T tcp -P "127.0.0.1:8080" -L 0`
本地执行:
`./proxy tcp -p ":23080" -T tcp -P "22.22.22.33:33080"`
那么访问本地23080端口就是访问22.22.22.33的8080端口.
**2.3.普通三级TCP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp -p ":38080" -T tcp -P "66.66.66.66:8080" -L 0`
二级TCP代理VPS_02,IP:33.33.33.33
`./proxy tcp -p ":28080" -T tcp -P "22.22.22.22:38080"`
三级TCP代理(本地)
`./proxy tcp -p ":8080" -T tcp -P "33.33.33.33:28080"`
那么访问本地8080端口就是通过加密TCP隧道访问66.66.66.66的8080端口.
**2.4.加密二级TCP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp --tls -p ":33080" -T tcp -P "127.0.0.1:8080" -L 0 -C proxy.crt -K proxy.key`
本地执行:
`./proxy tcp -p ":23080" -T tls -P "22.22.22.33:33080" -C proxy.crt -K proxy.key`
那么访问本地23080端口就是通过加密TCP隧道访问22.22.22.33的8080端口.
**2.5.加密三级TCP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp --tls -p ":38080" -T tcp -P "66.66.66.66:8080" -C proxy.crt -K proxy.key`
二级TCP代理VPS_02,IP:33.33.33.33
`./proxy tcp --tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
三级TCP代理(本地)
`./proxy tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地8080端口就是通过加密TCP隧道访问66.66.66.66的8080端口.
**2.6.查看帮助**
`./proxy help tcp`
### 3.UDP代理
**3.1.普通一级UDP代理**
本地执行:
`./proxy udp -p ":5353" -T udp -P "8.8.8.8:53"`
那么访问本地UDP:5353端口就是访问8.8.8.8的UDP:53端口.
**3.2.普通二级UDP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp -p ":33080" -T udp -P "8.8.8.8:53"`
本地执行:
`./proxy udp -p ":5353" -T tcp -P "22.22.22.33:33080"`
那么访问本地UDP:5353端口就是通过TCP隧道,通过VPS访问8.8.8.8的UDP:53端口.
**3.3.普通三级UDP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp -p ":38080" -T udp -P "8.8.8.8:53"`
二级TCP代理VPS_02,IP:33.33.33.33
`./proxy tcp -p ":28080" -T tcp -P "22.22.22.22:38080"`
三级TCP代理(本地)
`./proxy udp -p ":5353" -T tcp -P "33.33.33.33:28080"`
那么访问本地5353端口就是通过TCP隧道,通过VPS访问8.8.8.8的53端口.
**3.4.加密二级UDP代理**
VPS(IP:22.22.22.33)执行:
`./proxy tcp --tls -p ":33080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key`
本地执行:
`./proxy udp -p ":5353" -T tls -P "22.22.22.33:33080" -C proxy.crt -K proxy.key`
那么访问本地UDP:5353端口就是通过加密TCP隧道,通过VPS访问8.8.8.8的UDP:53端口.
**3.5.加密三级UDP代理**
一级TCP代理VPS_01,IP:22.22.22.22
`./proxy tcp --tls -p ":38080" -T udp -P "8.8.8.8:53" -C proxy.crt -K proxy.key`
二级TCP代理VPS_02,IP:33.33.33.33
`./proxy tcp --tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
三级TCP代理(本地)
`./proxy udp -p ":5353" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地5353端口就是通过加密TCP隧道,通过VPS_01访问8.8.8.8的53端口.
**3.6.查看帮助**
`./proxy help udp`
### 4.内网穿透
**4.1、原理说明**
内网穿透,由三部分组成:client端,server端,bridge端client和server主动连接bridge端进行桥接.
当用户访问server端,流程是:
1. server主动和bridge端建立连接
1. 然后bridge端通知client端连接bridge端,并连接内网目标端口;
1. 然后绑定client端到bridge端和client端到内网端口的连接
1. 然后bridge端把client过来的连接与server端过来的连接绑定
1. 整个通道建立完成;
**4.2、TCP普通用法**
背景:
- 公司机器A提供了web服务80端口
- 有VPS一个,公网IP:22.22.22.22
- 本用法典型案例就是微信接口本地开发  
4、作为加密二级代理。
加密模式的二级代理需要和加密的一级代理配合。加密模式的二级代理和加密模式的一级代理要使用相同的证书和key文件。
默认会使用程序相同目录下面的证书文件proxy.crt和key文件proxy.key。
比如在你的windows电脑上允许二级加密代理,需要-P指定上级代理同时设置-X代表是加密的上级代理。
假设一级代理vps外网IP是115.34.9.63。  
./proxy.exe -X -P "115.34.9.63:58080" -c "proxy.crt" -k "proxy.key" -p 18080
然后设置你的windos系统中需要通过代理上网的程序的代理为http模式地址为127.0.0.1端口为18080
然后程序即可通过加密通道通过vps上网。
需求:
在家里能够通过访问VPS的28080端口访问到公司机器A的80端口
步骤:
1. 在vps上执行
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
`./proxy tserver -r ":28080@:80" -P "127.0.0.1:33080" -C proxy.crt -K proxy.key`
1. 在公司机器A上面执行
`./proxy tclient -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
任何使用问题欢迎邮件交流arraykeys@gmail.com
1. 完成
**4.3、微信接口本地开发**
背景:
- 自己的笔记本提供了nginx服务80端口
- 有VPS一个,公网IP:22.22.22.22
需求:
在微信的开发帐号的网页回调接口配置里面填写地址:http://22.22.22.22/calback.php
然后就可以访问到笔记本的80端口下面的calback.php,如果需要绑定域名,可以用自己的域名
比如:wx-dev.xxx.com解析到22.22.22.22,然后在自己笔记本的nginx里
配置域名wx-dev.xxx.com到具体的目录即可.
步骤:
1. 在vps上执行,确保vps的80端口没被其它程序占用.
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
`./proxy tserver -r ":80@:80" -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 在自己笔记本上面执行
`./proxy tclient -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 完成
**4.4、UDP普通用法**
背景:
- 公司机器A提供了DNS解析服务,UDP:53端口
- 有VPS一个,公网IP:22.22.22.22
需求:
在家里能够通过设置本地dns为22.22.22.22,使用公司机器A进行域名解析服务.
步骤:
1. 在vps上执行
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
`./proxy tserver --udp -r ":53@:53" -P "127.0.0.1:33080" -C proxy.crt -K proxy.key`
1. 在公司机器A上面执行
`./proxy tclient --udp -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 完成
**4.5、高级用法一**
背景:
- 公司机器A提供了web服务80端口
- 有VPS一个,公网IP:22.22.22.22
需求:
为了安全,不想在VPS上能够访问到公司机器A,在家里能够通过访问本机的28080端口,
通过加密隧道访问到公司机器A的80端口.
步骤:
1. 在vps上执行
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
1. 在公司机器A上面执行
`./proxy tclient -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 在家里电脑上执行
`./proxy tserver -r ":28080@:80" -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 完成
**4.6、高级用法二**
提示:
如果同时有多个client连接到同一个bridge,需要指定不同的key,可以通过--k参数设定,--k可以是任意唯一字符串,
只要在同一个bridge上唯一即可.
server连接到bridge的时候,如果同时有多个client连接到同一个bridge,需要使用--k参数选择client.
暴露多个端口重复-r参数即可.-r格式是:"本地IP:本地端口@clientHOST:client端口".
背景:
- 公司机器A提供了web服务80端口,ftp服务21端口
- 有VPS一个,公网IP:22.22.22.22
需求:
在家里能够通过访问VPS的28080端口访问到公司机器A的80端口
在家里能够通过访问VPS的29090端口访问到公司机器A的21端口
步骤:
1. 在vps上执行
`./proxy tbridge -p ":33080" -C proxy.crt -K proxy.key`
`./proxy tserver -r ":28080@:80" -r ":29090@:21" --k test -P "127.0.0.1:33080" -C proxy.crt -K proxy.key`
1. 在公司机器A上面执行
`./proxy tclient --k test -P "22.22.22.22:33080" -C proxy.crt -K proxy.key`
1. 完成
**4.7.tserver的-r参数**
-r完整格式是:`PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT`
4.7.1.协议PROTOCOL:tcp或者udp.
比如: `-r "udp://:10053@:53" -r "tcp://:10800@:1080" -r ":8080@:80"`
如果指定了--udp参数,PROTOCOL默认为udp,那么:`-r ":8080@:80"`默认为udp;
如果没有指定--udp参数,PROTOCOL默认为tcp,那么:`-r ":8080@:80"`默认为tcp;
4.7.2.CLIENT_KEY:默认是default.
比如: -r "udp://:10053@[test1]:53" -r "tcp://:10800@[test2]:1080" -r ":8080@:80"
如果指定了--k参数,比如--k test,那么:`-r ":8080@:80"`CLIENT_KEY默认为test;
如果没有指定--k参数,那么:`-r ":8080@:80"`CLIENT_KEY默认为default;
4.7.3.LOCAL_IP为空默认是:`0.0.0.0`,CLIENT_LOCAL_HOST为空默认是:`127.0.0.1`;
**4.8.查看帮助**
`./proxy help tbridge`
`./proxy help tserver`
`./proxy help tserver`
### 5.SOCKS5代理
提示:SOCKS5代理,只支持TCP协议,不支持UDP协议,不支持用户名密码认证.
**5.1.普通SOCKS5代理**
`./proxy socks -t tcp -p "0.0.0.0:38080"`
**5.2.普通二级SOCKS5代理**
使用本地端口8090,假设上级SOCKS5代理是`22.22.22.22:8080`
`./proxy socks -t tcp -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" `
我们还可以指定网站域名的黑白名单文件,一行一个域名,怕匹配规则是最右批评匹配,比如:baidu.com,匹配的是*.*.baidu.com,黑名单的域名域名直接走上级代理,白名单的域名不走上级代理.
`./proxy socks -p "0.0.0.0:8090" -T tcp -P "22.22.22.22:8080" -b blocked.txt -d direct.txt`
**5.3.SOCKS二级代理(加密)**
一级SOCKS代理(VPS,IP:22.22.22.22)
`./proxy socks -t tls -p ":38080" -C proxy.crt -K proxy.key`
二级SOCKS代理(本地Linux)
`./proxy socks -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
那么访问本地的8080端口就是访问VPS上面的代理端口38080.
二级SOCKS代理(本地windows)
`./proxy.exe socks -t tcp -p ":8080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
然后设置你的windos系统中需要通过代理上网的程序的代理为socks5模式地址为127.0.0.1端口为8080,程序即可通过加密通道通过vps上网。
**5.4.SOCKS三级代理(加密)**
一级SOCKS代理VPS_01,IP:22.22.22.22
`./proxy socks -t tls -p ":38080" -C proxy.crt -K proxy.key`
二级SOCKS代理VPS_02,IP:33.33.33.33
`./proxy socks -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
三级SOCKS代理(本地)
`./proxy socks -t tcp -p ":8080" -T tls -P "33.33.33.33:28080" -C proxy.crt -K proxy.key`
那么访问本地的8080端口就是访问一级SOCKS代理上面的代理端口38080.
**5.5.SOCKS代理流量强制走上级SOCKS代理**
默认情况下,proxy会智能判断一个网站域名是否无法访问,如果无法访问才走上级SOCKS代理.通过--always可以使全部SOCKS代理流量强制走上级SOCKS代理.
`./proxy socks --always -t tls -p ":28080" -T tls -P "22.22.22.22:38080" -C proxy.crt -K proxy.key`
**5.6.SOCKS通过SSH中转**
说明:ssh中转的原理是利用了ssh的转发功能,就是你连接上ssh之后,可以通过ssh代理访问目标地址.
假设有:vps
- IP是2.2.2.2, ssh端口是22, ssh用户名是:user, ssh用户密码是:demo
- 用户user的ssh私钥名称是user.key
***5.6.1 ssh用户名和密码的方式***
本地SOCKS5代理28080端口,执行:
`./proxy socks -T ssh -P "2.2.2.2:22" -u user -A demo -t tcp -p ":28080"`
***5.6.2 ssh用户名和密钥的方式***
本地SOCKS5代理28080端口,执行:
`./proxy socks -T ssh -P "2.2.2.2:22" -u user -S user.key -t tcp -p ":28080"`
那么访问本地的28080端口就是通过VPS访问目标地址.
**5.7.查看帮助**
`./proxy help socks`
### TODO
- SOCKS5增加用户名密码认证
### 如何使用源码?
cd进入你的go src目录,然后git clone https://github.com/snail007/goproxy.git ./proxy 即可.
编译直接:go build
运行: go run *.go
utils是工具包,service是具体的每个服务类.
### License
Proxy is licensed under GPLv3 license.
### Contact
QQ交流群:189618940

3
auth-file Normal file
View File

@ -0,0 +1,3 @@
#comment
user3:pass3
user4:pass4

4833
blocked Normal file

File diff suppressed because it is too large Load Diff

157
config.go Executable file
View File

@ -0,0 +1,157 @@
package main
import (
"fmt"
"log"
"os"
"proxy/services"
"proxy/utils"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
var (
app *kingpin.Application
service *services.ServiceItem
)
func initConfig() (err error) {
//keygen
if len(os.Args) > 1 {
if os.Args[1] == "keygen" {
utils.Keygen()
os.Exit(0)
}
}
//define args
tcpArgs := services.TCPArgs{}
httpArgs := services.HTTPArgs{}
tunnelServerArgs := services.TunnelServerArgs{}
tunnelClientArgs := services.TunnelClientArgs{}
tunnelBridgeArgs := services.TunnelBridgeArgs{}
udpArgs := services.UDPArgs{}
socksArgs := services.SocksArgs{}
//build srvice args
app = kingpin.New("proxy", "happy with proxy")
app.Author("snail").Version(APP_VERSION)
//########http#########
http := app.Command("http", "proxy on http mode")
httpArgs.Parent = http.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
httpArgs.CertFile = http.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
httpArgs.KeyFile = http.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
httpArgs.LocalType = http.Flag("local-type", "local protocol type <tls|tcp>").Default("tcp").Short('t').Enum("tls", "tcp")
httpArgs.ParentType = http.Flag("parent-type", "parent protocol type <tls|tcp|ssh>").Short('T').Enum("tls", "tcp", "ssh")
httpArgs.Always = http.Flag("always", "always use parent proxy").Default("false").Bool()
httpArgs.Timeout = http.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("2000").Int()
httpArgs.HTTPTimeout = http.Flag("http-timeout", "check domain if blocked , http request timeout milliseconds when connect to host").Default("3000").Int()
httpArgs.Interval = http.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int()
httpArgs.Blocked = http.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String()
httpArgs.Direct = http.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
httpArgs.AuthFile = http.Flag("auth-file", "http basic auth file,\"username:password\" each line in file").Short('F').String()
httpArgs.Auth = http.Flag("auth", "http basic auth username and password, mutiple user repeat -a ,such as: -a user1:pass1 -a user2:pass2").Short('a').Strings()
httpArgs.PoolSize = http.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int()
httpArgs.CheckParentInterval = http.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
httpArgs.Local = http.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
httpArgs.SSHUser = http.Flag("ssh-user", "user for ssh").Short('u').Default("").String()
httpArgs.SSHKeyFile = http.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String()
httpArgs.SSHKeyFileSalt = http.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String()
httpArgs.SSHPassword = http.Flag("ssh-password", "password for ssh").Short('A').Default("").String()
//########tcp#########
tcp := app.Command("tcp", "proxy on tcp mode")
tcpArgs.Parent = tcp.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
tcpArgs.CertFile = tcp.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
tcpArgs.KeyFile = tcp.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tcpArgs.Timeout = tcp.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Short('t').Default("2000").Int()
tcpArgs.ParentType = tcp.Flag("parent-type", "parent protocol type <tls|tcp|udp>").Short('T').Enum("tls", "tcp", "udp")
tcpArgs.IsTLS = tcp.Flag("tls", "proxy on tls mode").Default("false").Bool()
tcpArgs.PoolSize = tcp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int()
tcpArgs.CheckParentInterval = tcp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
tcpArgs.Local = tcp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//########udp#########
udp := app.Command("udp", "proxy on udp mode")
udpArgs.Parent = udp.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
udpArgs.CertFile = udp.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
udpArgs.KeyFile = udp.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
udpArgs.Timeout = udp.Flag("timeout", "tcp timeout milliseconds when connect to parent proxy").Short('t').Default("2000").Int()
udpArgs.ParentType = udp.Flag("parent-type", "parent protocol type <tls|tcp|udp>").Short('T').Enum("tls", "tcp", "udp")
udpArgs.PoolSize = udp.Flag("pool-size", "conn pool size , which connect to parent proxy, zero: means turn off pool").Short('L').Default("0").Int()
udpArgs.CheckParentInterval = udp.Flag("check-parent-interval", "check if proxy is okay every interval seconds,zero: means no check").Short('I').Default("3").Int()
udpArgs.Local = udp.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//########tunnel-server#########
tunnelServer := app.Command("tserver", "proxy on tunnel server mode")
tunnelServerArgs.Parent = tunnelServer.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
tunnelServerArgs.CertFile = tunnelServer.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
tunnelServerArgs.KeyFile = tunnelServer.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tunnelServerArgs.Timeout = tunnelServer.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool()
tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String()
tunnelServerArgs.Route = tunnelServer.Flag("route", "local route to client's network, such as :PROTOCOL://LOCAL_IP:LOCAL_PORT@[CLIENT_KEY]CLIENT_LOCAL_HOST:CLIENT_LOCAL_PORT").Short('r').Default("").Strings()
//########tunnel-client#########
tunnelClient := app.Command("tclient", "proxy on tunnel client mode")
tunnelClientArgs.Parent = tunnelClient.Flag("parent", "parent address, such as: \"23.32.32.19:28008\"").Default("").Short('P').String()
tunnelClientArgs.CertFile = tunnelClient.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
tunnelClientArgs.KeyFile = tunnelClient.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tunnelClientArgs.Timeout = tunnelClient.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
tunnelClientArgs.Key = tunnelClient.Flag("k", "key same with server").Default("default").String()
//########tunnel-bridge#########
tunnelBridge := app.Command("tbridge", "proxy on tunnel bridge mode")
tunnelBridgeArgs.CertFile = tunnelBridge.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
tunnelBridgeArgs.KeyFile = tunnelBridge.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
tunnelBridgeArgs.Timeout = tunnelBridge.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
tunnelBridgeArgs.Local = tunnelBridge.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
//########ssh#########
socks := app.Command("socks", "proxy on ssh mode")
socksArgs.Parent = socks.Flag("parent", "parent ssh address, such as: \"23.32.32.19:22\"").Default("").Short('P').String()
socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type <tls|tcp|ssh>").Default("tcp").Short('T').Enum("tls", "tcp", "ssh")
socksArgs.LocalType = socks.Flag("local-type", "local protocol type <tls|tcp>").Default("tcp").Short('t').Enum("tls", "tcp")
socksArgs.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
socksArgs.CertFile = socks.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String()
socksArgs.KeyFile = socks.Flag("key", "key file for tls").Short('K').Default("proxy.key").String()
socksArgs.SSHUser = socks.Flag("ssh-user", "user for ssh").Short('u').Default("").String()
socksArgs.SSHKeyFile = socks.Flag("ssh-key", "private key file for ssh").Short('S').Default("").String()
socksArgs.SSHKeyFileSalt = socks.Flag("ssh-keysalt", "salt of ssh private key").Short('s').Default("").String()
socksArgs.SSHPassword = socks.Flag("ssh-password", "password for ssh").Short('A').Default("").String()
socksArgs.Always = socks.Flag("always", "always use parent proxy").Default("false").Bool()
socksArgs.Timeout = socks.Flag("timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("5000").Int()
socksArgs.Interval = socks.Flag("interval", "check domain if blocked every interval seconds").Default("10").Int()
socksArgs.Blocked = socks.Flag("blocked", "blocked domain file , one domain each line").Default("blocked").Short('b').String()
socksArgs.Direct = socks.Flag("direct", "direct domain file , one domain each line").Default("direct").Short('d').String()
//parse args
serviceName := kingpin.MustParse(app.Parse(os.Args[1:]))
poster()
//regist services and run service
services.Regist("http", services.NewHTTP(), httpArgs)
services.Regist("tcp", services.NewTCP(), tcpArgs)
services.Regist("udp", services.NewUDP(), udpArgs)
services.Regist("tserver", services.NewTunnelServerManager(), tunnelServerArgs)
services.Regist("tclient", services.NewTunnelClient(), tunnelClientArgs)
services.Regist("tbridge", services.NewTunnelBridge(), tunnelBridgeArgs)
services.Regist("socks", services.NewSocks(), socksArgs)
service, err = services.Run(serviceName)
if err != nil {
log.Fatalf("run service [%s] fail, ERR:%s", serviceName, err)
}
return
}
func poster() {
fmt.Printf(`
######## ######## ####### ## ## ## ##
## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ####
######## ######## ## ## ### ##
## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ##
## ## ## ####### ## ## ##
v%s`+" by snail , blog : http://www.host900.com/\n\n", APP_VERSION)
}

456
direct Normal file
View File

@ -0,0 +1,456 @@
07073.com
10010.com
100ye.com
114la.com
115.com
120ask.com
126.com
126.net
1616.net
163.com
17173.com
1778.com
178.com
17u.com
19lou.com
1o26.com
1ting.com
21cn.com
2345.com
265.com
265g.com
28.com
28tui.com
2hua.com
2mdn.net
315che.com
3366.com
360buy.com
360buyimg.com
360doc.com
36kr.com
39.net
3dmgame.com
4399.com
4738.com
500wan.com
51.com
51.la
5173.com
51auto.com
51buy.com
51cto.com
51fanli.com
51job.com
52kmh.com
52pk.net
52tlbb.com
53kf.com
55bbs.com
55tuan.com
56.com
58.com
591hx.com
5d6d.net
61.com
70e.com
777wyx.com
778669.com
7c.com
7k7k.com
88db.com
91.com
99bill.com
a135.net
abang.com
abchina.com
ad1111.com
admin5.com
adnxs.com
adobe.com
adroll.com
ads8.com
adsame.com
adsonar.com
adtechus.com
aibang.com
aifang.com
aili.com
aipai.com
aizhan.com
ali213.net
alibaba.com
alicdn.com
aliexpress.com
alimama.com
alipay.com
alipayobjects.com
alisoft.com
alivv.com
aliyun.com
allyes.com
amazon.com
anjuke.com
anzhi.com
aol.com
apple.com
arpg2.com
atdmt.com
b2b168.com
babytree.com
baidu.com
baihe.com
baixing.com
bankcomm.com
baomihua.com
bdimg.com
bdstatic.com
bendibao.com
betrad.com
bilibili.tv
bing.com
bitauto.com
blog.163.com
blogchina.com
blueidea.com
bluekai.com
booksky.org
caixin.com
ccb.com
ccidnet.com
cctv*.com
china.com
chinabyte.com
chinahr.com
chinanews.com
chinaw3.com
chinaz.com
chuangelm.com
ci123.com
cmbchina.com
cnbeta.com
cnblogs.com
cncn.com
cnhubei.com
cnki.net
cnmo.com
cnxad.com
cnzz.com
cocoren.com
compete.com
comsenz.com
coo8.com
cqnews.net
crsky.com
csdn.net
ct10000.com
ctrip.com
dangdang.com
daqi.com
dayoo.com
dbank.com
ddmap.com
dedecms.com
dh818.com
diandian.com
dianping.com
discuz.net
doc88.com
docin.com
donews.com
dospy.com
douban.com
douban.fm
doubleclick.com
doubleclick.net
duba.net
duote.com
duowan.com
dzwww.com
eastday.com
eastmoney.com
ebay.com
elong.com
ename.net
etao.com
exam8.com
eye.rs
fantong.com
fastcdn.com
fblife.com
fengniao.com
fenzhi.com
flickr.com
fobshanghai.com
ftuan.com
funshion.com
fx120.net
game3737.com
gamersky.com
gamestlbb.com
gamesville.com
ganji.com
gfan.com
gongchang.com
google-analytics.com
gougou.com
gtimg.com
hao123.com
haodf.com
harrenmedianetwork.com
hc360.com
hefei.cc
hf365.com
hiapk.com
hichina.com
homeinns.com
hotsales.net
house365.com
huaban.com
huanqiu.com
hudong.com
hupu.com
iask.com
iciba.com
icson.com
ifeng.com
iloveyouxi.com
im286.com
imanhua.com
img.cctvpic.com
imrworldwide.com
invitemedia.com
ip138.com
ipinyou.com
iqilu.com
iqiyi.com
irs01.com
irs01.net
it168.com
iteye.com
iyaya.com
jb51.net
jiathis.com
jiayuan.com
jing.fm
jinti.com
jqw.com
jumei.com
jxedt.com
jysq.net
kaixin001.com
kandian.com
kdnet.net
kimiss.com
ku6.com
ku6cdn.com
ku6img.com
kuaidi100.com
kugou.com
l99.com
lady8844.com
lafaso.com
lashou.com
legolas-media.com
lehecai.com
leho.com
letv.com
liebiao.com
lietou.com
linezing.com
linkedin.com
live.com
longhoo.net
lusongsong.com
lxdns.com
lycos.com
lygo.com
m18.com
m1905.com
made-in-china.com
makepolo.com
mangocity.com
manzuo.com
mapbar.com
mathtag.com
mediaplex.com
mediav.com
meilele.com
meilishuo.com
meishichina.com
meituan.com
meizu.com
miaozhen.com
microsoft.com
miercn.com
mlt01.com
mmcdn.cn
mmstat.com
mnwan.com
mogujie.com
mookie1.com
moonbasa.com
mop.com
mosso.com
mplife.com
msn.com
mtime.com
mumayi.com
mydrivers.com
net114.com
netease.com
newsmth.net
nipic.com
nowec.com
nuomi.com
oadz.com
oeeee.com
onetad.com
onlinedown.net
onlylady.com
oschina.net
otwan.com
paipai.com
paypal.com
pchome.net
pcpop.com
pengyou.com
php100.com
phpwind.net
pingan.com
pixlr.com
pp.cc
ppstream.com
pptv.com
ptlogin2.qq.com
pubmatic.com
q150.com
qianlong.com
qidian.com
qingdaonews.com
qire123.com
qiushibaike.com
qiyou.com
qjy168.com
qq.com
qq937.com
qstatic.com
quantserve.com
qunar.com
rakuten.co.jp
readnovel.com
renren.com
rtbidder.net
scanscout.com
scorecardresearch.com
sdo.com
seowhy.com
serving-sys.com
sf-express.com
shangdu.com
si.kz
sina.com
sinahk.net
sinajs.com
smzdm.com
snyu.com
sodu.org
sogou.com
sohu.com
soku.com
sootoo.com
soso.com
soufun.com
sourceforge.net
staticsdo.com
stockstar.com
sttlbb.com
suning.com
szhome.com
sznews.com
tangdou.com
tanx.com
tao123.com
taobao.com
taobaocdn.com
tbcdn.cn
tdimg.com
tenpay.com
tgbus.com
theplanet.com
thethirdmedia.com
tiancity.com
tianji.com
tiao8.info
tiexue.net
titan24.com
tmall.com
tom.com
toocle.com
tremormedia.com
tuan800.com
tudou.com
tudouui.com
tui18.com
tuniu.com
twcczhu.com
u17.com
ucjoy.com
ulink.cc
uniontoufang.com
up2c.com
uuu9.com
uuzu.com
vancl.com
verycd.com
vipshop.com
vizu.com
vjia.com
weibo.com
weiphone.com
west263.com
whlongda.com
wrating.com
wumii.com
xiami.com
xiaomi.com
xiazaiba.com
xici.net
xinhuanet.com
xinnet.com
xitek.com
xiu.com
xunlei.com
xyxy.net
yahoo.co.jp
yahoo.com
yaolan.com
yesky.com
yieldmanager.com
yihaodian.com
yingjiesheng.com
yinyuetai.com
yiqifa.com
ykimg.com
ynet.com
yoka.com
yolk7.com
youboy.com
youdao.com
yougou.com
youku.com
youshang.com
yupoo.com
yxlady.com
yyets.com
zhaodao123.com
zhaopin.com
zhenai.com
zhibo8.cc
zhihu.com
zhubajie.com
zongheng.com
zoosnet.net
zqgame.com
ztgame.com
zx915.com

View File

@ -1,40 +0,0 @@
这里以vps centos 64位为例子
Linux 部分
1.Putty工具或其他工具
root登入
2.下载批量命令文件install_auto.sh64位的话直接执行这个命令即可  
#curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.sh | bash
注意
这里的install_auto.sh 源码可以下载修改proxy版本,保存后执行.
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image001.png?raw=true"/>
3.修改/etc/proxy/proxy.toml配置文件
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image002.png?raw=true"/>
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image003.png?raw=true"/>
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image004.png?raw=true"/>
#/usr/bin/proxyd status
如果未运行那么执行调试命令:/usr/bin/proxy  
如果一切正常,可以使用proxyd命令管理proxy,执行 proxyd 可以查看用法.
后台启动proxy: proxyd start
4.下载证书加密文件/etc/proxy/proxy.crt和/etc/proxy/proxy.key到windows  
Windows部分  
5.https://github.com/snail007/goproxy/releases 下载对应windows版本
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image005.jpg?raw=true"/>
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image006.png?raw=true"/>
我的是d
6.修改windows下的proxy.toml vps服务ip和上面设置的端口哦
<img src="https://github.com/snail007/goproxy/blob/master/docs/images/image007.png?raw=true"/>
然后运行proxy.exe即可.  
这时候浏览器代理服务器就是127.0.0.1:9501啦,完毕!
要隐藏windows命令用工具下载RunHiddenConsole.exe 写个bat文件都放proxy目录下就行
Start.bat
@echo off
echo Starting
RunHiddenConsole D:/proxy/proxy.exe
Stop.bat
@echo off
echo Stopping
taskkill /F /IM proxy.exe > nul
exit

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/images/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

@ -1,10 +1,6 @@
#!/bin/bash
set -e
if [ -e /tmp/proxy ]; then
rm -rf /tmp/proxy
fi
mkdir /tmp/proxy
cd /tmp/proxy
# install monexec
tar zxvf monexec_0.1.1_linux_amd64.tar.gz
cd monexec_0.1.1_linux_amd64
@ -14,12 +10,11 @@ cd ..
# #install proxy
tar zxvf proxy-linux-amd64.tar.gz
cp proxy /usr/bin/
cp proxyd /usr/bin/
chmod +x /usr/bin/proxy
chmod +x /usr/bin/proxyd
if [ ! -e /etc/proxy ]; then
mkdir /etc/proxy
cp proxy.toml /etc/proxy/
cp blocked /etc/proxy
cp direct /etc/proxy
fi
if [ ! -e /etc/proxy/proxy.crt ]; then
@ -28,4 +23,4 @@ if [ ! -e /etc/proxy/proxy.crt ]; then
fi
rm -rf /tmp/proxy
echo "install done"
proxyd
proxy help

View File

@ -6,7 +6,7 @@ fi
mkdir /tmp/proxy
cd /tmp/proxy
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
wget https://github.com/snail007/goproxy/releases/download/v2.1/proxy-linux-amd64.tar.gz
wget https://github.com/snail007/goproxy/releases/download/v3.1fix/proxy-linux-amd64.tar.gz
# install monexec
tar zxvf monexec_0.1.1_linux_amd64.tar.gz
@ -17,12 +17,11 @@ cd ..
# #install proxy
tar zxvf proxy-linux-amd64.tar.gz
cp proxy /usr/bin/
cp proxyd /usr/bin/
chmod +x /usr/bin/proxy
chmod +x /usr/bin/proxyd
if [ ! -e /etc/proxy ]; then
mkdir /etc/proxy
cp proxy.toml /etc/proxy/
cp blocked /etc/proxy
cp direct /etc/proxy
fi
if [ ! -e /etc/proxy/proxy.crt ]; then
@ -31,4 +30,4 @@ if [ ! -e /etc/proxy/proxy.crt ]; then
fi
rm -rf /tmp/proxy
echo "install done"
proxyd
proxy help

3
keygen.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
openssl genrsa -out proxy.key 2048
openssl req -new -key proxy.key -x509 -days 3650 -out proxy.crt -subj /C=CN/ST=BJ/O="Localhost Ltd"/CN=proxy

38
main.go Normal file
View File

@ -0,0 +1,38 @@
package main
import (
"fmt"
"log"
"os"
"os/signal"
"proxy/services"
"syscall"
)
const APP_VERSION = "3.2"
func main() {
err := initConfig()
if err != nil {
log.Fatalf("err : %s", err)
}
Clean(&service.S)
}
func Clean(s *services.Service) {
signalChan := make(chan os.Signal, 1)
cleanupDone := make(chan bool)
signal.Notify(signalChan,
os.Interrupt,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT)
go func() {
for _ = range signalChan {
fmt.Println("\nReceived an interrupt, stopping services...")
(*s).Clean()
cleanupDone <- true
}
}()
<-cleanupDone
}

63
release.sh Executable file
View File

@ -0,0 +1,63 @@
#!/bin/bash
VER="3.2"
RELEASE="release-${VER}"
rm -rf .cert
mkdir .cert
go build
cd .cert
../proxy keygen
cd ..
rm -rf ${RELEASE}
mkdir ${RELEASE}
set CGO_ENABLED=0
#linux
GOOS=linux GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-linux-386.tar.gz" proxy direct blocked
GOOS=linux GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-linux-amd64.tar.gz" proxy direct blocked
GOOS=linux GOARCH=arm GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm.tar.gz" proxy direct blocked
GOOS=linux GOARCH=arm64 GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm64.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mips go build && tar zcfv "${RELEASE}/proxy-linux-mips.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mips64 go build && tar zcfv "${RELEASE}/proxy-linux-mips64.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mips64le go build && tar zcfv "${RELEASE}/proxy-linux-mips64le.tar.gz" proxy direct blocked
GOOS=linux GOARCH=mipsle go build && tar zcfv "${RELEASE}/proxy-linux-mipsle.tar.gz" proxy direct blocked
GOOS=linux GOARCH=ppc64 go build && tar zcfv "${RELEASE}/proxy-linux-ppc64.tar.gz" proxy direct blocked
GOOS=linux GOARCH=ppc64le go build && tar zcfv "${RELEASE}/proxy-linux-ppc64le.tar.gz" proxy direct blocked
GOOS=linux GOARCH=s390x go build && tar zcfv "${RELEASE}/proxy-linux-s390x.tar.gz" proxy direct blocked
#android
GOOS=android GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-android-386.tar.gz" proxy direct blocked
GOOS=android GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-android-amd64.tar.gz" proxy direct blocked
GOOS=android GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-android-arm.tar.gz" proxy direct blocked
GOOS=android GOARCH=arm64 go build && tar zcfv "${RELEASE}/proxy-android-arm64.tar.gz" proxy direct blocked
#darwin
GOOS=darwin GOARCH=386 go build go build && tar zcfv "${RELEASE}/proxy-darwin-386.tar.gz" proxy direct blocked
GOOS=darwin GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-darwin-amd64.tar.gz" proxy direct blocked
GOOS=darwin GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-darwin-arm.tar.gz" proxy direct blocked
GOOS=darwin GOARCH=arm64 go build && tar zcfv "${RELEASE}/proxy-darwin-arm64.tar.gz" proxy direct blocked
#dragonfly
GOOS=dragonfly GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-dragonfly-amd64.tar.gz" proxy direct blocked
#freebsd
GOOS=freebsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-freebsd-386.tar.gz" proxy direct blocked
GOOS=freebsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-freebsd-amd64.tar.gz" proxy direct blocked
GOOS=freebsd GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-freebsd-arm.tar.gz" proxy direct blocked
#nacl
GOOS=nacl GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-nacl-386.tar.gz" proxy direct blocked
GOOS=nacl GOARCH=amd64p32 go build && tar zcfv "${RELEASE}/proxy-nacl-amd64p32.tar.gz" proxy direct blocked
GOOS=nacl GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-nacl-arm.tar.gz" proxy direct blocked
#netbsd
GOOS=netbsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-netbsd-386.tar.gz" proxy direct blocked
GOOS=netbsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-netbsd-amd64.tar.gz" proxy direct blocked
GOOS=netbsd GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-netbsd-arm.tar.gz" proxy direct blocked
#openbsd
GOOS=openbsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-openbsd-386.tar.gz" proxy direct blocked
GOOS=openbsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-openbsd-amd64.tar.gz" proxy direct blocked
GOOS=openbsd GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-openbsd-arm.tar.gz" proxy direct blocked
#plan9
GOOS=plan9 GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-plan9-386.tar.gz" proxy direct blocked
GOOS=plan9 GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-plan9-amd64.tar.gz" proxy direct blocked
GOOS=plan9 GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-plan9-arm.tar.gz" proxy direct blocked
#solaris
GOOS=solaris GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-solaris-amd64.tar.gz" proxy direct blocked
#windows
GOOS=windows GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-windows-386.tar.gz" proxy.exe direct blocked .cert/proxy.crt .cert/proxy.key
GOOS=windows GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-windows-amd64.tar.gz" proxy.exe direct blocked .cert/proxy.crt .cert/proxy.key
rm -rf proxy proxy.exe .cert

128
services/args.go Normal file
View File

@ -0,0 +1,128 @@
package services
import "golang.org/x/crypto/ssh"
// tcp := app.Command("tcp", "proxy on tcp mode")
// t := tcp.Flag("tcp-timeout", "tcp timeout milliseconds when connect to real server or parent proxy").Default("2000").Int()
const (
TYPE_TCP = "tcp"
TYPE_UDP = "udp"
TYPE_HTTP = "http"
TYPE_TLS = "tls"
CONN_CONTROL = uint8(1)
CONN_SERVER = uint8(2)
CONN_CLIENT = uint8(3)
)
type TunnelServerArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
IsUDP *bool
Key *string
Remote *string
Timeout *int
Route *[]string
}
type TunnelClientArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Key *string
Timeout *int
}
type TunnelBridgeArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
Timeout *int
}
type TCPArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
ParentType *string
IsTLS *bool
Timeout *int
PoolSize *int
CheckParentInterval *int
}
type HTTPArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
Always *bool
HTTPTimeout *int
Interval *int
Blocked *string
Direct *string
AuthFile *string
Auth *[]string
ParentType *string
LocalType *string
Timeout *int
PoolSize *int
CheckParentInterval *int
SSHKeyFile *string
SSHKeyFileSalt *string
SSHPassword *string
SSHUser *string
SSHKeyBytes []byte
SSHAuthMethod ssh.AuthMethod
}
type UDPArgs struct {
Parent *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
Local *string
ParentType *string
Timeout *int
PoolSize *int
CheckParentInterval *int
}
type SocksArgs struct {
Parent *string
ParentType *string
Local *string
LocalType *string
CertFile *string
KeyFile *string
CertBytes []byte
KeyBytes []byte
SSHKeyFile *string
SSHKeyFileSalt *string
SSHPassword *string
SSHUser *string
SSHKeyBytes []byte
SSHAuthMethod ssh.AuthMethod
Timeout *int
Always *bool
Interval *int
Blocked *string
Direct *string
}
func (a *TCPArgs) Protocol() string {
if *a.IsTLS {
return "tls"
}
return "tcp"
}

311
services/http.go Normal file
View File

@ -0,0 +1,311 @@
package services
import (
"fmt"
"io"
"io/ioutil"
"log"
"net"
"proxy/utils"
"runtime/debug"
"strconv"
"time"
"golang.org/x/crypto/ssh"
)
type HTTP struct {
outPool utils.OutPool
cfg HTTPArgs
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
}
func NewHTTP() Service {
return &HTTP{
outPool: utils.OutPool{},
cfg: HTTPArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
}
}
func (s *HTTP) CheckArgs() {
var err error
if *s.cfg.Parent != "" && *s.cfg.ParentType == "" {
log.Fatalf("parent type unkown,use -T <tls|tcp|ssh>")
}
if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
if *s.cfg.ParentType == "ssh" {
if *s.cfg.SSHUser == "" {
log.Fatalf("ssh user required")
}
if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" {
log.Fatalf("ssh password or key required")
}
if *s.cfg.SSHPassword != "" {
s.cfg.SSHAuthMethod = ssh.Password(*s.cfg.SSHPassword)
} else {
var SSHSigner ssh.Signer
s.cfg.SSHKeyBytes, err = ioutil.ReadFile(*s.cfg.SSHKeyFile)
if err != nil {
log.Fatalf("read key file ERR: %s", err)
}
if *s.cfg.SSHKeyFileSalt != "" {
SSHSigner, err = ssh.ParsePrivateKeyWithPassphrase(s.cfg.SSHKeyBytes, []byte(*s.cfg.SSHKeyFileSalt))
} else {
SSHSigner, err = ssh.ParsePrivateKey(s.cfg.SSHKeyBytes)
}
if err != nil {
log.Fatalf("parse ssh private key fail,ERR: %s", err)
}
s.cfg.SSHAuthMethod = ssh.PublicKeys(SSHSigner)
}
}
}
func (s *HTTP) InitService() {
s.InitBasicAuth()
if *s.cfg.Parent != "" {
s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct)
}
if *s.cfg.ParentType == "ssh" {
err := s.ConnectSSH()
if err != nil {
log.Fatalf("init service fail, ERR: %s", err)
}
}
}
func (s *HTTP) StopService() {
if s.outPool.Pool != nil {
s.outPool.Pool.ReleaseAll()
}
}
func (s *HTTP) Start(args interface{}) (err error) {
s.cfg = args.(HTTPArgs)
s.CheckArgs()
if *s.cfg.Parent != "" {
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
s.InitOutConnPool()
}
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
if *s.cfg.LocalType == TYPE_TCP {
err = sc.ListenTCP(s.callback)
} else {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback)
}
if err != nil {
return
}
log.Printf("%s http(s) proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
return
}
func (s *HTTP) Clean() {
s.StopService()
}
func (s *HTTP) callback(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
log.Printf("http(s) conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
req, err := utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth)
if err != nil {
if err != io.EOF {
log.Printf("decoder error , form %s, ERR:%s", err, inConn.RemoteAddr())
}
utils.CloseConn(&inConn)
return
}
address := req.Host
useProxy := true
if *s.cfg.Parent == "" {
useProxy = false
} else if *s.cfg.Always {
useProxy = true
} else {
if req.IsHTTPS() {
s.checker.Add(address, true, req.Method, "", nil)
} else {
s.checker.Add(address, false, req.Method, req.URL, req.HeadBuf)
}
//var n, m uint
useProxy, _, _ = s.checker.IsBlocked(req.Host)
//log.Printf("blocked ? : %v, %s , fail:%d ,success:%d", useProxy, address, n, m)
}
log.Printf("use proxy : %v, %s", useProxy, address)
err = s.OutToTCP(useProxy, address, &inConn, &req)
if err != nil {
if *s.cfg.Parent == "" {
log.Printf("connect to %s fail, ERR:%s", address, err)
} else {
log.Printf("connect to %s parent %s fail", *s.cfg.ParentType, *s.cfg.Parent)
}
utils.CloseConn(&inConn)
}
}
func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (err error) {
inAddr := (*inConn).RemoteAddr().String()
inLocalAddr := (*inConn).LocalAddr().String()
//防止死循环
if s.IsDeadLoop(inLocalAddr, req.Host) {
utils.CloseConn(inConn)
err = fmt.Errorf("dead loop detected , %s", req.Host)
return
}
var outConn net.Conn
var _outConn interface{}
if useProxy {
if *s.cfg.ParentType == "ssh" {
outConn, err = s.getSSHConn(address)
} else {
_outConn, err = s.outPool.Pool.Get()
if err == nil {
outConn = _outConn.(net.Conn)
}
}
} else {
outConn, err = utils.ConnectHost(address, *s.cfg.Timeout)
}
if err != nil {
log.Printf("connect to %s , err:%s", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
outAddr := outConn.RemoteAddr().String()
outLocalAddr := outConn.LocalAddr().String()
if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") {
//https无上级或者上级非代理,proxy需要响应connect请求,并直连目标
err = req.HTTPSReply()
} else {
//https或者http,上级是代理,proxy需要转发
_, err = outConn.Write(req.HeadBuf)
if err != nil {
log.Printf("write to %s , err:%s", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
}
utils.IoBind((*inConn), outConn, func(err error) {
log.Printf("conn %s - %s - %s - %s released [%s]", inAddr, inLocalAddr, outLocalAddr, outAddr, req.Host)
utils.CloseConn(inConn)
utils.CloseConn(&outConn)
}, func(n int, d bool) {}, 0)
log.Printf("conn %s - %s - %s - %s connected [%s]", inAddr, inLocalAddr, outLocalAddr, outAddr, req.Host)
return
}
func (s *HTTP) getSSHConn(host string) (outConn net.Conn, err error) {
maxTryCount := 1
tryCount := 0
RETRY:
if tryCount >= maxTryCount {
return
}
outConn, err = s.sshClient.Dial("tcp", host)
//log.Printf("s.sshClient.Dial, host:%s)", host)
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
s.sshClient.Close()
e := s.ConnectSSH()
if e == nil {
tryCount++
time.Sleep(time.Second * 3)
goto RETRY
} else {
err = e
}
}
return
}
func (s *HTTP) ConnectSSH() (err error) {
config := ssh.ClientConfig{
User: *s.cfg.SSHUser,
Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
s.sshClient, err = ssh.Dial("tcp", *s.cfg.Parent, &config)
return
}
func (s *HTTP) InitOutConnPool() {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutPool(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType == TYPE_TLS,
s.cfg.CertBytes, s.cfg.KeyBytes,
*s.cfg.Parent,
*s.cfg.Timeout,
*s.cfg.PoolSize,
*s.cfg.PoolSize*2,
)
}
}
func (s *HTTP) InitBasicAuth() (err error) {
s.basicAuth = utils.NewBasicAuth()
if *s.cfg.AuthFile != "" {
var n = 0
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
if err != nil {
err = fmt.Errorf("auth-file ERR:%s", err)
return
}
log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total())
}
if len(*s.cfg.Auth) > 0 {
n := s.basicAuth.Add(*s.cfg.Auth)
log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total())
}
return
}
func (s *HTTP) IsBasicAuth() bool {
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0
}
func (s *HTTP) IsDeadLoop(inLocalAddr string, host string) bool {
inIP, inPort, err := net.SplitHostPort(inLocalAddr)
if err != nil {
return false
}
outDomain, outPort, err := net.SplitHostPort(host)
if err != nil {
return false
}
if inPort == outPort {
var outIPs []net.IP
outIPs, err = net.LookupIP(outDomain)
if err == nil {
for _, ip := range outIPs {
if ip.String() == inIP {
return true
}
}
}
interfaceIPs, err := utils.GetAllInterfaceAddr()
if err == nil {
for _, localIP := range interfaceIPs {
for _, outIP := range outIPs {
if localIP.Equal(outIP) {
return true
}
}
}
}
}
return false
}

52
services/service.go Normal file
View File

@ -0,0 +1,52 @@
package services
import (
"fmt"
"log"
"runtime/debug"
)
type Service interface {
Start(args interface{}) (err error)
Clean()
}
type ServiceItem struct {
S Service
Args interface{}
Name string
}
var servicesMap = map[string]*ServiceItem{}
func Regist(name string, s Service, args interface{}) {
servicesMap[name] = &ServiceItem{
S: s,
Args: args,
Name: name,
}
}
func Run(name string, args ...interface{}) (service *ServiceItem, err error) {
service, ok := servicesMap[name]
if ok {
go func() {
defer func() {
err := recover()
if err != nil {
log.Fatalf("%s servcie crashed, ERR: %s\ntrace:%s", name, err, string(debug.Stack()))
}
}()
if len(args) == 1 {
err = service.S.Start(args[0])
} else {
err = service.S.Start(service.Args)
}
if err != nil {
log.Fatalf("%s servcie fail, ERR: %s", name, err)
}
}()
}
if !ok {
err = fmt.Errorf("service %s not found", name)
}
return
}

304
services/socks.go Normal file
View File

@ -0,0 +1,304 @@
package services
import (
"bytes"
"crypto/tls"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"proxy/utils"
"time"
"golang.org/x/crypto/ssh"
)
type Socks struct {
cfg SocksArgs
checker utils.Checker
basicAuth utils.BasicAuth
sshClient *ssh.Client
}
func NewSocks() Service {
return &Socks{
cfg: SocksArgs{},
checker: utils.Checker{},
basicAuth: utils.BasicAuth{},
}
}
func (s *Socks) CheckArgs() {
var err error
if *s.cfg.Parent != "" {
if *s.cfg.ParentType == "" {
log.Fatalf("parent type unkown,use -T <tls|tcp|ssh>")
}
if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
if *s.cfg.ParentType == "ssh" {
if *s.cfg.SSHUser == "" {
log.Fatalf("ssh user required")
}
if *s.cfg.SSHKeyFile == "" && *s.cfg.SSHPassword == "" {
log.Fatalf("ssh password or key required")
}
if *s.cfg.SSHPassword != "" {
s.cfg.SSHAuthMethod = ssh.Password(*s.cfg.SSHPassword)
} else {
var SSHSigner ssh.Signer
s.cfg.SSHKeyBytes, err = ioutil.ReadFile(*s.cfg.SSHKeyFile)
if err != nil {
log.Fatalf("read key file ERR: %s", err)
}
if *s.cfg.SSHKeyFileSalt != "" {
SSHSigner, err = ssh.ParsePrivateKeyWithPassphrase(s.cfg.SSHKeyBytes, []byte(*s.cfg.SSHKeyFileSalt))
} else {
SSHSigner, err = ssh.ParsePrivateKey(s.cfg.SSHKeyBytes)
}
if err != nil {
log.Fatalf("parse ssh private key fail,ERR: %s", err)
}
s.cfg.SSHAuthMethod = ssh.PublicKeys(SSHSigner)
}
}
}
}
func (s *Socks) InitService() {
s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct)
if *s.cfg.ParentType == "ssh" {
err := s.ConnectSSH()
if err != nil {
log.Fatalf("init service fail, ERR: %s", err)
}
}
}
func (s *Socks) StopService() {
if s.sshClient != nil {
s.sshClient.Close()
}
}
func (s *Socks) Start(args interface{}) (err error) {
//start()
s.cfg = args.(SocksArgs)
s.CheckArgs()
s.InitService()
if *s.cfg.Parent != "" {
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
}
sc := utils.NewServerChannelHost(*s.cfg.Local)
if *s.cfg.LocalType == TYPE_TCP {
err = sc.ListenTCP(s.callback)
} else {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback)
}
if err != nil {
return
}
log.Printf("%s socks proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
return
}
func (s *Socks) Clean() {
s.StopService()
}
func (s *Socks) callback(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
//log.Printf("socks conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
utils.CloseConn(&inConn)
}()
var outConn net.Conn
defer utils.CloseConn(&outConn)
var b [1024]byte
n, err := inConn.Read(b[:])
if err != nil {
if err != io.EOF {
log.Printf("read request data fail,ERR: %s", err)
}
return
}
var reqBytes = b[:n]
//log.Printf("% x", b[:n])
//reply
n, err = inConn.Write([]byte{0x05, 0x00})
if err != nil {
log.Printf("reply answer data fail,ERR: %s", err)
return
}
//read answer
n, err = inConn.Read(b[:])
if err != nil {
log.Printf("read answer data fail,ERR: %s", err)
return
}
var headBytes = b[:n]
// log.Printf("% x", b[:n])
var addr string
switch b[3] {
case 0x01:
sip := sockIP{}
if err := binary.Read(bytes.NewReader(b[4:n]), binary.BigEndian, &sip); err != nil {
log.Printf("read ip fail,ERR: %s", err)
return
}
addr = sip.toAddr()
case 0x03:
host := string(b[5 : n-2])
var port uint16
err = binary.Read(bytes.NewReader(b[n-2:n]), binary.BigEndian, &port)
if err != nil {
log.Printf("read domain fail,ERR: %s", err)
return
}
addr = fmt.Sprintf("%s:%d", host, port)
}
useProxy := true
if *s.cfg.Always {
outConn, err = s.getOutConn(reqBytes, headBytes, addr)
} else {
if *s.cfg.Parent != "" {
s.checker.Add(addr, true, "", "", nil)
useProxy, _, _ = s.checker.IsBlocked(addr)
if useProxy {
outConn, err = s.getOutConn(reqBytes, headBytes, addr)
} else {
outConn, err = utils.ConnectHost(addr, *s.cfg.Timeout)
}
} else {
outConn, err = utils.ConnectHost(addr, *s.cfg.Timeout)
}
}
if err != nil {
log.Printf("get out conn fail,%s", err)
inConn.Write([]byte{0x05, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
return
}
log.Printf("use proxy %v : %s", useProxy, addr)
inConn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
inAddr := inConn.RemoteAddr().String()
inLocalAddr := inConn.LocalAddr().String()
log.Printf("conn %s - %s connected [%s]", inAddr, inLocalAddr, addr)
// utils.IoBind(outConn, inConn, func(err error) {
// log.Printf("conn %s - %s released [%s]", inAddr, inLocalAddr, addr)
// }, func(i int, b bool) {}, 0)
var bind = func() (err interface{}) {
defer func() {
if err == nil {
if err = recover(); err != nil {
log.Printf("bind crashed %s", err)
}
}
}()
go func() {
defer func() {
if err == nil {
if err = recover(); err != nil {
log.Printf("bind crashed %s", err)
}
}
}()
_, err = io.Copy(outConn, inConn)
}()
_, err = io.Copy(inConn, outConn)
return
}
bind()
log.Printf("conn %s - %s released [%s]", inAddr, inLocalAddr, addr)
utils.CloseConn(&inConn)
utils.CloseConn(&outConn)
}
func (s *Socks) getOutConn(reqBytes, headBytes []byte, host string) (outConn net.Conn, err error) {
switch *s.cfg.ParentType {
case "tls":
fallthrough
case "tcp":
if *s.cfg.ParentType == "tls" {
var _outConn tls.Conn
_outConn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes)
outConn = net.Conn(&_outConn)
} else {
outConn, err = utils.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout)
}
if err != nil {
return
}
var buf = make([]byte, 1024)
//var n int
_, err = outConn.Write(reqBytes)
if err != nil {
return
}
_, err = outConn.Read(buf)
if err != nil {
return
}
//resp := buf[:n]
//log.Printf("resp:%v", resp)
outConn.Write(headBytes)
_, err = outConn.Read(buf)
if err != nil {
return
}
//result := buf[:n]
//log.Printf("result:%v", result)
case "ssh":
maxTryCount := 1
tryCount := 0
RETRY:
if tryCount >= maxTryCount {
return
}
outConn, err = s.sshClient.Dial("tcp", host)
if err != nil {
log.Printf("connect ssh fail, ERR: %s, retrying...", err)
s.sshClient.Close()
e := s.ConnectSSH()
if e == nil {
tryCount++
time.Sleep(time.Second * 3)
goto RETRY
} else {
err = e
}
}
}
return
}
func (s *Socks) ConnectSSH() (err error) {
config := ssh.ClientConfig{
Timeout: time.Duration(*s.cfg.Timeout) * time.Millisecond,
User: *s.cfg.SSHUser,
Auth: []ssh.AuthMethod{s.cfg.SSHAuthMethod},
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
s.sshClient, err = ssh.Dial("tcp", *s.cfg.Parent, &config)
return
}
type sockIP struct {
A, B, C, D byte
PORT uint16
}
func (ip sockIP) toAddr() string {
return fmt.Sprintf("%d.%d.%d.%d:%d", ip.A, ip.B, ip.C, ip.D, ip.PORT)
}

178
services/tcp.go Normal file
View File

@ -0,0 +1,178 @@
package services
import (
"bufio"
"fmt"
"io"
"log"
"net"
"proxy/utils"
"runtime/debug"
"time"
"strconv"
)
type TCP struct {
outPool utils.OutPool
cfg TCPArgs
}
func NewTCP() Service {
return &TCP{
outPool: utils.OutPool{},
cfg: TCPArgs{},
}
}
func (s *TCP) CheckArgs() {
if *s.cfg.Parent == "" {
log.Fatalf("parent required for %s %s", s.cfg.Protocol(), *s.cfg.Local)
}
if *s.cfg.ParentType == "" {
log.Fatalf("parent type unkown,use -T <tls|tcp>")
}
if *s.cfg.ParentType == "tls" || *s.cfg.IsTLS {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
}
func (s *TCP) InitService() {
s.InitOutConnPool()
}
func (s *TCP) StopService() {
if s.outPool.Pool != nil {
s.outPool.Pool.ReleaseAll()
}
}
func (s *TCP) Start(args interface{}) (err error) {
s.cfg = args.(TCPArgs)
s.CheckArgs()
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
if !*s.cfg.IsTLS {
err = sc.ListenTCP(s.callback)
} else {
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback)
}
if err != nil {
return
}
log.Printf("%s proxy on %s", s.cfg.Protocol(), (*sc.Listener).Addr())
return
}
func (s *TCP) Clean() {
s.StopService()
}
func (s *TCP) callback(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
log.Printf("%s conn handler crashed with err : %s \nstack: %s", s.cfg.Protocol(), err, string(debug.Stack()))
}
}()
var err error
switch *s.cfg.ParentType {
case TYPE_TCP:
fallthrough
case TYPE_TLS:
err = s.OutToTCP(&inConn)
case TYPE_UDP:
err = s.OutToUDP(&inConn)
default:
err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType)
}
if err != nil {
log.Printf("connect to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err)
utils.CloseConn(&inConn)
}
}
func (s *TCP) OutToTCP(inConn *net.Conn) (err error) {
var outConn net.Conn
var _outConn interface{}
_outConn, err = s.outPool.Pool.Get()
if err == nil {
outConn = _outConn.(net.Conn)
}
if err != nil {
log.Printf("connect to %s , err:%s", *s.cfg.Parent, err)
utils.CloseConn(inConn)
return
}
inAddr := (*inConn).RemoteAddr().String()
inLocalAddr := (*inConn).LocalAddr().String()
outAddr := outConn.RemoteAddr().String()
outLocalAddr := outConn.LocalAddr().String()
utils.IoBind((*inConn), outConn, func(err error) {
log.Printf("conn %s - %s - %s - %s released", inAddr, inLocalAddr, outLocalAddr, outAddr)
utils.CloseConn(inConn)
utils.CloseConn(&outConn)
}, func(n int, d bool) {}, 0)
log.Printf("conn %s - %s - %s - %s connected", inAddr, inLocalAddr, outLocalAddr, outAddr)
return
}
func (s *TCP) OutToUDP(inConn *net.Conn) (err error) {
log.Printf("conn created , remote : %s ", (*inConn).RemoteAddr())
for {
srcAddr, body, err := utils.ReadUDPPacket(bufio.NewReader(*inConn))
if err == io.EOF || err == io.ErrUnexpectedEOF {
//log.Printf("connection %s released", srcAddr)
utils.CloseConn(inConn)
break
}
//log.Debugf("udp packet revecived:%s,%v", srcAddr, body)
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Parent)
if err != nil {
log.Printf("can't resolve address: %s", err)
utils.CloseConn(inConn)
break
}
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr)
if err != nil {
log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
continue
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write(body)
if err != nil {
log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
continue
}
//log.Debugf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 512)
len, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
continue
}
respBody := buf[0:len]
//log.Debugf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
_, err = (*inConn).Write(utils.UDPPacket(srcAddr, respBody))
if err != nil {
log.Printf("send udp response fail ,ERR:%s", err)
utils.CloseConn(inConn)
break
}
//log.Printf("send udp response success ,from:%s", dstAddr.String())
}
return
}
func (s *TCP) InitOutConnPool() {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutPool(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType == TYPE_TLS,
s.cfg.CertBytes, s.cfg.KeyBytes,
*s.cfg.Parent,
*s.cfg.Timeout,
*s.cfg.PoolSize,
*s.cfg.PoolSize*2,
)
}
}

191
services/tunnel_bridge.go Normal file
View File

@ -0,0 +1,191 @@
package services
import (
"bufio"
"encoding/binary"
"log"
"net"
"proxy/utils"
"strconv"
"time"
)
type ServerConn struct {
ClientLocalAddr string //tcp:2.2.22:333@ID
Conn *net.Conn
//Conn *utils.HeartbeatReadWriter
}
type TunnelBridge struct {
cfg TunnelBridgeArgs
serverConns utils.ConcurrentMap
clientControlConns utils.ConcurrentMap
}
func NewTunnelBridge() Service {
return &TunnelBridge{
cfg: TunnelBridgeArgs{},
serverConns: utils.NewConcurrentMap(),
clientControlConns: utils.NewConcurrentMap(),
}
}
func (s *TunnelBridge) InitService() {
}
func (s *TunnelBridge) CheckArgs() {
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
log.Fatalf("cert and key file required")
}
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
func (s *TunnelBridge) StopService() {
}
func (s *TunnelBridge) Start(args interface{}) (err error) {
s.cfg = args.(TunnelBridgeArgs)
s.CheckArgs()
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, func(inConn net.Conn) {
//log.Printf("connection from %s ", inConn.RemoteAddr())
reader := bufio.NewReader(inConn)
var connType uint8
err = binary.Read(reader, binary.LittleEndian, &connType)
if err != nil {
utils.CloseConn(&inConn)
return
}
//log.Printf("conn type %d", connType)
var key, clientLocalAddr, ID string
var connTypeStrMap = map[uint8]string{CONN_SERVER: "server", CONN_CLIENT: "client", CONN_CONTROL: "client"}
var keyLength uint16
err = binary.Read(reader, binary.LittleEndian, &keyLength)
if err != nil {
return
}
_key := make([]byte, keyLength)
n, err := reader.Read(_key)
if err != nil {
return
}
if n != int(keyLength) {
return
}
key = string(_key)
//log.Printf("conn key %s", key)
if connType != CONN_CONTROL {
var IDLength uint16
err = binary.Read(reader, binary.LittleEndian, &IDLength)
if err != nil {
return
}
_id := make([]byte, IDLength)
n, err := reader.Read(_id)
if err != nil {
return
}
if n != int(IDLength) {
return
}
ID = string(_id)
if connType == CONN_SERVER {
var addrLength uint16
err = binary.Read(reader, binary.LittleEndian, &addrLength)
if err != nil {
return
}
_addr := make([]byte, addrLength)
n, err = reader.Read(_addr)
if err != nil {
return
}
if n != int(addrLength) {
return
}
clientLocalAddr = string(_addr)
}
}
log.Printf("connection from %s , key: %s , id: %s", connTypeStrMap[connType], key, ID)
switch connType {
case CONN_SERVER:
// hb := utils.NewHeartbeatReadWriter(&inConn, 3, func(err error, hb *utils.HeartbeatReadWriter) {
// log.Printf("%s conn %s from server released", key, ID)
// s.serverConns.Remove(ID)
// })
addr := clientLocalAddr + "@" + ID
s.serverConns.Set(ID, ServerConn{
//Conn: &hb,
Conn: &inConn,
ClientLocalAddr: addr,
})
for {
item, ok := s.clientControlConns.Get(key)
if !ok {
log.Printf("client %s control conn not exists", key)
time.Sleep(time.Second * 3)
continue
}
_, err := (*item.(*net.Conn)).Write([]byte(addr))
if err != nil {
log.Printf("%s client control conn write signal fail, err: %s, retrying...", key, err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
case CONN_CLIENT:
serverConnItem, ok := s.serverConns.Get(ID)
if !ok {
inConn.Close()
log.Printf("server conn %s exists", ID)
return
}
serverConn := serverConnItem.(ServerConn).Conn
// hw := utils.NewHeartbeatReadWriter(&inConn, 3, func(err error, hw *utils.HeartbeatReadWriter) {
// log.Printf("%s conn %s from client released", key, ID)
// hw.Close()
// })
utils.IoBind(*serverConn, inConn, func(err error) {
// utils.IoBind(serverConn, inConn, func(isSrcErr bool, err error) {
//serverConn.Close()
(*serverConn).Close()
utils.CloseConn(&inConn)
// hw.Close()
s.serverConns.Remove(ID)
log.Printf("conn %s released", ID)
}, func(i int, b bool) {}, 0)
log.Printf("conn %s created", ID)
case CONN_CONTROL:
if s.clientControlConns.Has(key) {
item, _ := s.clientControlConns.Get(key)
//(*item.(*utils.HeartbeatReadWriter)).Close()
(*item.(*net.Conn)).Close()
}
// hb := utils.NewHeartbeatReadWriter(&inConn, 3, func(err error, hb *utils.HeartbeatReadWriter) {
// log.Printf("client %s disconnected", key)
// s.clientControlConns.Remove(key)
// })
// s.clientControlConns.Set(key, &hb)
s.clientControlConns.Set(key, &inConn)
log.Printf("set client %s control conn", key)
}
})
if err != nil {
return
}
log.Printf("proxy on tunnel bridge mode %s", (*sc.Listener).Addr())
return
}
func (s *TunnelBridge) Clean() {
s.StopService()
}

235
services/tunnel_client.go Normal file
View File

@ -0,0 +1,235 @@
package services
import (
"bytes"
"crypto/tls"
"encoding/binary"
"fmt"
"io"
"log"
"net"
"proxy/utils"
"strings"
"time"
)
type TunnelClient struct {
cfg TunnelClientArgs
}
func NewTunnelClient() Service {
return &TunnelClient{
cfg: TunnelClientArgs{},
}
}
func (s *TunnelClient) InitService() {
}
func (s *TunnelClient) CheckArgs() {
if *s.cfg.Parent != "" {
log.Printf("use tls parent %s", *s.cfg.Parent)
} else {
log.Fatalf("parent required")
}
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
log.Fatalf("cert and key file required")
}
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
func (s *TunnelClient) StopService() {
}
func (s *TunnelClient) Start(args interface{}) (err error) {
s.cfg = args.(TunnelClientArgs)
s.CheckArgs()
s.InitService()
log.Printf("proxy on tunnel client mode")
for {
ctrlConn, err := s.GetInConn(CONN_CONTROL, "")
if err != nil {
log.Printf("control connection err: %s", err)
time.Sleep(time.Second * 3)
utils.CloseConn(&ctrlConn)
continue
}
// rw := utils.NewHeartbeatReadWriter(&ctrlConn, 3, func(err error, hb *utils.HeartbeatReadWriter) {
// log.Printf("ctrlConn err %s", err)
// utils.CloseConn(&ctrlConn)
// })
for {
signal := make([]byte, 50)
// n, err := rw.Read(signal)
n, err := ctrlConn.Read(signal)
if err != nil {
utils.CloseConn(&ctrlConn)
log.Printf("read connection signal err: %s", err)
break
}
addr := string(signal[:n])
// log.Printf("n:%d addr:%s err:%s", n, addr, err)
// os.Exit(0)
log.Printf("signal revecived:%s", addr)
protocol := addr[:3]
atIndex := strings.Index(addr, "@")
ID := addr[atIndex+1:]
localAddr := addr[4:atIndex]
if protocol == "udp" {
go s.ServeUDP(localAddr, ID)
} else {
go s.ServeConn(localAddr, ID)
}
}
}
}
func (s *TunnelClient) Clean() {
s.StopService()
}
func (s *TunnelClient) GetInConn(typ uint8, ID string) (outConn net.Conn, err error) {
outConn, err = s.GetConn()
if err != nil {
err = fmt.Errorf("connection err: %s", err)
return
}
keyBytes := []byte(*s.cfg.Key)
keyLength := uint16(len(keyBytes))
pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, typ)
binary.Write(pkg, binary.LittleEndian, keyLength)
binary.Write(pkg, binary.LittleEndian, keyBytes)
if ID != "" {
IDBytes := []byte(ID)
IDLength := uint16(len(IDBytes))
binary.Write(pkg, binary.LittleEndian, IDLength)
binary.Write(pkg, binary.LittleEndian, IDBytes)
}
_, err = outConn.Write(pkg.Bytes())
if err != nil {
err = fmt.Errorf("write connection data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
return
}
return
}
func (s *TunnelClient) GetConn() (conn net.Conn, err error) {
var _conn tls.Conn
_conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes)
if err == nil {
conn = net.Conn(&_conn)
}
return
}
func (s *TunnelClient) ServeUDP(localAddr, ID string) {
var inConn net.Conn
var err error
// for {
for {
inConn, err = s.GetInConn(CONN_CLIENT, ID)
if err != nil {
utils.CloseConn(&inConn)
log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
log.Printf("conn %s created", ID)
// hw := utils.NewHeartbeatReadWriter(&inConn, 3, func(err error, hw *utils.HeartbeatReadWriter) {
// log.Printf("hw err %s", err)
// hw.Close()
// })
for {
// srcAddr, body, err := utils.ReadUDPPacket(&hw)
srcAddr, body, err := utils.ReadUDPPacket(inConn)
if err == io.EOF || err == io.ErrUnexpectedEOF {
log.Printf("connection %s released", ID)
utils.CloseConn(&inConn)
break
} else if err != nil {
log.Printf("udp packet revecived fail, err: %s", err)
} else {
//log.Printf("udp packet revecived:%s,%v", srcAddr, body)
go s.processUDPPacket(&inConn, srcAddr, localAddr, body)
}
}
// }
}
func (s *TunnelClient) processUDPPacket(inConn *net.Conn, srcAddr, localAddr string, body []byte) {
dstAddr, err := net.ResolveUDPAddr("udp", localAddr)
if err != nil {
log.Printf("can't resolve address: %s", err)
utils.CloseConn(inConn)
return
}
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr)
if err != nil {
log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
return
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write(body)
if err != nil {
log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
return
}
//log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 1024)
length, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
}
respBody := buf[0:length]
//log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
bs := utils.UDPPacket(srcAddr, respBody)
_, err = (*inConn).Write(bs)
if err != nil {
log.Printf("send udp response fail ,ERR:%s", err)
utils.CloseConn(inConn)
return
}
//log.Printf("send udp response success ,from:%s ,%d ,%v", dstAddr.String(), len(bs), bs)
}
func (s *TunnelClient) ServeConn(localAddr, ID string) {
var inConn, outConn net.Conn
var err error
for {
inConn, err = s.GetInConn(CONN_CLIENT, ID)
if err != nil {
utils.CloseConn(&inConn)
log.Printf("connection err: %s, retrying...", err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
i := 0
for {
i++
outConn, err = utils.ConnectHost(localAddr, *s.cfg.Timeout)
if err == nil || i == 3 {
break
} else {
if i == 3 {
log.Printf("connect to %s err: %s, retrying...", localAddr, err)
time.Sleep(2 * time.Second)
continue
}
}
}
if err != nil {
utils.CloseConn(&inConn)
utils.CloseConn(&outConn)
log.Printf("build connection error, err: %s", err)
return
}
utils.IoBind(inConn, outConn, func(err error) {
log.Printf("conn %s released", ID)
utils.CloseConn(&inConn)
utils.CloseConn(&outConn)
}, func(i int, b bool) {}, 0)
log.Printf("conn %s created", ID)
}

288
services/tunnel_server.go Normal file
View File

@ -0,0 +1,288 @@
package services
import (
"bytes"
"crypto/tls"
"encoding/binary"
"fmt"
"io"
"log"
"net"
"proxy/utils"
"runtime/debug"
"strconv"
"strings"
"time"
)
type TunnelServer struct {
cfg TunnelServerArgs
udpChn chan UDPItem
sc utils.ServerChannel
}
type TunnelServerManager struct {
cfg TunnelServerArgs
udpChn chan UDPItem
sc utils.ServerChannel
}
func NewTunnelServerManager() Service {
return &TunnelServerManager{
cfg: TunnelServerArgs{},
udpChn: make(chan UDPItem, 50000),
}
}
func (s *TunnelServerManager) Start(args interface{}) (err error) {
s.cfg = args.(TunnelServerArgs)
if *s.cfg.Parent != "" {
log.Printf("use tls parent %s", *s.cfg.Parent)
} else {
log.Fatalf("parent required")
}
//log.Printf("route:%v", *s.cfg.Route)
for _, _info := range *s.cfg.Route {
IsUDP := *s.cfg.IsUDP
if strings.HasPrefix(_info, "udp://") {
IsUDP = true
}
info := strings.TrimPrefix(_info, "udp://")
info = strings.TrimPrefix(info, "tcp://")
_routeInfo := strings.Split(info, "@")
server := NewTunnelServer()
local := _routeInfo[0]
remote := _routeInfo[1]
KEY := *s.cfg.Key
if strings.HasPrefix(remote, "[") {
KEY = remote[1:strings.LastIndex(remote, "]")]
remote = remote[strings.LastIndex(remote, "]")+1:]
}
if strings.HasPrefix(remote, ":") {
remote = fmt.Sprintf("127.0.0.1%s", remote)
}
err = server.Start(TunnelServerArgs{
CertBytes: s.cfg.CertBytes,
KeyBytes: s.cfg.KeyBytes,
Parent: s.cfg.Parent,
Local: &local,
IsUDP: &IsUDP,
Remote: &remote,
Key: &KEY,
Timeout: s.cfg.Timeout,
})
if err != nil {
return
}
}
return
}
func (s *TunnelServerManager) Clean() {
}
func NewTunnelServer() Service {
return &TunnelServer{
cfg: TunnelServerArgs{},
udpChn: make(chan UDPItem, 50000),
}
}
type UDPItem struct {
packet *[]byte
localAddr *net.UDPAddr
srcAddr *net.UDPAddr
}
func (s *TunnelServer) InitService() {
s.UDPConnDeamon()
}
func (s *TunnelServer) CheckArgs() {
if *s.cfg.Remote == "" {
log.Fatalf("remote required")
}
if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" {
log.Fatalf("cert and key file required")
}
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
func (s *TunnelServer) StopService() {
}
func (s *TunnelServer) Start(args interface{}) (err error) {
s.cfg = args.(TunnelServerArgs)
s.CheckArgs()
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
s.sc = utils.NewServerChannel(host, p)
if *s.cfg.IsUDP {
err = s.sc.ListenUDP(func(packet []byte, localAddr, srcAddr *net.UDPAddr) {
s.udpChn <- UDPItem{
packet: &packet,
localAddr: localAddr,
srcAddr: srcAddr,
}
})
if err != nil {
return
}
log.Printf("proxy on udp tunnel server mode %s", (*s.sc.UDPListener).LocalAddr())
} else {
err = s.sc.ListenTCP(func(inConn net.Conn) {
defer func() {
if err := recover(); err != nil {
log.Printf("tserver conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var outConn net.Conn
var ID string
for {
outConn, ID, err = s.GetOutConn("")
if err != nil {
utils.CloseConn(&outConn)
log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err)
time.Sleep(time.Second * 3)
continue
} else {
break
}
}
// hb := utils.NewHeartbeatReadWriter(&outConn, 3, func(err error, hb *utils.HeartbeatReadWriter) {
// log.Printf("%s conn %s to bridge released", *s.cfg.Key, ID)
// hb.Close()
// })
// utils.IoBind(inConn, &hb, func(err error) {
utils.IoBind(inConn, outConn, func(err error) {
utils.CloseConn(&outConn)
utils.CloseConn(&inConn)
log.Printf("%s conn %s released", *s.cfg.Key, ID)
}, func(i int, b bool) {}, 0)
log.Printf("%s conn %s created", *s.cfg.Key, ID)
})
if err != nil {
return
}
log.Printf("proxy on tunnel server mode %s", (*s.sc.Listener).Addr())
}
return
}
func (s *TunnelServer) Clean() {
s.StopService()
}
func (s *TunnelServer) GetOutConn(id string) (outConn net.Conn, ID string, err error) {
outConn, err = s.GetConn()
if err != nil {
log.Printf("connection err: %s", err)
return
}
keyBytes := []byte(*s.cfg.Key)
keyLength := uint16(len(keyBytes))
ID = utils.Uniqueid()
IDBytes := []byte(ID)
if id != "" {
ID = id
IDBytes = []byte(id)
}
IDLength := uint16(len(IDBytes))
remoteAddr := []byte("tcp:" + *s.cfg.Remote)
if *s.cfg.IsUDP {
remoteAddr = []byte("udp:" + *s.cfg.Remote)
}
remoteAddrLength := uint16(len(remoteAddr))
pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, CONN_SERVER)
binary.Write(pkg, binary.LittleEndian, keyLength)
binary.Write(pkg, binary.LittleEndian, keyBytes)
binary.Write(pkg, binary.LittleEndian, IDLength)
binary.Write(pkg, binary.LittleEndian, IDBytes)
binary.Write(pkg, binary.LittleEndian, remoteAddrLength)
binary.Write(pkg, binary.LittleEndian, remoteAddr)
_, err = outConn.Write(pkg.Bytes())
if err != nil {
log.Printf("write connection data err: %s ,retrying...", err)
utils.CloseConn(&outConn)
return
}
return
}
func (s *TunnelServer) GetConn() (conn net.Conn, err error) {
var _conn tls.Conn
_conn, err = utils.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes)
if err == nil {
conn = net.Conn(&_conn)
}
return
}
func (s *TunnelServer) UDPConnDeamon() {
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("udp conn deamon crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var outConn net.Conn
// var hb utils.HeartbeatReadWriter
var ID string
// var cmdChn = make(chan bool, 1000)
var err error
for {
item := <-s.udpChn
RETRY:
if outConn == nil {
for {
outConn, ID, err = s.GetOutConn("")
if err != nil {
// cmdChn <- true
outConn = nil
utils.CloseConn(&outConn)
log.Printf("connect to %s fail, err: %s, retrying...", *s.cfg.Parent, err)
time.Sleep(time.Second * 3)
continue
} else {
go func(outConn net.Conn, ID string) {
go func() {
// <-cmdChn
// outConn.Close()
}()
for {
srcAddrFromConn, body, err := utils.ReadUDPPacket(outConn)
if err == io.EOF || err == io.ErrUnexpectedEOF {
log.Printf("UDP deamon connection %s exited", ID)
break
}
if err != nil {
log.Printf("parse revecived udp packet fail, err: %s ,%v", err, body)
continue
}
//log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn)
_srcAddr := strings.Split(srcAddrFromConn, ":")
if len(_srcAddr) != 2 {
log.Printf("parse revecived udp packet fail, addr error : %s", srcAddrFromConn)
continue
}
port, _ := strconv.Atoi(_srcAddr[1])
dstAddr := &net.UDPAddr{IP: net.ParseIP(_srcAddr[0]), Port: port}
_, err = s.sc.UDPListener.WriteToUDP(body, dstAddr)
if err != nil {
log.Printf("udp response to local %s fail,ERR:%s", srcAddrFromConn, err)
continue
}
//log.Printf("udp response to local %s success , %v", srcAddrFromConn, body)
}
}(outConn, ID)
break
}
}
}
outConn.SetWriteDeadline(time.Now().Add(time.Second))
_, err = outConn.Write(utils.UDPPacket(item.srcAddr.String(), *item.packet))
outConn.SetWriteDeadline(time.Time{})
if err != nil {
utils.CloseConn(&outConn)
outConn = nil
log.Printf("write udp packet to %s fail ,flush err:%s ,retrying...", *s.cfg.Parent, err)
goto RETRY
}
//log.Printf("write packet %v", *item.packet)
}
}()
}

218
services/udp.go Normal file
View File

@ -0,0 +1,218 @@
package services
import (
"bufio"
"fmt"
"hash/crc32"
"io"
"log"
"net"
"proxy/utils"
"runtime/debug"
"strconv"
"strings"
"time"
)
type UDP struct {
p utils.ConcurrentMap
outPool utils.OutPool
cfg UDPArgs
sc *utils.ServerChannel
}
func NewUDP() Service {
return &UDP{
outPool: utils.OutPool{},
p: utils.NewConcurrentMap(),
}
}
func (s *UDP) CheckArgs() {
if *s.cfg.Parent == "" {
log.Fatalf("parent required for udp %s", *s.cfg.Local)
}
if *s.cfg.ParentType == "" {
log.Fatalf("parent type unkown,use -T <tls|tcp>")
}
if *s.cfg.ParentType == "tls" {
s.cfg.CertBytes, s.cfg.KeyBytes = utils.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile)
}
}
func (s *UDP) InitService() {
if *s.cfg.ParentType != TYPE_UDP {
s.InitOutConnPool()
}
}
func (s *UDP) StopService() {
if s.outPool.Pool != nil {
s.outPool.Pool.ReleaseAll()
}
}
func (s *UDP) Start(args interface{}) (err error) {
s.cfg = args.(UDPArgs)
s.CheckArgs()
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
s.InitService()
host, port, _ := net.SplitHostPort(*s.cfg.Local)
p, _ := strconv.Atoi(port)
sc := utils.NewServerChannel(host, p)
s.sc = &sc
err = sc.ListenUDP(s.callback)
if err != nil {
return
}
log.Printf("udp proxy on %s", (*sc.UDPListener).LocalAddr())
return
}
func (s *UDP) Clean() {
s.StopService()
}
func (s *UDP) callback(packet []byte, localAddr, srcAddr *net.UDPAddr) {
defer func() {
if err := recover(); err != nil {
log.Printf("udp conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
var err error
switch *s.cfg.ParentType {
case TYPE_TCP:
fallthrough
case TYPE_TLS:
err = s.OutToTCP(packet, localAddr, srcAddr)
case TYPE_UDP:
err = s.OutToUDP(packet, localAddr, srcAddr)
default:
err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType)
}
if err != nil {
log.Printf("connect to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err)
}
}
func (s *UDP) GetConn(connKey string) (conn net.Conn, isNew bool, err error) {
isNew = !s.p.Has(connKey)
var _conn interface{}
if isNew {
_conn, err = s.outPool.Pool.Get()
if err != nil {
return nil, false, err
}
s.p.Set(connKey, _conn)
} else {
_conn, _ = s.p.Get(connKey)
}
conn = _conn.(net.Conn)
return
}
func (s *UDP) OutToTCP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) {
numLocal := crc32.ChecksumIEEE([]byte(localAddr.String()))
numSrc := crc32.ChecksumIEEE([]byte(srcAddr.String()))
mod := uint32(*s.cfg.PoolSize)
if mod == 0 {
mod = 10
}
connKey := uint64((numLocal/10)*10 + numSrc%mod)
conn, isNew, err := s.GetConn(fmt.Sprintf("%d", connKey))
if err != nil {
log.Printf("upd get conn to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err)
return
}
if isNew {
go func() {
defer func() {
if err := recover(); err != nil {
log.Printf("udp conn handler out to tcp crashed with err : %s \nstack: %s", err, string(debug.Stack()))
}
}()
log.Printf("conn %d created , local: %s", connKey, srcAddr.String())
for {
srcAddrFromConn, body, err := utils.ReadUDPPacket(bufio.NewReader(conn))
if err == io.EOF || err == io.ErrUnexpectedEOF {
//log.Printf("connection %d released", connKey)
s.p.Remove(fmt.Sprintf("%d", connKey))
break
}
if err != nil {
log.Printf("parse revecived udp packet fail, err: %s", err)
continue
}
//log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn)
_srcAddr := strings.Split(srcAddrFromConn, ":")
if len(_srcAddr) != 2 {
log.Printf("parse revecived udp packet fail, addr error : %s", srcAddrFromConn)
continue
}
port, _ := strconv.Atoi(_srcAddr[1])
dstAddr := &net.UDPAddr{IP: net.ParseIP(_srcAddr[0]), Port: port}
_, err = s.sc.UDPListener.WriteToUDP(body, dstAddr)
if err != nil {
log.Printf("udp response to local %s fail,ERR:%s", srcAddr, err)
continue
}
//log.Printf("udp response to local %s success", srcAddr)
}
}()
}
//log.Printf("select conn %d , local: %s", connKey, srcAddr.String())
writer := bufio.NewWriter(conn)
//fmt.Println(conn, writer)
writer.Write(utils.UDPPacket(srcAddr.String(), packet))
err = writer.Flush()
if err != nil {
log.Printf("write udp packet to %s fail ,flush err:%s", *s.cfg.Parent, err)
return
}
//log.Printf("write packet %v", packet)
return
}
func (s *UDP) OutToUDP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) {
//log.Printf("udp packet revecived:%s,%v", srcAddr, packet)
dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Parent)
if err != nil {
log.Printf("resolve udp addr %s fail fail,ERR:%s", dstAddr.String(), err)
return
}
clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr)
if err != nil {
log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err)
return
}
conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout)))
_, err = conn.Write(packet)
if err != nil {
log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err)
return
}
//log.Printf("send udp packet to %s success", dstAddr.String())
buf := make([]byte, 512)
len, _, err := conn.ReadFromUDP(buf)
if err != nil {
log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err)
return
}
//log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody)
_, err = s.sc.UDPListener.WriteToUDP(buf[0:len], srcAddr)
if err != nil {
log.Printf("send udp response to cluster fail ,ERR:%s", err)
return
}
//log.Printf("send udp response to cluster success ,from:%s", dstAddr.String())
return
}
func (s *UDP) InitOutConnPool() {
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP {
//dur int, isTLS bool, certBytes, keyBytes []byte,
//parent string, timeout int, InitialCap int, MaxCap int
s.outPool = utils.NewOutPool(
*s.cfg.CheckParentInterval,
*s.cfg.ParentType == TYPE_TLS,
s.cfg.CertBytes, s.cfg.KeyBytes,
*s.cfg.Parent,
*s.cfg.Timeout,
*s.cfg.PoolSize,
*s.cfg.PoolSize*2,
)
}
}

384
utils/functions.go Executable file
View File

@ -0,0 +1,384 @@
package utils
import (
"bufio"
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/binary"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"math/rand"
"net"
"net/http"
"os"
"os/exec"
"sync"
"runtime/debug"
"strconv"
"strings"
"time"
)
func IoBind(dst io.ReadWriter, src io.ReadWriter, fn func(err error), cfn func(count int, isPositive bool), bytesPreSec float64) {
var one = &sync.Once{}
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("IoBind crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
var err error
if bytesPreSec > 0 {
newreader := NewReader(src)
newreader.SetRateLimit(bytesPreSec)
_, err = ioCopy(dst, newreader, func(c int) {
cfn(c, false)
})
} else {
_, err = ioCopy(dst, src, func(c int) {
cfn(c, false)
})
}
if err != nil {
one.Do(func() {
fn(err)
})
}
}()
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("IoBind crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
var err error
if bytesPreSec > 0 {
newReader := NewReader(dst)
newReader.SetRateLimit(bytesPreSec)
_, err = ioCopy(src, newReader, func(c int) {
cfn(c, true)
})
} else {
_, err = ioCopy(src, dst, func(c int) {
cfn(c, true)
})
}
if err != nil {
one.Do(func() {
fn(err)
})
}
}()
}
func ioCopy(dst io.Writer, src io.Reader, fn ...func(count int)) (written int64, err error) {
buf := make([]byte, 32*1024)
for {
nr, er := src.Read(buf)
if nr > 0 {
nw, ew := dst.Write(buf[0:nr])
if nw > 0 {
written += int64(nw)
if len(fn) == 1 {
fn[0](nw)
}
}
if ew != nil {
err = ew
break
}
if nr != nw {
err = io.ErrShortWrite
break
}
}
if er != nil {
err = er
break
}
}
return written, err
}
func TlsConnectHost(host string, timeout int, certBytes, keyBytes []byte) (conn tls.Conn, err error) {
h := strings.Split(host, ":")
port, _ := strconv.Atoi(h[1])
return TlsConnect(h[0], port, timeout, certBytes, keyBytes)
}
func TlsConnect(host string, port, timeout int, certBytes, keyBytes []byte) (conn tls.Conn, err error) {
conf, err := getRequestTlsConfig(certBytes, keyBytes)
if err != nil {
return
}
_conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), time.Duration(timeout)*time.Millisecond)
if err != nil {
return
}
return *tls.Client(_conn, conf), err
}
func getRequestTlsConfig(certBytes, keyBytes []byte) (conf *tls.Config, err error) {
var cert tls.Certificate
cert, err = tls.X509KeyPair(certBytes, keyBytes)
if err != nil {
return
}
serverCertPool := x509.NewCertPool()
ok := serverCertPool.AppendCertsFromPEM(certBytes)
if !ok {
err = errors.New("failed to parse root certificate")
}
conf = &tls.Config{
RootCAs: serverCertPool,
Certificates: []tls.Certificate{cert},
ServerName: "proxy",
InsecureSkipVerify: false,
}
return
}
func ConnectHost(hostAndPort string, timeout int) (conn net.Conn, err error) {
conn, err = net.DialTimeout("tcp", hostAndPort, time.Duration(timeout)*time.Millisecond)
return
}
func ListenTls(ip string, port int, certBytes, keyBytes []byte) (ln *net.Listener, err error) {
var cert tls.Certificate
cert, err = tls.X509KeyPair(certBytes, keyBytes)
if err != nil {
return
}
clientCertPool := x509.NewCertPool()
ok := clientCertPool.AppendCertsFromPEM(certBytes)
if !ok {
err = errors.New("failed to parse root certificate")
}
config := &tls.Config{
ClientCAs: clientCertPool,
ServerName: "proxy",
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
}
_ln, err := tls.Listen("tcp", fmt.Sprintf("%s:%d", ip, port), config)
if err == nil {
ln = &_ln
}
return
}
func PathExists(_path string) bool {
_, err := os.Stat(_path)
if err != nil && os.IsNotExist(err) {
return false
}
return true
}
func HTTPGet(URL string, timeout int) (err error) {
tr := &http.Transport{}
var resp *http.Response
var client *http.Client
defer func() {
if resp != nil && resp.Body != nil {
resp.Body.Close()
}
tr.CloseIdleConnections()
}()
client = &http.Client{Timeout: time.Millisecond * time.Duration(timeout), Transport: tr}
resp, err = client.Get(URL)
if err != nil {
return
}
return
}
func CloseConn(conn *net.Conn) {
if conn != nil && *conn != nil {
(*conn).SetDeadline(time.Now().Add(time.Millisecond))
(*conn).Close()
}
}
func Keygen() (err error) {
cmd := exec.Command("sh", "-c", "openssl genrsa -out proxy.key 2048")
out, err := cmd.CombinedOutput()
if err != nil {
log.Printf("err:%s", err)
return
}
fmt.Println(string(out))
cmd = exec.Command("sh", "-c", `openssl req -new -key proxy.key -x509 -days 3650 -out proxy.crt -subj /C=CN/ST=BJ/O="Localhost Ltd"/CN=proxy`)
out, err = cmd.CombinedOutput()
if err != nil {
log.Printf("err:%s", err)
return
}
fmt.Println(string(out))
return
}
func GetAllInterfaceAddr() ([]net.IP, error) {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
addresses := []net.IP{}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
// if iface.Flags&net.FlagLoopback != 0 {
// continue // loopback interface
// }
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
// if ip == nil || ip.IsLoopback() {
// continue
// }
ip = ip.To4()
if ip == nil {
continue // not an ipv4 address
}
addresses = append(addresses, ip)
}
}
if len(addresses) == 0 {
return nil, fmt.Errorf("no address Found, net.InterfaceAddrs: %v", addresses)
}
//only need first
return addresses, nil
}
func UDPPacket(srcAddr string, packet []byte) []byte {
addrBytes := []byte(srcAddr)
addrLength := uint16(len(addrBytes))
bodyLength := uint16(len(packet))
//log.Printf("build packet : addr len %d, body len %d", addrLength, bodyLength)
pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, addrLength)
binary.Write(pkg, binary.LittleEndian, addrBytes)
binary.Write(pkg, binary.LittleEndian, bodyLength)
binary.Write(pkg, binary.LittleEndian, packet)
return pkg.Bytes()
}
func ReadUDPPacket(_reader io.Reader) (srcAddr string, packet []byte, err error) {
reader := bufio.NewReader(_reader)
var addrLength uint16
var bodyLength uint16
err = binary.Read(reader, binary.LittleEndian, &addrLength)
if err != nil {
return
}
_srcAddr := make([]byte, addrLength)
n, err := reader.Read(_srcAddr)
if err != nil {
return
}
if n != int(addrLength) {
err = fmt.Errorf("n != int(addrLength), %d,%d", n, addrLength)
return
}
srcAddr = string(_srcAddr)
err = binary.Read(reader, binary.LittleEndian, &bodyLength)
if err != nil {
return
}
packet = make([]byte, bodyLength)
n, err = reader.Read(packet)
if err != nil {
return
}
if n != int(bodyLength) {
err = fmt.Errorf("n != int(bodyLength), %d,%d", n, bodyLength)
return
}
return
}
func Uniqueid() string {
var src = rand.NewSource(time.Now().UnixNano())
s := fmt.Sprintf("%d", src.Int63())
return s[len(s)-5:len(s)-1] + fmt.Sprintf("%d", uint64(time.Now().UnixNano()))[8:]
}
func TlsBytes(cert, key string) (certBytes, keyBytes []byte) {
certBytes, err := ioutil.ReadFile(cert)
if err != nil {
log.Fatalf("err : %s", err)
return
}
keyBytes, err = ioutil.ReadFile(key)
if err != nil {
log.Fatalf("err : %s", err)
return
}
return
}
// type sockaddr struct {
// family uint16
// data [14]byte
// }
// const SO_ORIGINAL_DST = 80
// realServerAddress returns an intercepted connection's original destination.
// func realServerAddress(conn *net.Conn) (string, error) {
// tcpConn, ok := (*conn).(*net.TCPConn)
// if !ok {
// return "", errors.New("not a TCPConn")
// }
// file, err := tcpConn.File()
// if err != nil {
// return "", err
// }
// // To avoid potential problems from making the socket non-blocking.
// tcpConn.Close()
// *conn, err = net.FileConn(file)
// if err != nil {
// return "", err
// }
// defer file.Close()
// fd := file.Fd()
// var addr sockaddr
// size := uint32(unsafe.Sizeof(addr))
// err = getsockopt(int(fd), syscall.SOL_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), &size)
// if err != nil {
// return "", err
// }
// var ip net.IP
// switch addr.family {
// case syscall.AF_INET:
// ip = addr.data[2:6]
// default:
// return "", errors.New("unrecognized address family")
// }
// port := int(addr.data[0])<<8 + int(addr.data[1])
// return net.JoinHostPort(ip.String(), strconv.Itoa(port)), nil
// }
// func getsockopt(s int, level int, name int, val uintptr, vallen *uint32) (err error) {
// _, _, e1 := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
// if e1 != 0 {
// err = e1
// }
// return
// }

97
utils/io-limiter.go Normal file
View File

@ -0,0 +1,97 @@
package utils
import (
"context"
"io"
"time"
"golang.org/x/time/rate"
)
const burstLimit = 1000 * 1000 * 1000
type Reader struct {
r io.Reader
limiter *rate.Limiter
ctx context.Context
}
type Writer struct {
w io.Writer
limiter *rate.Limiter
ctx context.Context
}
// NewReader returns a reader that implements io.Reader with rate limiting.
func NewReader(r io.Reader) *Reader {
return &Reader{
r: r,
ctx: context.Background(),
}
}
// NewReaderWithContext returns a reader that implements io.Reader with rate limiting.
func NewReaderWithContext(r io.Reader, ctx context.Context) *Reader {
return &Reader{
r: r,
ctx: ctx,
}
}
// NewWriter returns a writer that implements io.Writer with rate limiting.
func NewWriter(w io.Writer) *Writer {
return &Writer{
w: w,
ctx: context.Background(),
}
}
// NewWriterWithContext returns a writer that implements io.Writer with rate limiting.
func NewWriterWithContext(w io.Writer, ctx context.Context) *Writer {
return &Writer{
w: w,
ctx: ctx,
}
}
// SetRateLimit sets rate limit (bytes/sec) to the reader.
func (s *Reader) SetRateLimit(bytesPerSec float64) {
s.limiter = rate.NewLimiter(rate.Limit(bytesPerSec), burstLimit)
s.limiter.AllowN(time.Now(), burstLimit) // spend initial burst
}
// Read reads bytes into p.
func (s *Reader) Read(p []byte) (int, error) {
if s.limiter == nil {
return s.r.Read(p)
}
n, err := s.r.Read(p)
if err != nil {
return n, err
}
if err := s.limiter.WaitN(s.ctx, n); err != nil {
return n, err
}
return n, nil
}
// SetRateLimit sets rate limit (bytes/sec) to the writer.
func (s *Writer) SetRateLimit(bytesPerSec float64) {
s.limiter = rate.NewLimiter(rate.Limit(bytesPerSec), burstLimit)
s.limiter.AllowN(time.Now(), burstLimit) // spend initial burst
}
// Write writes bytes from p.
func (s *Writer) Write(p []byte) (int, error) {
if s.limiter == nil {
return s.w.Write(p)
}
n, err := s.w.Write(p)
if err != nil {
return n, err
}
if err := s.limiter.WaitN(s.ctx, n); err != nil {
return n, err
}
return n, err
}

315
utils/map.go Normal file
View File

@ -0,0 +1,315 @@
package utils
import (
"encoding/json"
"sync"
)
var SHARD_COUNT = 32
// A "thread" safe map of type string:Anything.
// To avoid lock bottlenecks this map is dived to several (SHARD_COUNT) map shards.
type ConcurrentMap []*ConcurrentMapShared
// A "thread" safe string to anything map.
type ConcurrentMapShared struct {
items map[string]interface{}
sync.RWMutex // Read Write mutex, guards access to internal map.
}
// Creates a new concurrent map.
func NewConcurrentMap() ConcurrentMap {
m := make(ConcurrentMap, SHARD_COUNT)
for i := 0; i < SHARD_COUNT; i++ {
m[i] = &ConcurrentMapShared{items: make(map[string]interface{})}
}
return m
}
// Returns shard under given key
func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared {
return m[uint(fnv32(key))%uint(SHARD_COUNT)]
}
func (m ConcurrentMap) MSet(data map[string]interface{}) {
for key, value := range data {
shard := m.GetShard(key)
shard.Lock()
shard.items[key] = value
shard.Unlock()
}
}
// Sets the given value under the specified key.
func (m ConcurrentMap) Set(key string, value interface{}) {
// Get map shard.
shard := m.GetShard(key)
shard.Lock()
shard.items[key] = value
shard.Unlock()
}
// Callback to return new element to be inserted into the map
// It is called while lock is held, therefore it MUST NOT
// try to access other keys in same map, as it can lead to deadlock since
// Go sync.RWLock is not reentrant
type UpsertCb func(exist bool, valueInMap interface{}, newValue interface{}) interface{}
// Insert or Update - updates existing element or inserts a new one using UpsertCb
func (m ConcurrentMap) Upsert(key string, value interface{}, cb UpsertCb) (res interface{}) {
shard := m.GetShard(key)
shard.Lock()
v, ok := shard.items[key]
res = cb(ok, v, value)
shard.items[key] = res
shard.Unlock()
return res
}
// Sets the given value under the specified key if no value was associated with it.
func (m ConcurrentMap) SetIfAbsent(key string, value interface{}) bool {
// Get map shard.
shard := m.GetShard(key)
shard.Lock()
_, ok := shard.items[key]
if !ok {
shard.items[key] = value
}
shard.Unlock()
return !ok
}
// Retrieves an element from map under given key.
func (m ConcurrentMap) Get(key string) (interface{}, bool) {
// Get shard
shard := m.GetShard(key)
shard.RLock()
// Get item from shard.
val, ok := shard.items[key]
shard.RUnlock()
return val, ok
}
// Returns the number of elements within the map.
func (m ConcurrentMap) Count() int {
count := 0
for i := 0; i < SHARD_COUNT; i++ {
shard := m[i]
shard.RLock()
count += len(shard.items)
shard.RUnlock()
}
return count
}
// Looks up an item under specified key
func (m ConcurrentMap) Has(key string) bool {
// Get shard
shard := m.GetShard(key)
shard.RLock()
// See if element is within shard.
_, ok := shard.items[key]
shard.RUnlock()
return ok
}
// Removes an element from the map.
func (m ConcurrentMap) Remove(key string) {
// Try to get shard.
shard := m.GetShard(key)
shard.Lock()
delete(shard.items, key)
shard.Unlock()
}
// Removes an element from the map and returns it
func (m ConcurrentMap) Pop(key string) (v interface{}, exists bool) {
// Try to get shard.
shard := m.GetShard(key)
shard.Lock()
v, exists = shard.items[key]
delete(shard.items, key)
shard.Unlock()
return v, exists
}
// Checks if map is empty.
func (m ConcurrentMap) IsEmpty() bool {
return m.Count() == 0
}
// Used by the Iter & IterBuffered functions to wrap two variables together over a channel,
type Tuple struct {
Key string
Val interface{}
}
// Returns an iterator which could be used in a for range loop.
//
// Deprecated: using IterBuffered() will get a better performence
func (m ConcurrentMap) Iter() <-chan Tuple {
chans := snapshot(m)
ch := make(chan Tuple)
go fanIn(chans, ch)
return ch
}
// Returns a buffered iterator which could be used in a for range loop.
func (m ConcurrentMap) IterBuffered() <-chan Tuple {
chans := snapshot(m)
total := 0
for _, c := range chans {
total += cap(c)
}
ch := make(chan Tuple, total)
go fanIn(chans, ch)
return ch
}
// Returns a array of channels that contains elements in each shard,
// which likely takes a snapshot of `m`.
// It returns once the size of each buffered channel is determined,
// before all the channels are populated using goroutines.
func snapshot(m ConcurrentMap) (chans []chan Tuple) {
chans = make([]chan Tuple, SHARD_COUNT)
wg := sync.WaitGroup{}
wg.Add(SHARD_COUNT)
// Foreach shard.
for index, shard := range m {
go func(index int, shard *ConcurrentMapShared) {
// Foreach key, value pair.
shard.RLock()
chans[index] = make(chan Tuple, len(shard.items))
wg.Done()
for key, val := range shard.items {
chans[index] <- Tuple{key, val}
}
shard.RUnlock()
close(chans[index])
}(index, shard)
}
wg.Wait()
return chans
}
// fanIn reads elements from channels `chans` into channel `out`
func fanIn(chans []chan Tuple, out chan Tuple) {
wg := sync.WaitGroup{}
wg.Add(len(chans))
for _, ch := range chans {
go func(ch chan Tuple) {
for t := range ch {
out <- t
}
wg.Done()
}(ch)
}
wg.Wait()
close(out)
}
// Returns all items as map[string]interface{}
func (m ConcurrentMap) Items() map[string]interface{} {
tmp := make(map[string]interface{})
// Insert items to temporary map.
for item := range m.IterBuffered() {
tmp[item.Key] = item.Val
}
return tmp
}
// Iterator callback,called for every key,value found in
// maps. RLock is held for all calls for a given shard
// therefore callback sess consistent view of a shard,
// but not across the shards
type IterCb func(key string, v interface{})
// Callback based iterator, cheapest way to read
// all elements in a map.
func (m ConcurrentMap) IterCb(fn IterCb) {
for idx := range m {
shard := (m)[idx]
shard.RLock()
for key, value := range shard.items {
fn(key, value)
}
shard.RUnlock()
}
}
// Return all keys as []string
func (m ConcurrentMap) Keys() []string {
count := m.Count()
ch := make(chan string, count)
go func() {
// Foreach shard.
wg := sync.WaitGroup{}
wg.Add(SHARD_COUNT)
for _, shard := range m {
go func(shard *ConcurrentMapShared) {
// Foreach key, value pair.
shard.RLock()
for key := range shard.items {
ch <- key
}
shard.RUnlock()
wg.Done()
}(shard)
}
wg.Wait()
close(ch)
}()
// Generate keys
keys := make([]string, 0, count)
for k := range ch {
keys = append(keys, k)
}
return keys
}
//Reviles ConcurrentMap "private" variables to json marshal.
func (m ConcurrentMap) MarshalJSON() ([]byte, error) {
// Create a temporary map, which will hold all item spread across shards.
tmp := make(map[string]interface{})
// Insert items to temporary map.
for item := range m.IterBuffered() {
tmp[item.Key] = item.Val
}
return json.Marshal(tmp)
}
func fnv32(key string) uint32 {
hash := uint32(2166136261)
const prime32 = uint32(16777619)
for i := 0; i < len(key); i++ {
hash *= prime32
hash ^= uint32(key[i])
}
return hash
}
// Concurrent map uses Interface{} as its value, therefor JSON Unmarshal
// will probably won't know which to type to unmarshal into, in such case
// we'll end up with a value of type map[string]interface{}, In most cases this isn't
// out value type, this is why we've decided to remove this functionality.
// func (m *ConcurrentMap) UnmarshalJSON(b []byte) (err error) {
// // Reverse process of Marshal.
// tmp := make(map[string]interface{})
// // Unmarshal into a single map.
// if err := json.Unmarshal(b, &tmp); err != nil {
// return nil
// }
// // foreach key,value pair in temporary map insert into our concurrent map.
// for key, val := range tmp {
// m.Set(key, val)
// }
// return nil
// }

145
utils/pool.go Executable file
View File

@ -0,0 +1,145 @@
package utils
import (
"log"
"sync"
"time"
)
//ConnPool to use
type ConnPool interface {
Get() (conn interface{}, err error)
Put(conn interface{})
ReleaseAll()
Len() (length int)
}
type poolConfig struct {
Factory func() (interface{}, error)
IsActive func(interface{}) bool
Release func(interface{})
InitialCap int
MaxCap int
}
func NewConnPool(poolConfig poolConfig) (pool ConnPool, err error) {
p := netPool{
config: poolConfig,
conns: make(chan interface{}, poolConfig.MaxCap),
lock: &sync.Mutex{},
}
//log.Printf("pool MaxCap:%d", poolConfig.MaxCap)
if poolConfig.MaxCap > 0 {
err = p.initAutoFill(false)
if err == nil {
p.initAutoFill(true)
}
}
return &p, nil
}
type netPool struct {
conns chan interface{}
lock *sync.Mutex
config poolConfig
}
func (p *netPool) initAutoFill(async bool) (err error) {
var worker = func() (err error) {
for {
//log.Printf("pool fill: %v , len: %d", p.Len() <= p.config.InitialCap/2, p.Len())
if p.Len() <= p.config.InitialCap/2 {
p.lock.Lock()
errN := 0
for i := 0; i < p.config.InitialCap; i++ {
c, err := p.config.Factory()
if err != nil {
errN++
if async {
continue
} else {
p.lock.Unlock()
return err
}
}
select {
case p.conns <- c:
default:
p.config.Release(c)
break
}
if p.Len() >= p.config.InitialCap {
break
}
}
if errN > 0 {
log.Printf("fill conn pool fail , ERRN:%d", errN)
}
p.lock.Unlock()
}
if !async {
return
}
time.Sleep(time.Second * 2)
}
}
if async {
go worker()
} else {
err = worker()
}
return
}
func (p *netPool) Get() (conn interface{}, err error) {
// defer func() {
// log.Printf("pool len : %d", p.Len())
// }()
p.lock.Lock()
defer p.lock.Unlock()
// for {
select {
case conn = <-p.conns:
if p.config.IsActive(conn) {
return
}
p.config.Release(conn)
default:
conn, err = p.config.Factory()
if err != nil {
return nil, err
}
return conn, nil
}
// }
return
}
func (p *netPool) Put(conn interface{}) {
if conn == nil {
return
}
p.lock.Lock()
defer p.lock.Unlock()
if !p.config.IsActive(conn) {
p.config.Release(conn)
}
select {
case p.conns <- conn:
default:
p.config.Release(conn)
}
}
func (p *netPool) ReleaseAll() {
p.lock.Lock()
defer p.lock.Unlock()
close(p.conns)
for c := range p.conns {
p.config.Release(c)
}
p.conns = make(chan interface{}, p.config.InitialCap)
}
func (p *netPool) Len() (length int) {
return len(p.conns)
}

138
utils/serve-channel.go Normal file
View File

@ -0,0 +1,138 @@
package utils
import (
"fmt"
"log"
"net"
"runtime/debug"
"strconv"
)
type ServerChannel struct {
ip string
port int
Listener *net.Listener
UDPListener *net.UDPConn
errAcceptHandler func(err error)
}
func NewServerChannel(ip string, port int) ServerChannel {
return ServerChannel{
ip: ip,
port: port,
errAcceptHandler: func(err error) {
fmt.Printf("accept error , ERR:%s", err)
},
}
}
func NewServerChannelHost(host string) ServerChannel {
h, port, _ := net.SplitHostPort(host)
p, _ := strconv.Atoi(port)
return ServerChannel{
ip: h,
port: p,
errAcceptHandler: func(err error) {
log.Printf("accept error , ERR:%s", err)
},
}
}
func (sc *ServerChannel) SetErrAcceptHandler(fn func(err error)) {
sc.errAcceptHandler = fn
}
func (sc *ServerChannel) ListenTls(certBytes, keyBytes []byte, fn func(conn net.Conn)) (err error) {
sc.Listener, err = ListenTls(sc.ip, sc.port, certBytes, keyBytes)
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("ListenTls crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
var conn net.Conn
conn, err = (*sc.Listener).Accept()
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(conn)
}()
} else {
sc.errAcceptHandler(err)
(*sc.Listener).Close()
break
}
}
}()
}
return
}
func (sc *ServerChannel) ListenTCP(fn func(conn net.Conn)) (err error) {
var l net.Listener
l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sc.ip, sc.port))
if err == nil {
sc.Listener = &l
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("ListenTCP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
var conn net.Conn
conn, err = (*sc.Listener).Accept()
if err == nil {
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(conn)
}()
} else {
sc.errAcceptHandler(err)
break
}
}
}()
}
return
}
func (sc *ServerChannel) ListenUDP(fn func(packet []byte, localAddr, srcAddr *net.UDPAddr)) (err error) {
addr := &net.UDPAddr{IP: net.ParseIP(sc.ip), Port: sc.port}
l, err := net.ListenUDP("udp", addr)
if err == nil {
sc.UDPListener = l
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("ListenUDP crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
for {
var buf = make([]byte, 2048)
n, srcAddr, err := (*sc.UDPListener).ReadFromUDP(buf)
if err == nil {
packet := buf[0:n]
go func() {
defer func() {
if e := recover(); e != nil {
log.Printf("udp data handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
}
}()
fn(packet, addr, srcAddr)
}()
} else {
sc.errAcceptHandler(err)
break
}
}
}()
}
return
}

614
utils/structs.go Normal file
View File

@ -0,0 +1,614 @@
package utils
import (
"bytes"
"crypto/tls"
"encoding/base64"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/url"
"strings"
"sync"
"time"
)
type Checker struct {
data ConcurrentMap
blockedMap ConcurrentMap
directMap ConcurrentMap
interval int64
timeout int
}
type CheckerItem struct {
IsHTTPS bool
Method string
URL string
Domain string
Host string
Data []byte
SuccessCount uint
FailCount uint
}
//NewChecker args:
//timeout : tcp timeout milliseconds ,connect to host
//interval: recheck domain interval seconds
func NewChecker(timeout int, interval int64, blockedFile, directFile string) Checker {
ch := Checker{
data: NewConcurrentMap(),
interval: interval,
timeout: timeout,
}
ch.blockedMap = ch.loadMap(blockedFile)
ch.directMap = ch.loadMap(directFile)
if !ch.blockedMap.IsEmpty() {
log.Printf("blocked file loaded , domains : %d", ch.blockedMap.Count())
}
if !ch.directMap.IsEmpty() {
log.Printf("direct file loaded , domains : %d", ch.directMap.Count())
}
ch.start()
return ch
}
func (c *Checker) loadMap(f string) (dataMap ConcurrentMap) {
dataMap = NewConcurrentMap()
if PathExists(f) {
_contents, err := ioutil.ReadFile(f)
if err != nil {
log.Printf("load file err:%s", err)
return
}
for _, line := range strings.Split(string(_contents), "\n") {
line = strings.Trim(line, "\r \t")
if line != "" {
dataMap.Set(line, true)
}
}
}
return
}
func (c *Checker) start() {
go func() {
for {
for _, v := range c.data.Items() {
go func(item CheckerItem) {
if c.isNeedCheck(item) {
//log.Printf("check %s", item.Domain)
var conn net.Conn
var err error
if item.IsHTTPS {
conn, err = ConnectHost(item.Host, c.timeout)
if err == nil {
conn.SetDeadline(time.Now().Add(time.Millisecond))
conn.Close()
}
} else {
err = HTTPGet(item.URL, c.timeout)
}
if err != nil {
item.FailCount = item.FailCount + 1
} else {
item.SuccessCount = item.SuccessCount + 1
}
c.data.Set(item.Host, item)
}
}(v.(CheckerItem))
}
time.Sleep(time.Second * time.Duration(c.interval))
}
}()
}
func (c *Checker) isNeedCheck(item CheckerItem) bool {
var minCount uint = 5
if (item.SuccessCount >= minCount && item.SuccessCount > item.FailCount) ||
(item.FailCount >= minCount && item.SuccessCount > item.FailCount) ||
c.domainIsInMap(item.Host, false) ||
c.domainIsInMap(item.Host, true) {
return false
}
return true
}
func (c *Checker) IsBlocked(address string) (blocked bool, failN, successN uint) {
if c.domainIsInMap(address, true) {
//log.Printf("%s in blocked ? true", address)
return true, 0, 0
}
if c.domainIsInMap(address, false) {
//log.Printf("%s in direct ? true", address)
return false, 0, 0
}
_item, ok := c.data.Get(address)
if !ok {
//log.Printf("%s not in map, blocked true", address)
return true, 0, 0
}
item := _item.(CheckerItem)
return item.FailCount >= item.SuccessCount, item.FailCount, item.SuccessCount
}
func (c *Checker) domainIsInMap(address string, blockedMap bool) bool {
u, err := url.Parse("http://" + address)
if err != nil {
log.Printf("blocked check , url parse err:%s", err)
return true
}
domainSlice := strings.Split(u.Hostname(), ".")
if len(domainSlice) > 1 {
subSlice := domainSlice[:len(domainSlice)-1]
topDomain := strings.Join(domainSlice[len(domainSlice)-1:], ".")
checkDomain := topDomain
for i := len(subSlice) - 1; i >= 0; i-- {
checkDomain = subSlice[i] + "." + checkDomain
if !blockedMap && c.directMap.Has(checkDomain) {
return true
}
if blockedMap && c.blockedMap.Has(checkDomain) {
return true
}
}
}
return false
}
func (c *Checker) Add(address string, isHTTPS bool, method, URL string, data []byte) {
if c.domainIsInMap(address, false) || c.domainIsInMap(address, true) {
return
}
if !isHTTPS && strings.ToLower(method) != "get" {
return
}
var item CheckerItem
u := strings.Split(address, ":")
item = CheckerItem{
URL: URL,
Domain: u[0],
Host: address,
Data: data,
IsHTTPS: isHTTPS,
Method: method,
}
c.data.SetIfAbsent(item.Host, item)
}
type BasicAuth struct {
data ConcurrentMap
}
func NewBasicAuth() BasicAuth {
return BasicAuth{
data: NewConcurrentMap(),
}
}
func (ba *BasicAuth) AddFromFile(file string) (n int, err error) {
_content, err := ioutil.ReadFile(file)
if err != nil {
return
}
userpassArr := strings.Split(strings.Replace(string(_content), "\r", "", -1), "\n")
for _, userpass := range userpassArr {
if strings.HasPrefix("#", userpass) {
continue
}
u := strings.Split(strings.Trim(userpass, " "), ":")
if len(u) == 2 {
ba.data.Set(u[0], u[1])
n++
}
}
return
}
func (ba *BasicAuth) Add(userpassArr []string) (n int) {
for _, userpass := range userpassArr {
u := strings.Split(userpass, ":")
if len(u) == 2 {
ba.data.Set(u[0], u[1])
n++
}
}
return
}
func (ba *BasicAuth) Check(userpass string) (ok bool) {
u := strings.Split(strings.Trim(userpass, " "), ":")
if len(u) == 2 {
if p, _ok := ba.data.Get(u[0]); _ok {
return p.(string) == u[1]
}
}
return
}
func (ba *BasicAuth) Total() (n int) {
n = ba.data.Count()
return
}
type HTTPRequest struct {
HeadBuf []byte
conn *net.Conn
Host string
Method string
URL string
hostOrURL string
isBasicAuth bool
basicAuth *BasicAuth
}
func NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth *BasicAuth) (req HTTPRequest, err error) {
buf := make([]byte, bufSize)
len := 0
req = HTTPRequest{
conn: inConn,
}
len, err = (*inConn).Read(buf[:])
if err != nil {
if err != io.EOF {
err = fmt.Errorf("http decoder read err:%s", err)
}
CloseConn(inConn)
return
}
req.HeadBuf = buf[:len]
index := bytes.IndexByte(req.HeadBuf, '\n')
if index == -1 {
err = fmt.Errorf("http decoder data line err:%s", string(req.HeadBuf)[:50])
CloseConn(inConn)
return
}
fmt.Sscanf(string(req.HeadBuf[:index]), "%s%s", &req.Method, &req.hostOrURL)
if req.Method == "" || req.hostOrURL == "" {
err = fmt.Errorf("http decoder data err:%s", string(req.HeadBuf)[:50])
CloseConn(inConn)
return
}
req.Method = strings.ToUpper(req.Method)
req.isBasicAuth = isBasicAuth
req.basicAuth = basicAuth
log.Printf("%s:%s", req.Method, req.hostOrURL)
if req.IsHTTPS() {
err = req.HTTPS()
} else {
err = req.HTTP()
}
return
}
func (req *HTTPRequest) HTTP() (err error) {
if req.isBasicAuth {
err = req.BasicAuth()
if err != nil {
return
}
}
req.URL, err = req.getHTTPURL()
if err == nil {
u, _ := url.Parse(req.URL)
req.Host = u.Host
req.addPortIfNot()
}
return
}
func (req *HTTPRequest) HTTPS() (err error) {
req.Host = req.hostOrURL
req.addPortIfNot()
//_, err = fmt.Fprint(*req.conn, "HTTP/1.1 200 Connection established\r\n\r\n")
return
}
func (req *HTTPRequest) HTTPSReply() (err error) {
_, err = fmt.Fprint(*req.conn, "HTTP/1.1 200 Connection established\r\n\r\n")
return
}
func (req *HTTPRequest) IsHTTPS() bool {
return req.Method == "CONNECT"
}
func (req *HTTPRequest) BasicAuth() (err error) {
//log.Printf("request :%s", string(b[:n]))
authorization, err := req.getHeader("Authorization")
if err != nil {
fmt.Fprint((*req.conn), "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"\"\r\n\r\nUnauthorized")
CloseConn(req.conn)
return
}
//log.Printf("Authorization:%s", authorization)
basic := strings.Fields(authorization)
if len(basic) != 2 {
err = fmt.Errorf("authorization data error,ERR:%s", authorization)
CloseConn(req.conn)
return
}
user, err := base64.StdEncoding.DecodeString(basic[1])
if err != nil {
err = fmt.Errorf("authorization data parse error,ERR:%s", err)
CloseConn(req.conn)
return
}
authOk := (*req.basicAuth).Check(string(user))
//log.Printf("auth %s,%v", string(user), authOk)
if !authOk {
fmt.Fprint((*req.conn), "HTTP/1.1 401 Unauthorized\r\n\r\nUnauthorized")
CloseConn(req.conn)
err = fmt.Errorf("basic auth fail")
return
}
return
}
func (req *HTTPRequest) getHTTPURL() (URL string, err error) {
if !strings.HasPrefix(req.hostOrURL, "/") {
return req.hostOrURL, nil
}
_host, err := req.getHeader("host")
if err != nil {
return
}
URL = fmt.Sprintf("http://%s%s", _host, req.hostOrURL)
return
}
func (req *HTTPRequest) getHeader(key string) (val string, err error) {
key = strings.ToUpper(key)
lines := strings.Split(string(req.HeadBuf), "\r\n")
for _, line := range lines {
line := strings.SplitN(strings.Trim(line, "\r\n "), ":", 2)
if len(line) == 2 {
k := strings.ToUpper(strings.Trim(line[0], " "))
v := strings.Trim(line[1], " ")
if key == k {
val = v
return
}
}
}
err = fmt.Errorf("can not find HOST header")
return
}
func (req *HTTPRequest) addPortIfNot() (newHost string) {
//newHost = req.Host
port := "80"
if req.IsHTTPS() {
port = "443"
}
if (!strings.HasPrefix(req.Host, "[") && strings.Index(req.Host, ":") == -1) || (strings.HasPrefix(req.Host, "[") && strings.HasSuffix(req.Host, "]")) {
//newHost = req.Host + ":" + port
//req.headBuf = []byte(strings.Replace(string(req.headBuf), req.Host, newHost, 1))
req.Host = req.Host + ":" + port
}
return
}
type OutPool struct {
Pool ConnPool
dur int
isTLS bool
certBytes []byte
keyBytes []byte
address string
timeout int
}
func NewOutPool(dur int, isTLS bool, certBytes, keyBytes []byte, address string, timeout int, InitialCap int, MaxCap int) (op OutPool) {
op = OutPool{
dur: dur,
isTLS: isTLS,
certBytes: certBytes,
keyBytes: keyBytes,
address: address,
timeout: timeout,
}
var err error
op.Pool, err = NewConnPool(poolConfig{
IsActive: func(conn interface{}) bool { return true },
Release: func(conn interface{}) {
if conn != nil {
conn.(net.Conn).SetDeadline(time.Now().Add(time.Millisecond))
conn.(net.Conn).Close()
// log.Println("conn released")
}
},
InitialCap: InitialCap,
MaxCap: MaxCap,
Factory: func() (conn interface{}, err error) {
conn, err = op.getConn()
return
},
})
if err != nil {
log.Fatalf("init conn pool fail ,%s", err)
} else {
if InitialCap > 0 {
log.Printf("init conn pool success")
op.initPoolDeamon()
} else {
log.Printf("conn pool closed")
}
}
return
}
func (op *OutPool) getConn() (conn interface{}, err error) {
if op.isTLS {
var _conn tls.Conn
_conn, err = TlsConnectHost(op.address, op.timeout, op.certBytes, op.keyBytes)
if err == nil {
conn = net.Conn(&_conn)
}
} else {
conn, err = ConnectHost(op.address, op.timeout)
}
return
}
func (op *OutPool) initPoolDeamon() {
go func() {
if op.dur <= 0 {
return
}
log.Printf("pool deamon started")
for {
time.Sleep(time.Second * time.Duration(op.dur))
conn, err := op.getConn()
if err != nil {
log.Printf("pool deamon err %s , release pool", err)
op.Pool.ReleaseAll()
} else {
conn.(net.Conn).SetDeadline(time.Now().Add(time.Millisecond))
conn.(net.Conn).Close()
}
}
}()
}
type HeartbeatData struct {
Data []byte
N int
Error error
}
type HeartbeatReadWriter struct {
conn *net.Conn
// rchn chan HeartbeatData
l *sync.Mutex
dur int
errHandler func(err error, hb *HeartbeatReadWriter)
once *sync.Once
datachn chan byte
// rbuf bytes.Buffer
// signal chan bool
rerrchn chan error
}
func NewHeartbeatReadWriter(conn *net.Conn, dur int, fn func(err error, hb *HeartbeatReadWriter)) (hrw HeartbeatReadWriter) {
hrw = HeartbeatReadWriter{
conn: conn,
l: &sync.Mutex{},
dur: dur,
// rchn: make(chan HeartbeatData, 10000),
// signal: make(chan bool, 1),
errHandler: fn,
datachn: make(chan byte, 4*1024),
once: &sync.Once{},
rerrchn: make(chan error, 1),
// rbuf: bytes.Buffer{},
}
hrw.heartbeat()
hrw.reader()
return
}
func (rw *HeartbeatReadWriter) Close() {
CloseConn(rw.conn)
}
func (rw *HeartbeatReadWriter) reader() {
go func() {
//log.Printf("heartbeat read started")
for {
n, data, err := rw.read()
if n == -1 {
continue
}
//log.Printf("n:%d , data:%s ,err:%s", n, string(data), err)
if err == nil {
//fmt.Printf("write data %s\n", string(data))
for _, b := range data {
rw.datachn <- b
}
}
if err != nil {
//log.Printf("heartbeat reader err: %s", err)
select {
case rw.rerrchn <- err:
default:
}
rw.once.Do(func() {
rw.errHandler(err, rw)
})
break
}
}
//log.Printf("heartbeat read exited")
}()
}
func (rw *HeartbeatReadWriter) read() (n int, data []byte, err error) {
var typ uint8
err = binary.Read((*rw.conn), binary.LittleEndian, &typ)
if err != nil {
return
}
if typ == 0 {
// log.Printf("heartbeat revecived")
n = -1
return
}
var dataLength uint32
binary.Read((*rw.conn), binary.LittleEndian, &dataLength)
_data := make([]byte, dataLength)
// log.Printf("dataLength:%d , data:%s", dataLength, string(data))
n, err = (*rw.conn).Read(_data)
//log.Printf("n:%d , data:%s ,err:%s", n, string(data), err)
if err != nil {
return
}
if uint32(n) != dataLength {
err = fmt.Errorf("read short data body")
return
}
data = _data[:n]
return
}
func (rw *HeartbeatReadWriter) heartbeat() {
go func() {
//log.Printf("heartbeat started")
for {
if rw.conn == nil || *rw.conn == nil {
//log.Printf("heartbeat err: conn nil")
break
}
rw.l.Lock()
_, err := (*rw.conn).Write([]byte{0})
rw.l.Unlock()
if err != nil {
//log.Printf("heartbeat err: %s", err)
rw.once.Do(func() {
rw.errHandler(err, rw)
})
break
} else {
// log.Printf("heartbeat send ok")
}
time.Sleep(time.Second * time.Duration(rw.dur))
}
//log.Printf("heartbeat exited")
}()
}
func (rw *HeartbeatReadWriter) Read(p []byte) (n int, err error) {
data := make([]byte, cap(p))
for i := 0; i < cap(p); i++ {
data[i] = <-rw.datachn
n++
//fmt.Printf("read %d %v\n", i, data[:n])
if len(rw.datachn) == 0 {
n = i + 1
copy(p, data[:n])
return
}
}
return
}
func (rw *HeartbeatReadWriter) Write(p []byte) (n int, err error) {
defer rw.l.Unlock()
rw.l.Lock()
pkg := new(bytes.Buffer)
binary.Write(pkg, binary.LittleEndian, uint8(1))
binary.Write(pkg, binary.LittleEndian, uint32(len(p)))
binary.Write(pkg, binary.LittleEndian, p)
bs := pkg.Bytes()
n, err = (*rw.conn).Write(bs)
if err == nil {
n = len(p)
}
return
}