diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c4f3145..d8c19d7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.13 + - name: Set up Go uses: actions/setup-go@v1 with: go-version: 1.13 @@ -26,7 +26,7 @@ jobs: needs: [test] steps: - - name: Set up Go 1.13 + - name: Set up Go uses: actions/setup-go@v1 with: go-version: 1.13 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 93904e5..52368a5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,14 +9,20 @@ jobs: name: Release on GitHub runs-on: ubuntu-latest steps: + + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.13 + id: go + - name: Check out code uses: actions/checkout@v1 - name: Create release on GitHub - uses: docker://goreleaser/goreleaser:latest - env: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + uses: goreleaser/goreleaser-action@v1 with: + version: latest args: release - if: success() - \ No newline at end of file + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yml b/.goreleaser.yml index 059bd02..d2d3661 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -5,6 +5,9 @@ # git push origin v0.1.0 # goreleaser release --skip-publish --rm-dist +# #git tag -d v0.1.0 +# #git push origin --delete tag v0.1.0 + # snapshot: # goreleaser --snapshot --rm-dist diff --git a/README.md b/README.md index 23a0497..65ad2fd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # [glider](https://github.com/nadoo/glider) -[![Go Report Card](https://goreportcard.com/badge/github.com/nadoo/glider?style=flat-square)](https://goreportcard.com/report/github.com/nadoo/glider) -[![GitHub release](https://img.shields.io/github/v/release/nadoo/glider.svg?include_prereleases&style=flat-square)](https://github.com/nadoo/glider/releases) +[![Go Report Card](https://goreportcard.com/badge/github.com/nadoo/glider)](https://goreportcard.com/report/github.com/nadoo/glider) +[![GitHub release](https://img.shields.io/github/v/release/nadoo/glider.svg?include_prereleases)](https://github.com/nadoo/glider/releases) +[![Actions Status](https://github.com/nadoo/glider/workflows/Build/badge.svg)](https://github.com/nadoo/glider/actions) glider is a forward proxy with multiple protocols support, and also a dns forwarding server with ipset management features(like dnsmasq). @@ -43,6 +44,7 @@ Forward (local proxy client/upstream proxy server): DNS Forwarding Server (udp2tcp): +- DNS Over Proxy - Listen on UDP and forward dns requests to remote dns server in TCP via forwarders - Specify different upstream dns server based on destinations(in rule file) - Tunnel mode: forward to a fixed upstream dns server @@ -114,7 +116,7 @@ glider -config CONFIGPATH -listen :8080 -verbose ## Usage ```bash -glider 0.8.0 usage: +glider 0.9.0 usage: -checkinterval int proxy check interval(seconds) (default 30) -checktimeout int @@ -189,6 +191,7 @@ Available methods for ss: AES-128-CFB AES-128-CTR AES-192-CFB AES-192-CTR AES-256-CFB AES-256-CTR CHACHA20-IETF XCHACHA20 CHACHA20 RC4-MD5 Alias: chacha20-ietf-poly1305 = AEAD_CHACHA20_POLY1305, xchacha20-ietf-poly1305 = AEAD_XCHACHA20_POLY1305 + Plain: DUMMY SSR scheme: ssr://method:pass@host:port?protocol=xxx&protocol_param=yyy&obfs=zzz&obfs_param=xyz @@ -280,17 +283,14 @@ Examples: glider -config glider.conf -run glider with specified config file. - glider -config glider.conf -rulefile office.rule -rulefile home.rule - -run glider with specified global config file and rule config files. + glider -listen :8443 -verbose + -listen on :8443, serve as http/socks5 proxy on the same port, in verbose mode. - glider -listen :8443 - -listen on :8443, serve as http/socks5 proxy on the same port. - - glider -listen ss://AEAD_CHACHA20_POLY1305:pass@:8443 + glider -listen ss://AEAD_CHACHA20_POLY1305:pass@:8443 -verbose -listen on 0.0.0.0:8443 as a ss server. - glider -listen socks5://:1080 -verbose - -listen on :1080 as a socks5 proxy server, in verbose mode. + glider -listen socks5://user1:pass1@:1080 -verbose + -listen on :1080 as a socks5 proxy server, enable authentication. glider -listen tls://:443?cert=crtFilePath&key=keyFilePath,http:// -verbose -listen on :443 as a https(http over tls) proxy server. diff --git a/common/log/log.go b/common/log/log.go index 9bf4e1c..ff34eb9 100644 --- a/common/log/log.go +++ b/common/log/log.go @@ -1,6 +1,12 @@ package log -import stdlog "log" +import ( + stdlog "log" +) + +func init() { + stdlog.SetFlags(stdlog.LstdFlags | stdlog.Lshortfile) +} // Func defines a simple log function type Func func(f string, v ...interface{}) diff --git a/conf.go b/conf.go index 020fc2f..4736578 100644 --- a/conf.go +++ b/conf.go @@ -143,6 +143,7 @@ func usage() { fmt.Fprintf(os.Stderr, " AES-128-CFB AES-128-CTR AES-192-CFB AES-192-CTR AES-256-CFB AES-256-CTR CHACHA20-IETF XCHACHA20 CHACHA20 RC4-MD5\n") fmt.Fprintf(os.Stderr, " Alias:\n") fmt.Fprintf(os.Stderr, " chacha20-ietf-poly1305 = AEAD_CHACHA20_POLY1305, xchacha20-ietf-poly1305 = AEAD_XCHACHA20_POLY1305\n") + fmt.Fprintf(os.Stderr, " Plain: DUMMY\n") fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, "SSR scheme:\n") @@ -254,17 +255,14 @@ func usage() { fmt.Fprintf(os.Stderr, " "+app+" -config glider.conf\n") fmt.Fprintf(os.Stderr, " -run glider with specified config file.\n") fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintf(os.Stderr, " "+app+" -config glider.conf -rulefile office.rule -rulefile home.rule\n") - fmt.Fprintf(os.Stderr, " -run glider with specified global config file and rule config files.\n") + fmt.Fprintf(os.Stderr, " "+app+" -listen :8443 -verbose\n") + fmt.Fprintf(os.Stderr, " -listen on :8443, serve as http/socks5 proxy on the same port, in verbose mode.\n") fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintf(os.Stderr, " "+app+" -listen :8443\n") - fmt.Fprintf(os.Stderr, " -listen on :8443, serve as http/socks5 proxy on the same port.\n") - fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintf(os.Stderr, " "+app+" -listen ss://AEAD_CHACHA20_POLY1305:pass@:8443\n") + fmt.Fprintf(os.Stderr, " "+app+" -listen ss://AEAD_CHACHA20_POLY1305:pass@:8443 -verbose\n") fmt.Fprintf(os.Stderr, " -listen on 0.0.0.0:8443 as a ss server.\n") fmt.Fprintf(os.Stderr, "\n") - fmt.Fprintf(os.Stderr, " "+app+" -listen socks5://:1080 -verbose\n") - fmt.Fprintf(os.Stderr, " -listen on :1080 as a socks5 proxy server, in verbose mode.\n") + fmt.Fprintf(os.Stderr, " "+app+" -listen socks5://user1:pass1@:1080 -verbose\n") + fmt.Fprintf(os.Stderr, " -listen on :1080 as a socks5 proxy server, enable authentication.\n") fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, " "+app+" -listen tls://:443?cert=crtFilePath&key=keyFilePath,http:// -verbose\n") fmt.Fprintf(os.Stderr, " -listen on :443 as a https(http over tls) proxy server.\n") diff --git a/go.mod b/go.mod index e68a88b..03218ea 100644 --- a/go.mod +++ b/go.mod @@ -3,26 +3,20 @@ module github.com/nadoo/glider go 1.13 require ( - github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63 // indirect - github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect - github.com/dgryski/go-camellia v0.0.0-20140412174459-3be6b3054dd1 // indirect - github.com/dgryski/go-idea v0.0.0-20170306091226-d2fb45a411fb // indirect - github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152 // indirect - github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect github.com/klauspost/cpuid v1.2.1 // indirect - github.com/klauspost/reedsolomon v1.9.2 // indirect - github.com/nadoo/conflag v0.2.0 - github.com/nadoo/go-shadowsocks2 v0.1.0 + github.com/klauspost/reedsolomon v1.9.3 // indirect + github.com/nadoo/conflag v0.2.2 + github.com/nadoo/go-shadowsocks2 v0.1.2 + github.com/nadoo/shadowsocksR v0.1.0 github.com/pkg/errors v0.8.1 // indirect - github.com/sun8911879/shadowsocksR v0.0.0-20180529042039-da20fda4804f github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect github.com/tjfoc/gmsm v1.0.1 // indirect - github.com/xtaci/kcp-go v5.4.4+incompatible + github.com/xtaci/kcp-go v5.4.11+incompatible github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect - golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 - golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect - golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect + golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 + golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect + golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2 // indirect ) // Replace dependency modules with local developing copy diff --git a/go.sum b/go.sum index 0b80609..1af70dc 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63 h1:I6/SJSN9wJMJ+ZyQaCHUlzoTA4ypU5Bb44YWR1wTY/0= -github.com/Yawning/chacha20 v0.0.0-20170904085104-e3b1f968fc63/go.mod h1:nf+Komq6fVP4SwmKEaVGxHTyQGKREVlwjQKpvOV39yE= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/dgryski/go-camellia v0.0.0-20140412174459-3be6b3054dd1 h1:/5UddQ9I3CXetvBVN2ipRc209YUB0AMR8bufErftAxI= @@ -12,36 +10,40 @@ github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 h1:fBHFH+Y/GPGFGo7LIrErQc github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:ucvhdsUCE3TH0LoLRb6ShHiJl8e39dGlx6A4g/ujlow= github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/reedsolomon v1.9.2 h1:E9CMS2Pqbv+C7tsrYad4YC9MfhnMVWhMRsTi7U0UB18= -github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= -github.com/nadoo/conflag v0.2.0 h1:xao13tYqfD+5bjQ1A/jT2kBL8tUcVpFhq3seuN5kpeM= -github.com/nadoo/conflag v0.2.0/go.mod h1:Ayl83klaw7fagwYaI6luTmbOi4psAf7FqJNRRv5YMvU= -github.com/nadoo/go-shadowsocks2 v0.1.0 h1:NkdUrZrI8uYq8R0YDmHLttLqKt0Z9i7dUKtGvBqZQl8= -github.com/nadoo/go-shadowsocks2 v0.1.0/go.mod h1:J0B/QoRZtqUwE9BJqkP3F3M5+N8t+b5fXeNrkUarveM= +github.com/klauspost/reedsolomon v1.9.3 h1:N/VzgeMfHmLc+KHMD1UL/tNkfXAt8FnUqlgXGIduwAY= +github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= +github.com/nadoo/conflag v0.2.2 h1:xywuyaevdBnA3+4g9S11ng+Nby725WN1LXargWnAXpM= +github.com/nadoo/conflag v0.2.2/go.mod h1:dzFfDUpXdr2uS2oV+udpy5N2vfNOu/bFzjhX1WI52co= +github.com/nadoo/go-shadowsocks2 v0.1.2 h1:+tCSt65YAAMf24wj3tqv6a9oVBcqSGFYVsifBZwT9w8= +github.com/nadoo/go-shadowsocks2 v0.1.2/go.mod h1:/E2kSkS0mqF/e79wcAA0PezoWXk4CY9HldJlzwWtbwU= +github.com/nadoo/shadowsocksR v0.1.0 h1:sYPxZi0l8F1nxDDcckzb0DHXxhe0LNW5iSeohqPw6Fg= +github.com/nadoo/shadowsocksR v0.1.0/go.mod h1:nqcLRU7laARXdLLBrHP8odruT/6GIureicuRTs4R+RY= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/sun8911879/shadowsocksR v0.0.0-20180529042039-da20fda4804f h1:66c28UIO0JbJi5he9n+QN9Ya0OAW0eKb8Eu02kMSXHI= -github.com/sun8911879/shadowsocksR v0.0.0-20180529042039-da20fda4804f/go.mod h1:uEm3LP/z9l1+zfo2FTzUvWnxua7rbrUoGAMiLaHdujk= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM= github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU= github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= -github.com/xtaci/kcp-go v5.4.4+incompatible h1:QIJ0a0Q0N1G20yLHL2+fpdzyy2v/Cb3PI+xiwx/KK9c= -github.com/xtaci/kcp-go v5.4.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= +github.com/xtaci/kcp-go v5.4.11+incompatible h1:tJbtarpmOoOD74cZ41uvvF5Hyt1nvctHQCOxZ6ot5xw= +github.com/xtaci/kcp-go v5.4.11+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM= github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= +golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191010185427-af544f31c8ac h1:/b4NMZurYfBIQyRMqaPGMDeUrSW6gU7/7Hv6owY1Vjk= +golang.org/x/crypto v0.0.0-20191010185427-af544f31c8ac/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA= +golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2 h1:nq114VpM8lsSlP+lyUbANecYHYiFcSNFtqcBlxRV+gA= +golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/main.go b/main.go index e500b6a..e3f1aee 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" stdlog "log" "os" "os/signal" @@ -29,7 +30,7 @@ import ( _ "github.com/nadoo/glider/proxy/ws" ) -var version = "0.8.2" +var version = "0.9.2" func main() { // read configs @@ -38,7 +39,7 @@ func main() { // setup a log func log.F = func(f string, v ...interface{}) { if conf.Verbose { - stdlog.Printf(f, v...) + stdlog.Output(2, fmt.Sprintf(f, v...)) } } diff --git a/proxy/http/client.go b/proxy/http/client.go new file mode 100644 index 0000000..db7ba95 --- /dev/null +++ b/proxy/http/client.go @@ -0,0 +1,81 @@ +package http + +import ( + "bytes" + "encoding/base64" + "errors" + "net" + "net/textproto" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +// NewHTTPDialer returns a http proxy dialer. +func NewHTTPDialer(s string, d proxy.Dialer) (proxy.Dialer, error) { + return NewHTTP(s, d, nil) +} + +// Addr returns forwarder's address. +func (s *HTTP) Addr() string { + if s.addr == "" { + return s.dialer.Addr() + } + return s.addr +} + +// Dial connects to the address addr on the network net via the proxy. +func (s *HTTP) Dial(network, addr string) (net.Conn, error) { + rc, err := s.dialer.Dial(network, s.addr) + if err != nil { + log.F("[http] dial to %s error: %s", s.addr, err) + return nil, err + } + + var buf bytes.Buffer + buf.WriteString("CONNECT " + addr + " HTTP/1.1\r\n") + buf.WriteString("Host: " + addr + "\r\n") + buf.WriteString("Proxy-Connection: Keep-Alive\r\n") + + if s.user != "" && s.password != "" { + auth := s.user + ":" + s.password + buf.Write([]byte("Proxy-Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + "\r\n")) + } + + // header ended + buf.WriteString("\r\n") + _, err = rc.Write(buf.Bytes()) + if err != nil { + return nil, err + } + + c := conn.NewConn(rc) + tpr := textproto.NewReader(c.Reader()) + line, err := tpr.ReadLine() + if err != nil { + return c, err + } + + _, code, _, ok := parseStartLine(line) + if ok && code == "200" { + tpr.ReadMIMEHeader() + return c, err + } + + switch code { + case "403": + log.F("[http] 'CONNECT' to ports other than 443 are not allowed by proxy %s", s.addr) + case "405": + log.F("[http] 'CONNECT' method not allowed by proxy %s", s.addr) + case "407": + log.F("[http] authencation needed by proxy %s", s.addr) + } + + return nil, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code) +} + +// DialUDP connects to the given address via the proxy. +func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { + return nil, nil, errors.New("http client does not support udp") +} diff --git a/proxy/http/http.go b/proxy/http/http.go index a7d89b6..e1a6bd3 100644 --- a/proxy/http/http.go +++ b/proxy/http/http.go @@ -1,34 +1,28 @@ -// http proxy +// https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages // NOTE: never keep-alive so the implementation can be much easier. +// Package http implements a http proxy. package http import ( - "bufio" "bytes" "encoding/base64" - "errors" - "fmt" - "io" - "net" "net/textproto" "net/url" "strings" - "time" - "github.com/nadoo/glider/common/conn" "github.com/nadoo/glider/common/log" "github.com/nadoo/glider/proxy" ) // HTTP struct. type HTTP struct { - dialer proxy.Dialer - proxy proxy.Proxy - addr string - user string - password string - pretendAsWebServer bool + dialer proxy.Dialer + proxy proxy.Proxy + addr string + user string + password string + pretend bool } func init() { @@ -49,242 +43,23 @@ func NewHTTP(s string, d proxy.Dialer, p proxy.Proxy) (*HTTP, error) { pass, _ := u.User.Password() h := &HTTP{ - dialer: d, - proxy: p, - addr: addr, - user: user, - password: pass, - pretendAsWebServer: false, + dialer: d, + proxy: p, + addr: addr, + user: user, + password: pass, + pretend: false, } - pretend := u.Query().Get("pretend") - if pretend == "true" { - h.pretendAsWebServer = true + if u.Query().Get("pretend") == "true" { + h.pretend = true } return h, nil } -// NewHTTPDialer returns a http proxy dialer. -func NewHTTPDialer(s string, d proxy.Dialer) (proxy.Dialer, error) { - return NewHTTP(s, d, nil) -} - -// NewHTTPServer returns a http proxy server. -func NewHTTPServer(s string, p proxy.Proxy) (proxy.Server, error) { - return NewHTTP(s, nil, p) -} - -// ListenAndServe listens on server's addr and serves connections. -func (s *HTTP) ListenAndServe() { - l, err := net.Listen("tcp", s.addr) - if err != nil { - log.F("[http] failed to listen on %s: %v", s.addr, err) - return - } - defer l.Close() - - log.F("[http] listening TCP on %s", s.addr) - - for { - c, err := l.Accept() - if err != nil { - log.F("[http] failed to accept: %v", err) - continue - } - - go s.Serve(c) - } -} - -// Serve serves a connection. -func (s *HTTP) Serve(c net.Conn) { - defer c.Close() - - if c, ok := c.(*net.TCPConn); ok { - c.SetKeepAlive(true) - } - - reqR := bufio.NewReader(c) - reqTP := textproto.NewReader(reqR) - method, requestURI, proto, ok := parseFirstLine(reqTP) - if !ok { - return - } - - if s.pretendAsWebServer { - fmt.Fprintf(c, "%s 404 Not Found\r\nServer: nginx\r\n\r\n404 Not Found\r\n", proto) - log.F("[http pretender] being accessed as web server from %s", c.RemoteAddr().String()) - return - } - - if method == "CONNECT" { - s.servHTTPS(method, requestURI, proto, c) - return - } - - reqHeader, err := reqTP.ReadMIMEHeader() - if err != nil { - log.F("[http] read header error:%s", err) - return - } - cleanHeaders(reqHeader) - - // tell the remote server not to keep alive - reqHeader.Set("Connection", "close") - - u, err := url.ParseRequestURI(requestURI) - if err != nil { - log.F("[http] parse request url error: %s", err) - return - } - - var tgt = u.Host - if !strings.Contains(u.Host, ":") { - tgt += ":80" - } - - rc, p, err := s.proxy.Dial("tcp", tgt) - if err != nil { - fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto) - log.F("[http] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), tgt, p, err) - return - } - defer rc.Close() - - // GET http://example.com/a/index.htm HTTP/1.1 --> - // GET /a/index.htm HTTP/1.1 - u.Scheme = "" - u.Host = "" - uri := u.String() - - var reqBuf bytes.Buffer - writeFirstLine(&reqBuf, method, uri, proto) - writeHeaders(&reqBuf, reqHeader) - - // send request to remote server - rc.Write(reqBuf.Bytes()) - - // copy the left request bytes to remote server. eg. length specificed or chunked body - go func() { - if _, err := reqR.Peek(1); err == nil { - io.Copy(rc, reqR) - rc.SetDeadline(time.Now()) - c.SetDeadline(time.Now()) - } - }() - - respR := bufio.NewReader(rc) - respTP := textproto.NewReader(respR) - proto, code, status, ok := parseFirstLine(respTP) - if !ok { - return - } - - respHeader, err := respTP.ReadMIMEHeader() - if err != nil { - log.F("[http] %s <-> %s via %s, read header error: %v", c.RemoteAddr(), tgt, p, err) - return - } - - respHeader.Set("Proxy-Connection", "close") - respHeader.Set("Connection", "close") - - var respBuf bytes.Buffer - writeFirstLine(&respBuf, proto, code, status) - writeHeaders(&respBuf, respHeader) - - log.F("[http] %s <-> %s via %s", c.RemoteAddr(), tgt, p) - c.Write(respBuf.Bytes()) - - io.Copy(c, respR) - -} - -func (s *HTTP) servHTTPS(method, requestURI, proto string, c net.Conn) { - rc, p, err := s.proxy.Dial("tcp", requestURI) - if err != nil { - c.Write([]byte(proto)) - c.Write([]byte(" 502 ERROR\r\n\r\n")) - log.F("[http] %s <-> %s [c] via %s, error in dial: %v", c.RemoteAddr(), requestURI, p, err) - return - } - - c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) - - log.F("[http] %s <-> %s [c] via %s", c.RemoteAddr(), requestURI, p) - - _, _, err = conn.Relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } - log.F("[http] relay error: %v", err) - } -} - -// Addr returns forwarder's address. -func (s *HTTP) Addr() string { - if s.addr == "" { - return s.dialer.Addr() - } - return s.addr -} - -// Dial connects to the address addr on the network net via the proxy. -func (s *HTTP) Dial(network, addr string) (net.Conn, error) { - rc, err := s.dialer.Dial(network, s.addr) - if err != nil { - log.F("[http] dial to %s error: %s", s.addr, err) - return nil, err - } - - var buf bytes.Buffer - buf.Write([]byte("CONNECT " + addr + " HTTP/1.1\r\n")) - // TODO: add host header for compatibility? - buf.Write([]byte("Proxy-Connection: Keep-Alive\r\n")) - - if s.user != "" && s.password != "" { - auth := s.user + ":" + s.password - buf.Write([]byte("Proxy-Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + "\r\n")) - } - - // header ended - buf.Write([]byte("\r\n")) - _, err = rc.Write(buf.Bytes()) - if err != nil { - return nil, err - } - - c := conn.NewConn(rc) - tpr := textproto.NewReader(c.Reader()) - _, code, _, ok := parseFirstLine(tpr) - if ok && code == "200" { - tpr.ReadMIMEHeader() - return c, err - } - - if code == "407" { - log.F("[http] authencation needed by proxy %s", s.addr) - } else if code == "405" { - log.F("[http] 'CONNECT' method not allowed by proxy %s", s.addr) - } - - return nil, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code) -} - -// DialUDP connects to the given address via the proxy. -func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { - return nil, nil, errors.New("http client does not support udp") -} - -// parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts. -func parseFirstLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) { - line, err := tp.ReadLine() - if err != nil { - return - } - +// parseStartLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts. +func parseStartLine(line string) (r1, r2, r3 string, ok bool) { s1 := strings.Index(line, " ") s2 := strings.Index(line[s1+1:], " ") if s1 < 0 || s2 < 0 { @@ -306,7 +81,7 @@ func cleanHeaders(header textproto.MIMEHeader) { header.Del("Upgrade") } -func writeFirstLine(buf *bytes.Buffer, s1, s2, s3 string) { +func writeStartLine(buf *bytes.Buffer, s1, s2, s3 string) { buf.WriteString(s1 + " " + s2 + " " + s3 + "\r\n") } @@ -318,3 +93,22 @@ func writeHeaders(buf *bytes.Buffer, header textproto.MIMEHeader) { } buf.WriteString("\r\n") } + +func extractUserPass(auth string) (username, password string, ok bool) { + if !strings.HasPrefix(auth, "Basic ") { + return + } + + b, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(auth, "Basic ")) + if err != nil { + return + } + + s := string(b) + idx := strings.IndexByte(s, ':') + if idx < 0 { + return + } + + return s[:idx], s[idx+1:], true +} diff --git a/proxy/http/request.go b/proxy/http/request.go new file mode 100644 index 0000000..cf627bf --- /dev/null +++ b/proxy/http/request.go @@ -0,0 +1,114 @@ +package http + +import ( + "bufio" + "bytes" + "errors" + "net/textproto" + "net/url" + "strings" + + "github.com/nadoo/glider/common/log" +) + +// Methods are http methods from rfc. +// https://www.ietf.org/rfc/rfc2616.txt, http methods must be uppercase +var Methods = [...][]byte{ + []byte("GET"), + []byte("POST"), + []byte("PUT"), + []byte("DELETE"), + []byte("CONNECT"), + []byte("HEAD"), + []byte("OPTIONS"), + []byte("TRACE"), + []byte("PATCH"), +} + +type request struct { + method string + uri string + proto string + auth string + header textproto.MIMEHeader + + target string // target host with port + ruri string // relative uri + absuri string // absolute uri +} + +func parseRequest(r *bufio.Reader) (*request, error) { + tpr := textproto.NewReader(r) + line, err := tpr.ReadLine() + if err != nil { + return nil, err + } + + method, uri, proto, ok := parseStartLine(line) + if !ok { + return nil, errors.New("error in parseStartLine") + } + + header, err := tpr.ReadMIMEHeader() + if err != nil { + log.F("[http] read header error:%s", err) + return nil, err + } + + auth := header.Get("Proxy-Authorization") + + cleanHeaders(header) + header.Set("Connection", "close") + + u, err := url.ParseRequestURI(uri) + if err != nil { + log.F("[http] parse request url error: %s, uri: %s", err, uri) + return nil, err + } + + var tgt = u.Host + if !strings.Contains(u.Host, ":") { + tgt += ":80" + } + + req := &request{ + method: method, + uri: uri, + proto: proto, + auth: auth, + header: header, + target: tgt, + } + + if u.IsAbs() { + req.absuri = u.String() + u.Scheme = "" + u.Host = "" + req.ruri = u.String() + } else { + req.ruri = u.String() + + base, err := url.Parse("http://" + header.Get("Host")) + if err != nil { + return nil, err + } + u = base.ResolveReference(u) + req.absuri = u.String() + } + + return req, nil +} + +func (r *request) Marshal() []byte { + var buf bytes.Buffer + writeStartLine(&buf, r.method, r.ruri, r.proto) + writeHeaders(&buf, r.header) + return buf.Bytes() +} + +func (r *request) MarshalAbs() []byte { + var buf bytes.Buffer + writeStartLine(&buf, r.method, r.absuri, r.proto) + writeHeaders(&buf, r.header) + return buf.Bytes() +} diff --git a/proxy/http/server.go b/proxy/http/server.go new file mode 100644 index 0000000..dccd7c7 --- /dev/null +++ b/proxy/http/server.go @@ -0,0 +1,165 @@ +package http + +import ( + "bufio" + "bytes" + "fmt" + "io" + "net" + "net/textproto" + "time" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +// NewHTTPServer returns a http proxy server. +func NewHTTPServer(s string, p proxy.Proxy) (proxy.Server, error) { + return NewHTTP(s, nil, p) +} + +// ListenAndServe listens on server's addr and serves connections. +func (s *HTTP) ListenAndServe() { + l, err := net.Listen("tcp", s.addr) + if err != nil { + log.F("[http] failed to listen on %s: %v", s.addr, err) + return + } + defer l.Close() + + log.F("[http] listening TCP on %s", s.addr) + + for { + c, err := l.Accept() + if err != nil { + log.F("[http] failed to accept: %v", err) + continue + } + + go s.Serve(c) + } +} + +// Serve serves a connection. +func (s *HTTP) Serve(cc net.Conn) { + defer cc.Close() + + var c *conn.Conn + switch ccc := cc.(type) { + case *net.TCPConn: + ccc.SetKeepAlive(true) + c = conn.NewConn(ccc) + case *conn.Conn: + c = ccc + } + + req, err := parseRequest(c.Reader()) + if err != nil { + log.F("[http] can not parse request from %s", c.RemoteAddr()) + return + } + + if s.pretend { + fmt.Fprintf(c, "%s 404 Not Found\r\nServer: nginx\r\n\r\n404 Not Found\r\n", req.proto) + log.F("[http] %s <-> %s,pretend as web server", c.RemoteAddr().String(), s.Addr()) + return + } + + s.servRequest(req, c) +} + +func (s *HTTP) servRequest(req *request, c *conn.Conn) { + // Auth + if s.user != "" && s.password != "" { + if user, pass, ok := extractUserPass(req.auth); !ok || user != s.user || pass != s.password { + c.Write([]byte("HTTP/1.1 407 Proxy Authentication Required\r\nProxy-Authenticate: Basic\r\n\r\n")) + log.F("[http] auth failed from %s, auth info: %s:%s", c.RemoteAddr(), user, pass) + return + } + } + + if req.method == "CONNECT" { + s.servHTTPS(req, c) + return + } + + s.servHTTP(req, c) +} + +func (s *HTTP) servHTTPS(r *request, c net.Conn) { + rc, p, err := s.proxy.Dial("tcp", r.uri) + if err != nil { + c.Write([]byte(r.proto + " 502 ERROR\r\n\r\n")) + log.F("[http] %s <-> %s [c] via %s, error in dial: %v", c.RemoteAddr(), r.uri, p, err) + return + } + defer rc.Close() + + c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n")) + + log.F("[http] %s <-> %s [c] via %s", c.RemoteAddr(), r.uri, p) + + _, _, err = conn.Relay(c, rc) + if err != nil { + if err, ok := err.(net.Error); ok && err.Timeout() { + return // ignore i/o timeout + } + log.F("[http] relay error: %v", err) + } +} + +func (s *HTTP) servHTTP(req *request, c *conn.Conn) { + rc, p, err := s.proxy.Dial("tcp", req.target) + if err != nil { + fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", req.proto) + log.F("[http] %s <-> %s via %s, error in dial: %v", c.RemoteAddr(), req.target, p, err) + return + } + defer rc.Close() + + // send request to remote server + _, err = rc.Write(req.Marshal()) + if err != nil { + return + } + + // copy the left request bytes to remote server. eg. length specificed or chunked body. + go func() { + if _, err := c.Reader().Peek(1); err == nil { + io.Copy(rc, c) + rc.SetDeadline(time.Now()) + c.SetDeadline(time.Now()) + } + }() + + r := bufio.NewReader(rc) + tpr := textproto.NewReader(r) + line, err := tpr.ReadLine() + if err != nil { + return + } + + proto, code, status, ok := parseStartLine(line) + if !ok { + return + } + + header, err := tpr.ReadMIMEHeader() + if err != nil { + log.F("[http] read header error:%s", err) + return + } + + header.Set("Proxy-Connection", "close") + header.Set("Connection", "close") + + var buf bytes.Buffer + writeStartLine(&buf, proto, code, status) + writeHeaders(&buf, header) + + log.F("[http] %s <-> %s", c.RemoteAddr(), req.target) + c.Write(buf.Bytes()) + + io.Copy(c, r) +} diff --git a/proxy/mixed/mixed.go b/proxy/mixed/mixed.go index f07236d..e59e554 100644 --- a/proxy/mixed/mixed.go +++ b/proxy/mixed/mixed.go @@ -12,26 +12,13 @@ import ( "github.com/nadoo/glider/proxy/socks5" ) -// https://www.ietf.org/rfc/rfc2616.txt, http methods must be uppercase -var httpMethods = [...][]byte{ - []byte("GET"), - []byte("POST"), - []byte("PUT"), - []byte("DELETE"), - []byte("CONNECT"), - []byte("HEAD"), - []byte("OPTIONS"), - []byte("TRACE"), - []byte("PATCH"), -} - // Mixed struct. type Mixed struct { proxy proxy.Proxy addr string - http *http.HTTP - socks5 *socks5.Socks5 + httpServer *http.HTTP + socks5Server *socks5.Socks5 } func init() { @@ -51,8 +38,15 @@ func NewMixed(s string, p proxy.Proxy) (*Mixed, error) { addr: u.Host, } - m.http, _ = http.NewHTTP(s, nil, p) - m.socks5, _ = socks5.NewSocks5(s, nil, p) + m.httpServer, err = http.NewHTTP(s, nil, p) + if err != nil { + return nil, err + } + + m.socks5Server, err = socks5.NewSocks5(s, nil, p) + if err != nil { + return nil, err + } return m, nil } @@ -64,7 +58,7 @@ func NewMixedServer(s string, p proxy.Proxy) (proxy.Server, error) { // ListenAndServe listens on server's addr and serves connections. func (m *Mixed) ListenAndServe() { - go m.socks5.ListenAndServeUDP() + go m.socks5Server.ListenAndServeUDP() l, err := net.Listen("tcp", m.addr) if err != nil { @@ -95,33 +89,30 @@ func (m *Mixed) Serve(c net.Conn) { cc := conn.NewConn(c) - if m.socks5 != nil { - head, err := cc.Peek(1) - if err != nil { - // log.F("[mixed] socks5 peek error: %s", err) - return - } + head, err := cc.Peek(1) + if err != nil { + // log.F("[mixed] socks5 peek error: %s", err) + return + } - // check socks5, client send socksversion: 5 as the first byte - if head[0] == socks5.Version { - m.socks5.Serve(cc) + // check socks5, client send socksversion: 5 as the first byte + if head[0] == socks5.Version { + m.socks5Server.Serve(cc) + return + } + + head, err = cc.Peek(8) + if err != nil { + log.F("[mixed] http peek error: %s", err) + return + } + + for _, method := range http.Methods { + if bytes.HasPrefix(head, method) { + m.httpServer.Serve(cc) return } } - if m.http != nil { - head, err := cc.Peek(8) - if err != nil { - log.F("[mixed] http peek error: %s", err) - return - } - - for _, method := range httpMethods { - if bytes.HasPrefix(head, method) { - m.http.Serve(cc) - return - } - } - } - + log.F("[mixed] unknown request from %s, ignored", c.RemoteAddr()) } diff --git a/proxy/socks5/socks5.go b/proxy/socks5/socks5.go index 0de87f9..c6f0165 100644 --- a/proxy/socks5/socks5.go +++ b/proxy/socks5/socks5.go @@ -6,9 +6,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// socks5 server: -// https://github.com/shadowsocks/go-shadowsocks2/tree/master/socks - // Package socks5 implements a socks5 proxy. package socks5 @@ -106,11 +103,16 @@ func (s *Socks5) ListenAndServeTCP() { } // Serve serves a connection. -func (s *Socks5) Serve(c net.Conn) { - defer c.Close() +func (s *Socks5) Serve(cc net.Conn) { + defer cc.Close() - if c, ok := c.(*net.TCPConn); ok { - c.SetKeepAlive(true) + var c *conn.Conn + switch ccc := cc.(type) { + case *net.TCPConn: + ccc.SetKeepAlive(true) + c = conn.NewConn(ccc) + case *conn.Conn: + c = ccc } tgt, err := s.handshake(c) @@ -129,7 +131,7 @@ func (s *Socks5) Serve(c net.Conn) { } } - log.F("[socks5] failed to get target address: %v", err) + log.F("[socks5] failed in handshake with %s: %v", c.RemoteAddr(), err) return } @@ -253,7 +255,7 @@ func (s *Socks5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A } // send VER, NMETHODS, METHODS - c.Write([]byte{5, 1, 0}) + c.Write([]byte{Version, 1, 0}) buf := make([]byte, socks.MaxAddrLen) // read VER METHOD @@ -263,7 +265,7 @@ func (s *Socks5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A dstAddr := socks.ParseAddr(addr) // write VER CMD RSV ATYP DST.ADDR DST.PORT - c.Write(append([]byte{5, socks.CmdUDPAssociate, 0}, dstAddr...)) + c.Write(append([]byte{Version, socks.CmdUDPAssociate, 0}, dstAddr...)) // read VER REP RSV ATYP BND.ADDR BND.PORT if _, err := io.ReadFull(c, buf[:3]); err != nil { @@ -325,7 +327,7 @@ func (s *Socks5) connect(conn net.Conn, target string) error { if _, err := io.ReadFull(conn, buf[:2]); err != nil { return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } - if buf[0] != 5 { + if buf[0] != Version { return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) } if buf[1] == 0xff { @@ -432,14 +434,73 @@ func (s *Socks5) handshake(rw io.ReadWriter) (socks.Addr, error) { if _, err := io.ReadFull(rw, buf[:2]); err != nil { return nil, err } + nmethods := buf[1] if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil { return nil, err } + // write VER METHOD - if _, err := rw.Write([]byte{5, 0}); err != nil { + if s.user != "" && s.password != "" { + _, err := rw.Write([]byte{Version, socks.AuthPassword}) + if err != nil { + return nil, err + } + + _, err = io.ReadFull(rw, buf[:2]) + if err != nil { + return nil, err + } + + // Get username + userLen := int(buf[1]) + if userLen <= 0 { + rw.Write([]byte{1, 1}) + return nil, errors.New("auth failed: wrong username length") + } + + if _, err := io.ReadFull(rw, buf[:userLen]); err != nil { + return nil, errors.New("auth failed: cannot get username") + } + user := string(buf[:userLen]) + + // Get password + _, err = rw.Read(buf[:1]) + if err != nil { + return nil, errors.New("auth failed: cannot get password len") + } + + passLen := int(buf[0]) + if passLen <= 0 { + rw.Write([]byte{1, 1}) + return nil, errors.New("auth failed: wrong password length") + } + + _, err = io.ReadFull(rw, buf[:passLen]) + if err != nil { + return nil, errors.New("auth failed: cannot get password") + } + pass := string(buf[:passLen]) + + // Verify + if user != s.user || pass != s.password { + _, err = rw.Write([]byte{1, 1}) + if err != nil { + return nil, err + } + return nil, errors.New("auth failed, authinfo: " + user + ":" + pass) + } + + // Response auth state + _, err = rw.Write([]byte{1, 0}) + if err != nil { + return nil, err + } + + } else if _, err := rw.Write([]byte{Version, socks.AuthNone}); err != nil { return nil, err } + // read VER CMD RSV ATYP DST.ADDR DST.PORT if _, err := io.ReadFull(rw, buf[:3]); err != nil { return nil, err diff --git a/proxy/ssr/ssr.go b/proxy/ssr/ssr.go index 4da8704..b9f6650 100644 --- a/proxy/ssr/ssr.go +++ b/proxy/ssr/ssr.go @@ -7,10 +7,10 @@ import ( "strconv" "strings" - shadowsocksr "github.com/sun8911879/shadowsocksR" - "github.com/sun8911879/shadowsocksR/obfs" - "github.com/sun8911879/shadowsocksR/protocol" - "github.com/sun8911879/shadowsocksR/ssr" + shadowsocksr "github.com/nadoo/shadowsocksR" + "github.com/nadoo/shadowsocksR/obfs" + "github.com/nadoo/shadowsocksR/protocol" + "github.com/nadoo/shadowsocksR/ssr" "github.com/nadoo/glider/common/log" "github.com/nadoo/glider/common/socks" diff --git a/proxy/tcptun/tcptun.go b/proxy/tcptun/tcptun.go index 1898863..7da066b 100644 --- a/proxy/tcptun/tcptun.go +++ b/proxy/tcptun/tcptun.go @@ -1,6 +1,7 @@ package tcptun import ( + "errors" "net" "net/url" "strings" @@ -26,12 +27,15 @@ func init() { func NewTCPTun(s string, p proxy.Proxy) (*TCPTun, error) { u, err := url.Parse(s) if err != nil { - log.F("parse err: %s", err) + log.F("[tcptun] parse err: %s", err) return nil, err } addr := u.Host d := strings.Split(addr, "=") + if len(d) < 2 { + return nil, errors.New("error in strings.Split") + } t := &TCPTun{ proxy: p, diff --git a/proxy/udptun/udptun.go b/proxy/udptun/udptun.go index d2d66a6..0bee32a 100644 --- a/proxy/udptun/udptun.go +++ b/proxy/udptun/udptun.go @@ -1,6 +1,7 @@ package udptun import ( + "errors" "net" "net/url" "strings" @@ -34,6 +35,9 @@ func NewUDPTun(s string, p proxy.Proxy) (*UDPTun, error) { addr := u.Host d := strings.Split(addr, "=") + if len(d) < 2 { + return nil, errors.New("error in strings.Split") + } ut := &UDPTun{ proxy: p, diff --git a/proxy/uottun/uottun.go b/proxy/uottun/uottun.go index 1f6be96..3bbe142 100644 --- a/proxy/uottun/uottun.go +++ b/proxy/uottun/uottun.go @@ -1,6 +1,7 @@ package uottun import ( + "errors" "io/ioutil" "net" "net/url" @@ -34,6 +35,9 @@ func NewUoTTun(s string, p proxy.Proxy) (*UoTTun, error) { addr := u.Host d := strings.Split(addr, "=") + if len(d) < 2 { + return nil, errors.New("error in strings.Split") + } ut := &UoTTun{ proxy: p,