diff --git a/.travis.yml b/.travis.yml index bd371ec9..69c4d72b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: go go: - - 1.3 + - 1.4.2 env: - TRAVIS="yes" install: diff --git a/CHANGELOG b/CHANGELOG index 2f844357..4799aa33 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +0.9.5 (2015-05-12) + * Support new encryption method "chacha20" and "salsa20" + * Avoid biased parent proxy selection for hash load balacing + * Fix AirDrop on OS X when using PAC + * Fix failed start with corrupted stat file + * Support changing the estimate timeout target + 0.9.4 (2014-10-08) * Bug fix (#179): close stat file after load diff --git a/README.md b/README.md index 1265ca8f..2037e002 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ COW 是一个简化穿墙的 HTTP 代理服务器。它能自动检测被墙网 [English README](README-en.md). -当前版本:0.9.4 [CHANGELOG](CHANGELOG) +当前版本:0.9.5 [CHANGELOG](CHANGELOG) [![Build Status](https://travis-ci.org/cyfdecyf/cow.png?branch=master)](https://travis-ci.org/cyfdecyf/cow) **欢迎在 develop branch 进行开发并发送 pull request :)** @@ -37,6 +37,7 @@ COW 的设计目标是自动化,理想情况下用户无需关心哪些网站 #开头的行是注释,会被忽略 # 本地 HTTP 代理地址 # 配置 HTTP 和 HTTPS 代理时请填入该地址 + # 若配置代理时有对所有协议使用该代理的选项,且你不清楚此选项的含义,请勾选 # 或者在自动代理配置中填入 http://127.0.0.1:7777/pac listen = http://127.0.0.1:7777 @@ -120,14 +121,14 @@ COW 默认配置下检测到被墙后,过两分钟再次尝试直连也是为 - 不提供 cache - 不支持 HTTP pipeline(Chrome, Firefox 默认都没开启 pipeline,支持这个功能容易增加问题而好处并不明显) -# 致谢 +# 致谢 (Acknowledgements) 贡献代码: - @tevino: http parent proxy basic authentication - @xupefei: 提供 cow-hide.exe 以在 windows 上在后台执行 cow.exe - @sunteya: 改进启动和安装脚本 -- @fzerorubigd: identify blocked site by HTTP error code +- @fzerorubigd: identify blocked site by HTTP error code and various bug fixes Bug reporter: diff --git a/config.go b/config.go index 8784fe01..624cdb37 100644 --- a/config.go +++ b/config.go @@ -4,7 +4,6 @@ import ( "errors" "flag" "fmt" - "github.com/cyfdecyf/bufio" "net" "os" "path" @@ -12,11 +11,14 @@ import ( "strconv" "strings" "time" + + "github.com/cyfdecyf/bufio" ) const ( - version = "0.9.4" - defaultListenAddr = "127.0.0.1:7777" + version = "0.9.5" + defaultListenAddr = "127.0.0.1:7777" + defaultEstimateTarget = "example.com" ) type LoadBalanceMode byte @@ -64,7 +66,8 @@ type Config struct { // not configurable in config file PrintVer bool - EstimateTimeout bool // if run estimateTimeout() + EstimateTimeout bool // Whether to run estimateTimeout(). + EstimateTarget string // Timeout estimate target site. // not config option saveReqLine bool // for http and cow parent, should save request line from client @@ -103,6 +106,8 @@ func init() { for _, port := range defaultTunnelAllowedPort { config.TunnelAllowedPort[port] = true } + + config.EstimateTarget = defaultEstimateTarget } // Whether command line options specifies listen addr @@ -560,6 +565,10 @@ func (p configParser) ParseDetectSSLErr(val string) { config.DetectSSLErr = parseBool(val, "detectSSLErr") } +func (p configParser) ParseEstimateTarget(val string) { + config.EstimateTarget = val +} + // overrideConfig should contain options from command line to override options // in config file. func parseConfig(rc string, override *Config) { diff --git a/doc/sample-config/rc b/doc/sample-config/rc index 11c34805..726d5744 100644 --- a/doc/sample-config/rc +++ b/doc/sample-config/rc @@ -10,6 +10,8 @@ # listen = http://127.0.0.1:7777 # # 上面的例子中,cow 生成的 PAC url 为 http://127.0.0.1:7777/pac +# 配置浏览器或系统 HTTP 和 HTTPS 代理时请填入该地址 +# 若配置代理时有对所有协议使用该代理的选项,且你不清楚此选项的含义,请勾选 # # cow (需两个 cow 服务器配合使用): # listen = cow://encrypt_method:password@1.2.3.4:5678 @@ -67,7 +69,8 @@ listen = http://127.0.0.1:7777 # # authinfo 中指定加密方法和密码,所有支持的加密方法如下: # aes-128-cfb, aes-192-cfb, aes-256-cfb, -# bf-cfb, cast5-cfb, des-cfb, rc4-md5, rc4, table +# bf-cfb, cast5-cfb, des-cfb, rc4-md5, +# chacha20, salsa20, rc4, table # 推荐使用 aes-128-cfb # # cow: @@ -124,6 +127,9 @@ listen = http://127.0.0.1:7777 # 最多允许使用多少个 CPU 核 #core = 2 +# 检测超时时间使用的网站,最好使用能快速访问的站点 +#estimateTarget = example.com + # 允许建立隧道连接的端口,多个端口用逗号分隔,可重复多次 # 默认总是允许下列服务的端口: ssh, http, https, rsync, imap, pop, jabber, cvs, git, svn # 如需允许其他端口,请用该选项添加 diff --git a/doc/sample-config/rc-en b/doc/sample-config/rc-en index 5e0c3523..32c62a82 100644 --- a/doc/sample-config/rc-en +++ b/doc/sample-config/rc-en @@ -79,7 +79,8 @@ listen = http://127.0.0.1:7777 # Here are the supported encryption methods: # # aes-128-cfb, aes-192-cfb, aes-256-cfb, -# bf-cfb, cast5-cfb, des-cfb, rc4-md5, rc4, table +# bf-cfb, cast5-cfb, des-cfb, rc4-md5, +# chacha20, salsa20, rc4, table # # aes-128-cfb is recommended. # @@ -145,6 +146,9 @@ listen = http://127.0.0.1:7777 # Maximum CPU core to use. #core = 2 +# cow uses this site to estimate timeout, better to use a fast website. +#estimateTarget = example.com + # Ports allowed to create tunnel (HTTP CONNECT method), comma separated list # or repeat to append more ports. # Ports for the following service are allowed by default: diff --git a/estimate_timeout.go b/estimate_timeout.go index 6a975c6c..20b664c5 100644 --- a/estimate_timeout.go +++ b/estimate_timeout.go @@ -17,11 +17,8 @@ const maxTimeout = 15 * time.Second var dialTimeout = defaultDialTimeout var readTimeout = defaultReadTimeout -// use a fast to fetch web site -const estimateSite = "www.baidu.com" - var estimateReq = []byte("GET / HTTP/1.1\r\n" + - "Host: " + estimateSite + "\r\n" + + "Host: " + config.EstimateTarget + "\r\n" + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:11.0) Gecko/20100101 Firefox/11.0\r\n" + "Accept: */*\r\n" + "Accept-Language: en-us,en;q=0.5\r\n" + @@ -32,16 +29,16 @@ var estimateReq = []byte("GET / HTTP/1.1\r\n" + // how much time is spent on connect and fetch. This avoids incorrectly // considering non-blocked sites as blocked when network connection is bad. func estimateTimeout() { - // debug.Println("estimating timeout") + //debug.Println("estimating timeout") buf := connectBuf.Get() defer connectBuf.Put(buf) var est time.Duration start := time.Now() - c, err := net.Dial("tcp", estimateSite+":80") + c, err := net.Dial("tcp", config.EstimateTarget+":80") if err != nil { errl.Printf("estimateTimeout: can't connect to %s: %v, network has problem?\n", - estimateSite, err) + config.EstimateTarget, err) goto onErr } defer c.Close() @@ -71,7 +68,7 @@ func estimateTimeout() { } if err != io.EOF { errl.Printf("estimateTimeout: error getting %s: %v, network has problem?\n", - estimateSite, err) + config.EstimateTarget, err) goto onErr } est = time.Now().Sub(start) * 10 @@ -97,7 +94,7 @@ func runEstimateTimeout() { dialTimeout = config.DialTimeout for { estimateTimeout() - time.Sleep(30 * time.Second) + time.Sleep(time.Minute) } } diff --git a/http.go b/http.go index 4768dfb6..4bd7b109 100644 --- a/http.go +++ b/http.go @@ -710,7 +710,7 @@ func parseResponse(sv *serverConn, r *Request, rp *Response) (err error) { //Check for http error code from config file if config.HttpErrorCode > 0 && rp.Status == config.HttpErrorCode { - errl.Println("Requested http code is raised") + debug.Println("Requested http code is raised") return CustomHttpErr } diff --git a/install-cow.sh b/install-cow.sh index 84a4ddeb..d4f5d3f2 100755 --- a/install-cow.sh +++ b/install-cow.sh @@ -1,6 +1,6 @@ #!/bin/bash -version=0.9.4 +version=0.9.5 arch=`uname -m` case $arch in @@ -83,7 +83,7 @@ fi bin=cow-$os$arch-$version tmpdir=`mktemp -d /tmp/cow.XXXXXX` tmpbin=$tmpdir/cow -binary_url="http://dl.chenyufei.info/cow/$bin.gz" +binary_url="http://dl.chenyufei.info/cow/$version/$bin.gz" echo "Downloading cow binary $binary_url to $tmpbin.gz" curl -L "$binary_url" -o $tmpbin.gz || \ exit_on_fail "Downloading cow binary failed" diff --git a/main.go b/main.go index d1684699..f4f36eda 100644 --- a/main.go +++ b/main.go @@ -16,8 +16,7 @@ func sigHandler() { // TODO On Windows, these signals will not be triggered on closing cmd // window. How to detect this? sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, - syscall.SIGHUP) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) for sig := range sigChan { // May handle other signals in the future. diff --git a/pac.go b/pac.go index a1d913a6..7e2e7f97 100644 --- a/pac.go +++ b/pac.go @@ -116,6 +116,9 @@ function host2Domain(host) { function FindProxyForURL(url, host) { if (url.substring(0,4) == "ftp:") return direct; + if (host.indexOf(".local", host.length - 6) !== -1) { + return direct; + } var domain = host2Domain(host); if (host.length == domain.length) { return directAcc[host] ? direct : httpProxy; diff --git a/pac.js b/pac.js index 6d49cc0c..fdd85b74 100644 --- a/pac.js +++ b/pac.js @@ -86,6 +86,9 @@ function host2Domain(host) { function FindProxyForURL(url, host) { if (url.substring(0,4) == "ftp:") return direct; + if (host.indexOf(".local", host.length - 6) !== -1) { + return direct; + } var domain = host2Domain(host); if (host.length == domain.length) { return directAcc[host] ? direct : httpProxy; @@ -160,7 +163,11 @@ testData = [ { host: 'foo.baidu.com', mode: httpProxy}, { host: 'google.com', mode: httpProxy}, { host: 'www.google.com', mode: httpProxy}, - { host: 'www.google.com.hk', mode: httpProxy} + { host: 'www.google.com.hk', mode: httpProxy}, + + // host in local domain should return direct + { host: 'test.local', mode: direct}, + { host: '.local', mode: direct}, ]; for (i = 0; i < testData.length; i += 1) { diff --git a/parent_proxy.go b/parent_proxy.go index f78b0b23..04ad92a3 100644 --- a/parent_proxy.go +++ b/parent_proxy.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" ss "github.com/shadowsocks/shadowsocks-go/shadowsocks" + "hash/crc32" "io" "math/rand" "net" @@ -109,7 +110,7 @@ type hashParentPool struct { } func (pp *hashParentPool) connect(url *URL) (srvconn net.Conn, err error) { - start := int(stringHash(url.Host) % uint64(len(pp.parent))) + start := int(crc32.ChecksumIEEE([]byte(url.Host)) % uint32(len(pp.parent))) debug.Printf("hash host %s try %d parent first", url.Host, start) return connectInOrder(url, pp.parent, start) } diff --git a/script/build.sh b/script/build.sh index 799df97c..ca62cf63 100755 --- a/script/build.sh +++ b/script/build.sh @@ -48,7 +48,7 @@ build() { } build darwin amd64 mac64 -build darwin 386 mac32 +#build darwin 386 mac32 build linux amd64 linux64 build linux 386 linux32 build linux arm linux-armv5tel diff --git a/sitestat.go b/sitestat.go index e5c0ceb1..02097802 100644 --- a/sitestat.go +++ b/sitestat.go @@ -4,13 +4,14 @@ import ( "encoding/json" "errors" "fmt" - "github.com/cyfdecyf/bufio" "io/ioutil" "math/rand" "os" "strings" "sync" "time" + + "github.com/cyfdecyf/bufio" ) func init() { @@ -426,9 +427,18 @@ func (ss *SiteStat) GetDirectList() []string { var siteStat = newSiteStat() func initSiteStat() { - if err := siteStat.load(configPath.stat); err != nil { - os.Exit(1) + err := siteStat.load(configPath.stat) + if err != nil { + errl.Printf("loading stat file failed, reason : %s", err.Error()) + // Simply try to load the stat.back + err = siteStat.load(configPath.stat + ".bak") + // After all its not critical , simply re-create a stat object if anything is not ok + if err != nil { + errl.Printf("loading stat backup failed, creating new one , reason: %s", err.Error()) + siteStat = newSiteStat() + } } + // Dump site stat while running, so we don't always need to close cow to // get updated stat. go func() { diff --git a/util.go b/util.go index 1b900a32..26ec153f 100644 --- a/util.go +++ b/util.go @@ -453,15 +453,6 @@ func host2Domain(host string) (domain string) { return host[dot2ndLast+1:] } -// djb2 string hash function, from http://www.cse.yorku.ca/~oz/hash.html -func stringHash(s string) (hash uint64) { - hash = 5381 - for i := 0; i < len(s); i++ { - hash = ((hash << 5) + 1) + uint64(s[i]) - } - return -} - // IgnoreUTF8BOM consumes UTF-8 encoded BOM character if present in the file. func IgnoreUTF8BOM(f *os.File) error { bom := make([]byte, 3)