Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
proxy
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
release-*
|
||||||
|
proxy.crt
|
||||||
|
proxy.key
|
||||||
23
CHANGELOG
Normal file
23
CHANGELOG
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
proxy更新日志:
|
||||||
|
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,websocket
|
||||||
|
3.增加了TCP代理模式,支持是否加密通讯.
|
||||||
|
4.优化了链接关闭逻辑,避免出现大量CLOSE_WAIT.
|
||||||
|
5.增加了黑白名单机制,更自由快速的访问.
|
||||||
|
6.优化了网站Block机制检测,判断更准确.
|
||||||
|
|
||||||
|
v1.0
|
||||||
|
1.始发版本,可以代理http,https.
|
||||||
115
config.go
Executable file
115
config.go
Executable file
@ -0,0 +1,115 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cfg = viper.New()
|
||||||
|
)
|
||||||
|
|
||||||
|
func initConfig() (err error) {
|
||||||
|
//define command line args
|
||||||
|
|
||||||
|
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
|
||||||
|
configFile := pflag.StringP("config", "c", "", "config file path")
|
||||||
|
|
||||||
|
pflag.BoolP("parent-tls", "X", false, "parent proxy is tls")
|
||||||
|
pflag.BoolP("local-tls", "x", false, "local proxy is tls")
|
||||||
|
version := pflag.BoolP("version", "v", false, "show version")
|
||||||
|
pflag.BoolP("tcp", "C", false, "proxy on tcp")
|
||||||
|
pflag.Bool("always", false, "always use parent proxy")
|
||||||
|
|
||||||
|
pflag.Int("check-proxy-interval", 3, "check if proxy is okay every interval seconds")
|
||||||
|
pflag.IntP("port", "p", 33080, "local port to listen")
|
||||||
|
pflag.IntP("check-timeout", "t", 3000, "chekc domain blocked , http request timeout milliseconds when connect to host")
|
||||||
|
pflag.IntP("tcp-timeout", "T", 2000, "tcp timeout milliseconds when connect to real server or parent proxy")
|
||||||
|
pflag.IntP("check-interval", "I", 10, "check domain if blocked every interval seconds")
|
||||||
|
pflag.IntP("pool-size", "s", 50, "conn pool size , which connect to parent proxy, zero: means turn off pool")
|
||||||
|
|
||||||
|
pflag.StringP("parent", "P", "", "parent proxy address")
|
||||||
|
pflag.StringP("ip", "i", "0.0.0.0", "local ip to bind")
|
||||||
|
pflag.StringP("cert", "f", "proxy.crt", "cert file for tls")
|
||||||
|
pflag.StringP("key", "k", "proxy.key", "key file for tls")
|
||||||
|
pflag.StringP("blocked", "b", "blocked", "blocked domain file , one domain each line")
|
||||||
|
pflag.StringP("direct", "d", "direct", "direct domain file , one domain each line")
|
||||||
|
pflag.StringP("auth-file", "F", "", "http basic auth file,\"username:password\" each line in file")
|
||||||
|
pflag.StringSliceP("auth", "a", []string{}, "http basic auth username and password,such as: \"user1:pass1,user2:pass2\"")
|
||||||
|
|
||||||
|
pflag.Parse()
|
||||||
|
|
||||||
|
cfg.BindPFlag("parent-tls", pflag.Lookup("parent-tls"))
|
||||||
|
cfg.BindPFlag("local-tls", pflag.Lookup("local-tls"))
|
||||||
|
cfg.BindPFlag("tcp", pflag.Lookup("tcp"))
|
||||||
|
cfg.BindPFlag("always", pflag.Lookup("always"))
|
||||||
|
cfg.BindPFlag("check-proxy-interval", pflag.Lookup("check-proxy-interval"))
|
||||||
|
cfg.BindPFlag("port", pflag.Lookup("port"))
|
||||||
|
cfg.BindPFlag("check-timeout", pflag.Lookup("check-timeout"))
|
||||||
|
cfg.BindPFlag("tcp-timeout", pflag.Lookup("tcp-timeout"))
|
||||||
|
cfg.BindPFlag("check-interval", pflag.Lookup("check-interval"))
|
||||||
|
cfg.BindPFlag("pool-size", pflag.Lookup("pool-size"))
|
||||||
|
cfg.BindPFlag("parent", pflag.Lookup("parent"))
|
||||||
|
cfg.BindPFlag("ip", pflag.Lookup("ip"))
|
||||||
|
cfg.BindPFlag("cert", pflag.Lookup("cert"))
|
||||||
|
cfg.BindPFlag("key", pflag.Lookup("key"))
|
||||||
|
cfg.BindPFlag("blocked", pflag.Lookup("blocked"))
|
||||||
|
cfg.BindPFlag("direct", pflag.Lookup("direct"))
|
||||||
|
cfg.BindPFlag("auth", pflag.Lookup("auth"))
|
||||||
|
cfg.BindPFlag("auth-file", pflag.Lookup("auth-file"))
|
||||||
|
|
||||||
|
//version
|
||||||
|
if *version {
|
||||||
|
fmt.Printf("proxy v%s\n", APP_VERSION)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
//keygen
|
||||||
|
if len(pflag.Args()) > 0 {
|
||||||
|
if pflag.Arg(0) == "keygen" {
|
||||||
|
keygen()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
poster()
|
||||||
|
|
||||||
|
if *configFile != "" {
|
||||||
|
cfg.SetConfigFile(*configFile)
|
||||||
|
} else {
|
||||||
|
cfg.SetConfigName("proxy")
|
||||||
|
cfg.AddConfigPath("/etc/proxy/")
|
||||||
|
cfg.AddConfigPath("$HOME/.proxy")
|
||||||
|
cfg.AddConfigPath(".proxy")
|
||||||
|
cfg.AddConfigPath(".")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cfg.ReadInConfig()
|
||||||
|
file := cfg.ConfigFileUsed()
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "Not") {
|
||||||
|
log.Fatalf("parse config fail, ERR:%s", err)
|
||||||
|
} else if file != "" {
|
||||||
|
log.Printf("use config file : %s", file)
|
||||||
|
}
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func poster() {
|
||||||
|
fmt.Printf(`
|
||||||
|
######## ######## ####### ## ## ## ##
|
||||||
|
## ## ## ## ## ## ## ## ## ##
|
||||||
|
## ## ## ## ## ## ## ## ####
|
||||||
|
######## ######## ## ## ### ##
|
||||||
|
## ## ## ## ## ## ## ##
|
||||||
|
## ## ## ## ## ## ## ##
|
||||||
|
## ## ## ####### ## ## ##
|
||||||
|
|
||||||
|
v%s`+" by snail , blog : http://www.host900.com/\n\n", APP_VERSION)
|
||||||
|
}
|
||||||
456
direct
Normal file
456
direct
Normal 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
|
||||||
346
functions.go
Executable file
346
functions.go
Executable file
@ -0,0 +1,346 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"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) {
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
log.Printf("IoBind crashed , err : %s , \ntrace:%s", e, string(debug.Stack()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
errchn := make(chan error, 2)
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
errchn <- 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
errchn <- err
|
||||||
|
}()
|
||||||
|
fn(<-errchn)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
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 initOutPool(isTLS bool, certBytes, keyBytes []byte, address string, timeout int, InitialCap int, MaxCap int) {
|
||||||
|
outPool, 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 = getConn(isTLS, certBytes, keyBytes, address, timeout)
|
||||||
|
return
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("init conn pool fail ,%s", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("init conn pool success")
|
||||||
|
initPoolDeamon(isTLS, certBytes, keyBytes, address, timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func getConn(isTLS bool, certBytes, keyBytes []byte, address string, timeout int) (conn interface{}, err error) {
|
||||||
|
if isTLS {
|
||||||
|
var _conn tls.Conn
|
||||||
|
_conn, err = TlsConnectHost(address, timeout, certBytes, keyBytes)
|
||||||
|
if err == nil {
|
||||||
|
conn = net.Conn(&_conn)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
conn, err = ConnectHost(address, timeout)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func initPoolDeamon(isTLS bool, certBytes, keyBytes []byte, address string, timeout int) {
|
||||||
|
go func() {
|
||||||
|
dur := cfg.GetInt("check-proxy-interval")
|
||||||
|
if dur <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("pool deamon started")
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Second * time.Duration(dur))
|
||||||
|
conn, err := getConn(isTLS, certBytes, keyBytes, address, timeout)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("pool deamon err %s , release pool", err)
|
||||||
|
outPool.ReleaseAll()
|
||||||
|
} else {
|
||||||
|
conn.(net.Conn).SetDeadline(time.Now().Add(time.Millisecond))
|
||||||
|
conn.(net.Conn).Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
func getURL(header []byte, host string) (URL string, err error) {
|
||||||
|
if !strings.HasPrefix(host, "/") {
|
||||||
|
return host, nil
|
||||||
|
}
|
||||||
|
_host, err := getHeader("host", header)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
URL = fmt.Sprintf("http://%s%s", _host, host)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func getHeader(key string, headData []byte) (val string, err error) {
|
||||||
|
key = strings.ToUpper(key)
|
||||||
|
lines := strings.Split(string(headData), "\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 hostIsNoPort(host string) bool {
|
||||||
|
//host: [dd:dafds:fsd:dasd:2.2.23.3] or 2.2.23.3 or [dd:dafds:fsd:dasd:2.2.23.3]:2323 or 2.2.23.3:1234
|
||||||
|
if strings.HasPrefix(host, "[") {
|
||||||
|
return strings.HasSuffix(host, "]")
|
||||||
|
}
|
||||||
|
return strings.Index(host, ":") == -1
|
||||||
|
}
|
||||||
|
func fixHost(host string) string {
|
||||||
|
if !strings.HasPrefix(host, "[") && len(strings.Split(host, ":")) > 2 {
|
||||||
|
if strings.HasSuffix(host, ":80") {
|
||||||
|
return fmt.Sprintf("[%s]:80", host[:strings.LastIndex(host, ":")])
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(host, ":443") {
|
||||||
|
return fmt.Sprintf("[%s]:443", host[:strings.LastIndex(host, ":")])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
|
||||||
|
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
io-limiter.go
Normal file
97
io-limiter.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
3
keygen.sh
Executable file
3
keygen.sh
Executable 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
|
||||||
382
main.go
Normal file
382
main.go
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const APP_VERSION = "2.2"
|
||||||
|
|
||||||
|
var (
|
||||||
|
checker Checker
|
||||||
|
proxyIsTls bool
|
||||||
|
localIsTls bool
|
||||||
|
proxyAddr string
|
||||||
|
isTCP bool
|
||||||
|
connTimeout int
|
||||||
|
certBytes []byte
|
||||||
|
keyBytes []byte
|
||||||
|
err error
|
||||||
|
outPool ConnPool
|
||||||
|
basicAuth BasicAuth
|
||||||
|
httpAuthorization bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
err = initConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("err : %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
//catch panic error
|
||||||
|
defer func() {
|
||||||
|
e := recover()
|
||||||
|
if e != nil {
|
||||||
|
log.Printf("err : %s,\ntrace:%s", e, string(debug.Stack()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
//define command line args
|
||||||
|
proxyIsTls = cfg.GetBool("parent-tls")
|
||||||
|
localIsTls = cfg.GetBool("local-tls")
|
||||||
|
proxyAddr = cfg.GetString("parent")
|
||||||
|
bindIP := cfg.GetString("ip")
|
||||||
|
bindPort := cfg.GetInt("port")
|
||||||
|
timeout := cfg.GetInt("check-timeout")
|
||||||
|
connTimeout = cfg.GetInt("tcp-timeout")
|
||||||
|
interval := cfg.GetInt("check-interval")
|
||||||
|
certFile := cfg.GetString("cert")
|
||||||
|
keyFile := cfg.GetString("key")
|
||||||
|
blockedFile := cfg.GetString("blocked")
|
||||||
|
directFile := cfg.GetString("direct")
|
||||||
|
|
||||||
|
isTCP = cfg.GetBool("tcp")
|
||||||
|
poolInitSize := cfg.GetInt("pool-size")
|
||||||
|
|
||||||
|
//check args required
|
||||||
|
if proxyIsTls && proxyAddr == "" {
|
||||||
|
log.Fatalf("parent proxy address required")
|
||||||
|
}
|
||||||
|
|
||||||
|
//check tls cert&key file
|
||||||
|
if certFile == "" {
|
||||||
|
certFile = "proxy.crt"
|
||||||
|
}
|
||||||
|
if keyFile == "" {
|
||||||
|
keyFile = "proxy.key"
|
||||||
|
}
|
||||||
|
if proxyIsTls || localIsTls {
|
||||||
|
certBytes, err = ioutil.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("err : %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keyBytes, err = ioutil.ReadFile(keyFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("err : %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//init tls info string
|
||||||
|
var proxyIsTlsStr string
|
||||||
|
var localIsTlsStr string
|
||||||
|
protocolStr := "tcp"
|
||||||
|
if !isTCP {
|
||||||
|
protocolStr = "http(s)"
|
||||||
|
}
|
||||||
|
if proxyIsTls {
|
||||||
|
proxyIsTlsStr = "tls "
|
||||||
|
}
|
||||||
|
if localIsTls {
|
||||||
|
localIsTlsStr = "tls "
|
||||||
|
}
|
||||||
|
//init checker and pool if needed
|
||||||
|
if proxyAddr != "" {
|
||||||
|
if !isTCP && !cfg.GetBool("always") {
|
||||||
|
checker = NewChecker(timeout, int64(interval), blockedFile, directFile)
|
||||||
|
}
|
||||||
|
log.Printf("use %sparent %s proxy : %s", proxyIsTlsStr, protocolStr, proxyAddr)
|
||||||
|
initOutPool(proxyIsTls, certBytes, keyBytes, proxyAddr, connTimeout, poolInitSize, poolInitSize*2)
|
||||||
|
} else if isTCP {
|
||||||
|
log.Printf("tcp proxy need parent")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//init basic auth only in http mode
|
||||||
|
if !isTCP {
|
||||||
|
basicAuth = NewBasicAuth()
|
||||||
|
if cfg.GetString("auth-file") != "" {
|
||||||
|
httpAuthorization = true
|
||||||
|
n, err := basicAuth.AddFromFile(cfg.GetString("auth-file"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("auth-file:%s", err)
|
||||||
|
}
|
||||||
|
log.Printf("auth data added from file %d , total:%d", n, basicAuth.Total())
|
||||||
|
}
|
||||||
|
if len(cfg.GetStringSlice("auth")) > 0 {
|
||||||
|
httpAuthorization = true
|
||||||
|
n := basicAuth.Add(cfg.GetStringSlice("auth"))
|
||||||
|
log.Printf("auth data added %d, total:%d", n, basicAuth.Total())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//listen
|
||||||
|
sc := NewServerChannel(bindIP, bindPort)
|
||||||
|
var err error
|
||||||
|
if localIsTls {
|
||||||
|
err = sc.ListenTls(certBytes, keyBytes, connHandler)
|
||||||
|
} else {
|
||||||
|
err = sc.ListenTCP(connHandler)
|
||||||
|
}
|
||||||
|
//listen fail
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("ERR:%s", err)
|
||||||
|
} else {
|
||||||
|
log.Printf("%s %sproxy on %s", protocolStr, localIsTlsStr, (*sc.Listener).Addr())
|
||||||
|
}
|
||||||
|
//block main()
|
||||||
|
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 {
|
||||||
|
if outPool != nil {
|
||||||
|
fmt.Println("\nReceived an interrupt, stopping services...")
|
||||||
|
outPool.ReleaseAll()
|
||||||
|
//time.Sleep(time.Second * 10)
|
||||||
|
// fmt.Println("done")
|
||||||
|
}
|
||||||
|
cleanupDone <- true
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
<-cleanupDone
|
||||||
|
}
|
||||||
|
func connHandler(inConn net.Conn) {
|
||||||
|
defer func() {
|
||||||
|
err := recover()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("connHandler crashed,err:%s\nstack:%s", err, string(debug.Stack()))
|
||||||
|
closeConn(&inConn)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if isTCP {
|
||||||
|
tcpHandler(&inConn)
|
||||||
|
} else {
|
||||||
|
httpHandler(&inConn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcpHandler(inConn *net.Conn) {
|
||||||
|
var outConn net.Conn
|
||||||
|
var _outConn interface{}
|
||||||
|
_outConn, err = outPool.Get()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("connect to %s , err:%s", proxyAddr, err)
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
outConn = _outConn.(net.Conn)
|
||||||
|
inAddr := (*inConn).RemoteAddr().String()
|
||||||
|
outAddr := outConn.RemoteAddr().String()
|
||||||
|
IoBind((*inConn), outConn, func(err error) {
|
||||||
|
log.Printf("conn %s - %s released", inAddr, outAddr)
|
||||||
|
closeConn(inConn)
|
||||||
|
closeConn(&outConn)
|
||||||
|
}, func(n int, d bool) {}, 0)
|
||||||
|
log.Printf("conn %s - %s connected", inAddr, outAddr)
|
||||||
|
}
|
||||||
|
func httpHandler(inConn *net.Conn) {
|
||||||
|
var b [4096]byte
|
||||||
|
var n int
|
||||||
|
n, err = (*inConn).Read(b[:])
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
log.Printf("read err:%s", err)
|
||||||
|
}
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var method, host, address string
|
||||||
|
index := bytes.IndexByte(b[:], '\n')
|
||||||
|
if index == -1 {
|
||||||
|
log.Printf("data err:%s", string(b[:n])[:50])
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Sscanf(string(b[:index]), "%s%s", &method, &host)
|
||||||
|
if method == "" || host == "" {
|
||||||
|
log.Printf("data err:%s", string(b[:n])[:50])
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isHTTPS := method == "CONNECT"
|
||||||
|
|
||||||
|
//http basic auth,only http
|
||||||
|
if !isHTTPS {
|
||||||
|
if httpAuthorization {
|
||||||
|
//log.Printf("request :%s", string(b[:n]))
|
||||||
|
authorization, err := getHeader("Authorization", b[:n])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprint(*inConn, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"\"\r\n\r\nUnauthorized")
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//log.Printf("Authorization:%s", authorization)
|
||||||
|
basic := strings.Fields(authorization)
|
||||||
|
if len(basic) != 2 {
|
||||||
|
log.Printf("authorization data error,ERR:%s", authorization)
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err := base64.StdEncoding.DecodeString(basic[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("authorization data parse error,ERR:%s", err)
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
authOk := basicAuth.Check(string(user))
|
||||||
|
//log.Printf("auth %s,%v", string(user), authOk)
|
||||||
|
if !authOk {
|
||||||
|
fmt.Fprint(*inConn, "HTTP/1.1 401 Unauthorized\r\n\r\nUnauthorized")
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes []byte
|
||||||
|
if isHTTPS { //https访问
|
||||||
|
// [dd:dafds:fsd:dasd:2.2.23.3] or 2.2.23.3 or [dd:dafds:fsd:dasd:2.2.23.3]:2323 or 2.2.23.3:1234
|
||||||
|
address = fixHost(host)
|
||||||
|
if hostIsNoPort(host) { //host不带端口, 默认443
|
||||||
|
address = address + ":443"
|
||||||
|
}
|
||||||
|
} else { //http访问
|
||||||
|
hostPortURL, err := url.Parse(host)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("url.Parse %s ERR:%s", host, err)
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_host := fixHost(hostPortURL.Host)
|
||||||
|
address = _host
|
||||||
|
if hostIsNoPort(_host) { //host不带端口, 默认80
|
||||||
|
address = _host + ":80"
|
||||||
|
}
|
||||||
|
if _host != hostPortURL.Host {
|
||||||
|
bytes = []byte(strings.Replace(string(b[:n]), hostPortURL.Host, _host, 1))
|
||||||
|
host = strings.Replace(host, hostPortURL.Host, _host, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//get url , reslut host is the full url
|
||||||
|
host, err = getURL(b[:n], host)
|
||||||
|
// log.Printf("body:%s", string(b[:n]))
|
||||||
|
// log.Printf("%s:%s", method, host)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("header data err:%s", err)
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
useProxy := false
|
||||||
|
if proxyAddr != "" {
|
||||||
|
if cfg.GetBool("always") {
|
||||||
|
useProxy = true
|
||||||
|
} else {
|
||||||
|
if isHTTPS {
|
||||||
|
checker.Add(address, true, method, "", nil)
|
||||||
|
} else {
|
||||||
|
if bytes != nil {
|
||||||
|
checker.Add(address, false, method, host, bytes)
|
||||||
|
} else {
|
||||||
|
checker.Add(address, false, method, host, b[:n])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
useProxy, _, _ = checker.IsBlocked(address)
|
||||||
|
}
|
||||||
|
// var failN, successN uint
|
||||||
|
// useProxy, failN, successN = checker.IsBlocked(address)
|
||||||
|
//log.Printf("use proxy ? %s : %v ,fail:%d, success:%d", address, useProxy, failN, successN)
|
||||||
|
//log.Printf("use proxy ? %s : %v", address, useProxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
var outConn net.Conn
|
||||||
|
var _outConn interface{}
|
||||||
|
if useProxy {
|
||||||
|
_outConn, err = outPool.Get()
|
||||||
|
if err == nil {
|
||||||
|
outConn = _outConn.(net.Conn)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outConn, err = ConnectHost(address, connTimeout)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("connect to %s , err:%s", address, err)
|
||||||
|
closeConn(inConn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
inAddr := (*inConn).RemoteAddr().String()
|
||||||
|
outAddr := outConn.RemoteAddr().String()
|
||||||
|
|
||||||
|
if isHTTPS {
|
||||||
|
if useProxy {
|
||||||
|
outConn.Write(b[:n])
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(*inConn, "HTTP/1.1 200 Connection established\r\n\r\n")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if bytes != nil {
|
||||||
|
outConn.Write(bytes)
|
||||||
|
} else {
|
||||||
|
outConn.Write(b[:n])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IoBind(*inConn, outConn, func(err error) {
|
||||||
|
log.Printf("conn %s - %s [%s] released", inAddr, outAddr, address)
|
||||||
|
closeConn(inConn)
|
||||||
|
closeConn(&outConn)
|
||||||
|
}, func(n int, d bool) {}, 0)
|
||||||
|
log.Printf("conn %s - %s [%s] connected", inAddr, outAddr, address)
|
||||||
|
}
|
||||||
|
func closeConn(conn *net.Conn) {
|
||||||
|
if *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
|
||||||
|
}
|
||||||
315
map.go
Normal file
315
map.go
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
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
pool.go
Executable file
145
pool.go
Executable file
@ -0,0 +1,145 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
72
proxy.toml
Normal file
72
proxy.toml
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
#####################################
|
||||||
|
##############parent#################
|
||||||
|
#####################################
|
||||||
|
#parent proxy address,such as: 223.78.2.33:8090
|
||||||
|
parent=""
|
||||||
|
|
||||||
|
#parent proxy is tls
|
||||||
|
parent-tls=false
|
||||||
|
|
||||||
|
#tcp timeout milliseconds when connect to real server or parent proxy
|
||||||
|
tcp-timeout=2000
|
||||||
|
|
||||||
|
#conn pool size , which connect to parent proxy
|
||||||
|
pool-size=50
|
||||||
|
|
||||||
|
#always use parent proxy
|
||||||
|
always=false
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
##############local##################
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
#local ip to bind
|
||||||
|
ip="0.0.0.0"
|
||||||
|
|
||||||
|
#local port to listen
|
||||||
|
port=33080
|
||||||
|
|
||||||
|
#local proxy is tls
|
||||||
|
local-tls=false
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
################tls##################
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
#cert file for tls
|
||||||
|
cert="proxy.crt"
|
||||||
|
|
||||||
|
#key file for tls
|
||||||
|
key="proxy.key"
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
################protocol#############
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
#proxy on tcp
|
||||||
|
tcp=false
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
################check################
|
||||||
|
#####################################
|
||||||
|
|
||||||
|
#chekc domain blocked , http request timeout milliseconds when connect to host
|
||||||
|
check-timeout=3000
|
||||||
|
|
||||||
|
#check domain if blocked every interval seconds
|
||||||
|
check-interval=10
|
||||||
|
|
||||||
|
#check if proxy is okay every interval seconds
|
||||||
|
#this is very helpful to proxy fix pool status , zero means:no check
|
||||||
|
check-proxy-interval=3
|
||||||
|
|
||||||
|
#blocked domain file , one domain each line
|
||||||
|
#google.com means (*.)*.google.com
|
||||||
|
blocked="blocked"
|
||||||
|
|
||||||
|
#direct domain file , one domain each line
|
||||||
|
#qq.com means (*.)*.qq.com
|
||||||
|
direct="direct"
|
||||||
|
|
||||||
|
|
||||||
36
proxyd
Executable file
36
proxyd
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
PID="/var/run/proxy.pid"
|
||||||
|
NAME="proxy"
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
if [ -e ${PID} ]; then
|
||||||
|
echo ${NAME} is running, pid=`cat ${PID}`, please stop first
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
monexec run -w /etc/proxy -- proxy >/dev/null 2>&1 &
|
||||||
|
echo $!>${PID}
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
if [ -e ${PID} ]; then
|
||||||
|
kill `cat ${PID}`
|
||||||
|
rm ${PID}
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
$0 stop
|
||||||
|
$0 start
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
if [ -e ${PID} ]; then
|
||||||
|
echo ${NAME} is running, pid=`cat ${PID}`
|
||||||
|
else
|
||||||
|
echo ${NAME} is NOT running
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {start|stop|status|restart}"
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
57
release.sh
Executable file
57
release.sh
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
VER="2.2"
|
||||||
|
RELEASE="release-${VER}"
|
||||||
|
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 proxy.toml proxyd direct blocked
|
||||||
|
GOOS=linux GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-linux-amd64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=linux GOARCH=arm GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=linux GOARCH=arm64 GOARM=7 go build && tar zcfv "${RELEASE}/proxy-linux-arm64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=linux GOARCH=mips go build && tar zcfv "${RELEASE}/proxy-linux-mips.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=linux GOARCH=mips64 go build && tar zcfv "${RELEASE}/proxy-linux-mips64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=linux GOARCH=mips64le go build && tar zcfv "${RELEASE}/proxy-linux-mips64le.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=linux GOARCH=mipsle go build && tar zcfv "${RELEASE}/proxy-linux-mipsle.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=linux GOARCH=ppc64 go build && tar zcfv "${RELEASE}/proxy-linux-ppc64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=linux GOARCH=ppc64le go build && tar zcfv "${RELEASE}/proxy-linux-ppc64le.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=linux GOARCH=s390x go build && tar zcfv "${RELEASE}/proxy-linux-s390x.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
#android
|
||||||
|
GOOS=android GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-android-386.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=android GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-android-amd64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=android GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-android-arm.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=android GOARCH=arm64 go build && tar zcfv "${RELEASE}/proxy-android-arm64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
#darwin
|
||||||
|
GOOS=darwin GOARCH=386 go build go build && tar zcfv "${RELEASE}/proxy-darwin-386.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=darwin GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-darwin-amd64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=darwin GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-darwin-arm.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=darwin GOARCH=arm64 go build && tar zcfv "${RELEASE}/proxy-darwin-arm64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
#dragonfly
|
||||||
|
GOOS=dragonfly GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-dragonfly-amd64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
#freebsd
|
||||||
|
GOOS=freebsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-freebsd-386.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=freebsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-freebsd-amd64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=freebsd GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-freebsd-arm.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
#nacl
|
||||||
|
GOOS=nacl GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-nacl-386.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=nacl GOARCH=amd64p32 go build && tar zcfv "${RELEASE}/proxy-nacl-amd64p32.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=nacl GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-nacl-arm.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
#netbsd
|
||||||
|
GOOS=netbsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-netbsd-386.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=netbsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-netbsd-amd64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=netbsd GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-netbsd-arm.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
#openbsd
|
||||||
|
GOOS=openbsd GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-openbsd-386.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=openbsd GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-openbsd-amd64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=openbsd GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-openbsd-arm.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
#plan9
|
||||||
|
GOOS=plan9 GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-plan9-386.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=plan9 GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-plan9-amd64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
GOOS=plan9 GOARCH=arm go build && tar zcfv "${RELEASE}/proxy-plan9-arm.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
#solaris
|
||||||
|
GOOS=solaris GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-solaris-amd64.tar.gz" proxy proxy.toml proxyd direct blocked
|
||||||
|
#windows
|
||||||
|
GOOS=windows GOARCH=386 go build && tar zcfv "${RELEASE}/proxy-windows-386.tar.gz" proxy.exe proxy.toml direct blocked
|
||||||
|
GOOS=windows GOARCH=amd64 go build && tar zcfv "${RELEASE}/proxy-windows-amd64.tar.gz" proxy.exe proxy.toml direct blocked
|
||||||
|
|
||||||
|
rm proxy proxy.exe
|
||||||
125
serve-channel.go
Normal file
125
serve-channel.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"runtime/debug"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 (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) {
|
||||||
|
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
|
||||||
|
}
|
||||||
219
structs.go
Normal file
219
structs.go
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"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) {
|
||||||
|
return true, 0, 0
|
||||||
|
}
|
||||||
|
if c.domainIsInMap(address, false) {
|
||||||
|
return false, 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_item, ok := c.data.Get(address)
|
||||||
|
if !ok {
|
||||||
|
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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user