mirror of
https://github.com/nadoo/glider.git
synced 2025-04-21 19:52:07 +08:00
Merge branch 'master' of https://github.com/nadoo/glider into HEAD
This commit is contained in:
commit
1c9df2d55c
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Set up Go 1.13
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
go-version: 1.13
|
go-version: 1.13
|
||||||
@ -26,7 +26,7 @@ jobs:
|
|||||||
needs: [test]
|
needs: [test]
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Set up Go 1.13
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
go-version: 1.13
|
go-version: 1.13
|
||||||
|
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@ -9,14 +9,20 @@ jobs:
|
|||||||
name: Release on GitHub
|
name: Release on GitHub
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v1
|
||||||
|
with:
|
||||||
|
go-version: 1.13
|
||||||
|
id: go
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
- name: Create release on GitHub
|
- name: Create release on GitHub
|
||||||
uses: docker://goreleaser/goreleaser:latest
|
uses: goreleaser/goreleaser-action@v1
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
||||||
with:
|
with:
|
||||||
|
version: latest
|
||||||
args: release
|
args: release
|
||||||
if: success()
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
# git push origin v0.1.0
|
# git push origin v0.1.0
|
||||||
# goreleaser release --skip-publish --rm-dist
|
# goreleaser release --skip-publish --rm-dist
|
||||||
|
|
||||||
|
# #git tag -d v0.1.0
|
||||||
|
# #git push origin --delete tag v0.1.0
|
||||||
|
|
||||||
# snapshot:
|
# snapshot:
|
||||||
# goreleaser --snapshot --rm-dist
|
# goreleaser --snapshot --rm-dist
|
||||||
|
|
||||||
|
22
README.md
22
README.md
@ -1,7 +1,8 @@
|
|||||||
# [glider](https://github.com/nadoo/glider)
|
# [glider](https://github.com/nadoo/glider)
|
||||||
|
|
||||||
[](https://goreportcard.com/report/github.com/nadoo/glider)
|
[](https://goreportcard.com/report/github.com/nadoo/glider)
|
||||||
[](https://github.com/nadoo/glider/releases)
|
[](https://github.com/nadoo/glider/releases)
|
||||||
|
[](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).
|
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 Forwarding Server (udp2tcp):
|
||||||
|
|
||||||
|
- DNS Over Proxy
|
||||||
- Listen on UDP and forward dns requests to remote dns server in TCP via forwarders
|
- 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)
|
- Specify different upstream dns server based on destinations(in rule file)
|
||||||
- Tunnel mode: forward to a fixed upstream dns server
|
- Tunnel mode: forward to a fixed upstream dns server
|
||||||
@ -114,7 +116,7 @@ glider -config CONFIGPATH -listen :8080 -verbose
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
glider 0.8.0 usage:
|
glider 0.9.0 usage:
|
||||||
-checkinterval int
|
-checkinterval int
|
||||||
proxy check interval(seconds) (default 30)
|
proxy check interval(seconds) (default 30)
|
||||||
-checktimeout int
|
-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
|
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:
|
Alias:
|
||||||
chacha20-ietf-poly1305 = AEAD_CHACHA20_POLY1305, xchacha20-ietf-poly1305 = AEAD_XCHACHA20_POLY1305
|
chacha20-ietf-poly1305 = AEAD_CHACHA20_POLY1305, xchacha20-ietf-poly1305 = AEAD_XCHACHA20_POLY1305
|
||||||
|
Plain: DUMMY
|
||||||
|
|
||||||
SSR scheme:
|
SSR scheme:
|
||||||
ssr://method:pass@host:port?protocol=xxx&protocol_param=yyy&obfs=zzz&obfs_param=xyz
|
ssr://method:pass@host:port?protocol=xxx&protocol_param=yyy&obfs=zzz&obfs_param=xyz
|
||||||
@ -280,17 +283,14 @@ Examples:
|
|||||||
glider -config glider.conf
|
glider -config glider.conf
|
||||||
-run glider with specified config file.
|
-run glider with specified config file.
|
||||||
|
|
||||||
glider -config glider.conf -rulefile office.rule -rulefile home.rule
|
glider -listen :8443 -verbose
|
||||||
-run glider with specified global config file and rule config files.
|
-listen on :8443, serve as http/socks5 proxy on the same port, in verbose mode.
|
||||||
|
|
||||||
glider -listen :8443
|
glider -listen ss://AEAD_CHACHA20_POLY1305:pass@:8443 -verbose
|
||||||
-listen on :8443, serve as http/socks5 proxy on the same port.
|
|
||||||
|
|
||||||
glider -listen ss://AEAD_CHACHA20_POLY1305:pass@:8443
|
|
||||||
-listen on 0.0.0.0:8443 as a ss server.
|
-listen on 0.0.0.0:8443 as a ss server.
|
||||||
|
|
||||||
glider -listen socks5://:1080 -verbose
|
glider -listen socks5://user1:pass1@:1080 -verbose
|
||||||
-listen on :1080 as a socks5 proxy server, in verbose mode.
|
-listen on :1080 as a socks5 proxy server, enable authentication.
|
||||||
|
|
||||||
glider -listen tls://:443?cert=crtFilePath&key=keyFilePath,http:// -verbose
|
glider -listen tls://:443?cert=crtFilePath&key=keyFilePath,http:// -verbose
|
||||||
-listen on :443 as a https(http over tls) proxy server.
|
-listen on :443 as a https(http over tls) proxy server.
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
package log
|
package log
|
||||||
|
|
||||||
import stdlog "log"
|
import (
|
||||||
|
stdlog "log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stdlog.SetFlags(stdlog.LstdFlags | stdlog.Lshortfile)
|
||||||
|
}
|
||||||
|
|
||||||
// Func defines a simple log function
|
// Func defines a simple log function
|
||||||
type Func func(f string, v ...interface{})
|
type Func func(f string, v ...interface{})
|
||||||
|
14
conf.go
14
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, " 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, " Alias:\n")
|
||||||
fmt.Fprintf(os.Stderr, " chacha20-ietf-poly1305 = AEAD_CHACHA20_POLY1305, xchacha20-ietf-poly1305 = AEAD_XCHACHA20_POLY1305\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, "\n")
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "SSR scheme:\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, " "+app+" -config glider.conf\n")
|
||||||
fmt.Fprintf(os.Stderr, " -run glider with specified config file.\n")
|
fmt.Fprintf(os.Stderr, " -run glider with specified config file.\n")
|
||||||
fmt.Fprintf(os.Stderr, "\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, " "+app+" -listen :8443 -verbose\n")
|
||||||
fmt.Fprintf(os.Stderr, " -run glider with specified global config file and rule config files.\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, "\n")
|
||||||
fmt.Fprintf(os.Stderr, " "+app+" -listen :8443\n")
|
fmt.Fprintf(os.Stderr, " "+app+" -listen ss://AEAD_CHACHA20_POLY1305:pass@:8443 -verbose\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, " -listen on 0.0.0.0:8443 as a ss server.\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, "\n")
|
||||||
fmt.Fprintf(os.Stderr, " "+app+" -listen socks5://:1080 -verbose\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, in verbose mode.\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, "\n")
|
||||||
fmt.Fprintf(os.Stderr, " "+app+" -listen tls://:443?cert=crtFilePath&key=keyFilePath,http:// -verbose\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")
|
fmt.Fprintf(os.Stderr, " -listen on :443 as a https(http over tls) proxy server.\n")
|
||||||
|
22
go.mod
22
go.mod
@ -3,26 +3,20 @@ module github.com/nadoo/glider
|
|||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
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/cpuid v1.2.1 // indirect
|
||||||
github.com/klauspost/reedsolomon v1.9.2 // indirect
|
github.com/klauspost/reedsolomon v1.9.3 // indirect
|
||||||
github.com/nadoo/conflag v0.2.0
|
github.com/nadoo/conflag v0.2.2
|
||||||
github.com/nadoo/go-shadowsocks2 v0.1.0
|
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/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/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
|
||||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
|
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
|
||||||
github.com/tjfoc/gmsm v1.0.1 // 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
|
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect
|
||||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect
|
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 // indirect
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
|
golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
// Replace dependency modules with local developing copy
|
// Replace dependency modules with local developing copy
|
||||||
|
42
go.sum
42
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 h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
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=
|
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/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 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
|
||||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
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.3 h1:N/VzgeMfHmLc+KHMD1UL/tNkfXAt8FnUqlgXGIduwAY=
|
||||||
github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
github.com/klauspost/reedsolomon v1.9.3/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.2 h1:xywuyaevdBnA3+4g9S11ng+Nby725WN1LXargWnAXpM=
|
||||||
github.com/nadoo/conflag v0.2.0/go.mod h1:Ayl83klaw7fagwYaI6luTmbOi4psAf7FqJNRRv5YMvU=
|
github.com/nadoo/conflag v0.2.2/go.mod h1:dzFfDUpXdr2uS2oV+udpy5N2vfNOu/bFzjhX1WI52co=
|
||||||
github.com/nadoo/go-shadowsocks2 v0.1.0 h1:NkdUrZrI8uYq8R0YDmHLttLqKt0Z9i7dUKtGvBqZQl8=
|
github.com/nadoo/go-shadowsocks2 v0.1.2 h1:+tCSt65YAAMf24wj3tqv6a9oVBcqSGFYVsifBZwT9w8=
|
||||||
github.com/nadoo/go-shadowsocks2 v0.1.0/go.mod h1:J0B/QoRZtqUwE9BJqkP3F3M5+N8t+b5fXeNrkUarveM=
|
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 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
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 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
|
||||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
|
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 h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM=
|
||||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
|
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 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU=
|
||||||
github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
|
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.11+incompatible h1:tJbtarpmOoOD74cZ41uvvF5Hyt1nvctHQCOxZ6ot5xw=
|
||||||
github.com/xtaci/kcp-go v5.4.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
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 h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
||||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
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-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-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191010185427-af544f31c8ac h1:/b4NMZurYfBIQyRMqaPGMDeUrSW6gU7/7Hv6owY1Vjk=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 h1:p9xBe/w/OzkeYVKm234g55gMdD1nSIooTir5kV11kfA=
|
||||||
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/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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/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=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
5
main.go
5
main.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
stdlog "log"
|
stdlog "log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@ -29,7 +30,7 @@ import (
|
|||||||
_ "github.com/nadoo/glider/proxy/ws"
|
_ "github.com/nadoo/glider/proxy/ws"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "0.8.2"
|
var version = "0.9.2"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// read configs
|
// read configs
|
||||||
@ -38,7 +39,7 @@ func main() {
|
|||||||
// setup a log func
|
// setup a log func
|
||||||
log.F = func(f string, v ...interface{}) {
|
log.F = func(f string, v ...interface{}) {
|
||||||
if conf.Verbose {
|
if conf.Verbose {
|
||||||
stdlog.Printf(f, v...)
|
stdlog.Output(2, fmt.Sprintf(f, v...))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
81
proxy/http/client.go
Normal file
81
proxy/http/client.go
Normal file
@ -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")
|
||||||
|
}
|
@ -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.
|
// NOTE: never keep-alive so the implementation can be much easier.
|
||||||
|
|
||||||
|
// Package http implements a http proxy.
|
||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/nadoo/glider/common/conn"
|
|
||||||
"github.com/nadoo/glider/common/log"
|
"github.com/nadoo/glider/common/log"
|
||||||
"github.com/nadoo/glider/proxy"
|
"github.com/nadoo/glider/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTP struct.
|
// HTTP struct.
|
||||||
type HTTP struct {
|
type HTTP struct {
|
||||||
dialer proxy.Dialer
|
dialer proxy.Dialer
|
||||||
proxy proxy.Proxy
|
proxy proxy.Proxy
|
||||||
addr string
|
addr string
|
||||||
user string
|
user string
|
||||||
password string
|
password string
|
||||||
pretendAsWebServer bool
|
pretend bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -49,242 +43,23 @@ func NewHTTP(s string, d proxy.Dialer, p proxy.Proxy) (*HTTP, error) {
|
|||||||
pass, _ := u.User.Password()
|
pass, _ := u.User.Password()
|
||||||
|
|
||||||
h := &HTTP{
|
h := &HTTP{
|
||||||
dialer: d,
|
dialer: d,
|
||||||
proxy: p,
|
proxy: p,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
user: user,
|
user: user,
|
||||||
password: pass,
|
password: pass,
|
||||||
pretendAsWebServer: false,
|
pretend: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
pretend := u.Query().Get("pretend")
|
if u.Query().Get("pretend") == "true" {
|
||||||
if pretend == "true" {
|
h.pretend = true
|
||||||
h.pretendAsWebServer = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTTPDialer returns a http proxy dialer.
|
// parseStartLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts.
|
||||||
func NewHTTPDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
|
func parseStartLine(line string) (r1, r2, r3 string, ok bool) {
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
s1 := strings.Index(line, " ")
|
s1 := strings.Index(line, " ")
|
||||||
s2 := strings.Index(line[s1+1:], " ")
|
s2 := strings.Index(line[s1+1:], " ")
|
||||||
if s1 < 0 || s2 < 0 {
|
if s1 < 0 || s2 < 0 {
|
||||||
@ -306,7 +81,7 @@ func cleanHeaders(header textproto.MIMEHeader) {
|
|||||||
header.Del("Upgrade")
|
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")
|
buf.WriteString(s1 + " " + s2 + " " + s3 + "\r\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,3 +93,22 @@ func writeHeaders(buf *bytes.Buffer, header textproto.MIMEHeader) {
|
|||||||
}
|
}
|
||||||
buf.WriteString("\r\n")
|
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
|
||||||
|
}
|
||||||
|
114
proxy/http/request.go
Normal file
114
proxy/http/request.go
Normal file
@ -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()
|
||||||
|
}
|
165
proxy/http/server.go
Normal file
165
proxy/http/server.go
Normal file
@ -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)
|
||||||
|
}
|
@ -12,26 +12,13 @@ import (
|
|||||||
"github.com/nadoo/glider/proxy/socks5"
|
"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.
|
// Mixed struct.
|
||||||
type Mixed struct {
|
type Mixed struct {
|
||||||
proxy proxy.Proxy
|
proxy proxy.Proxy
|
||||||
addr string
|
addr string
|
||||||
|
|
||||||
http *http.HTTP
|
httpServer *http.HTTP
|
||||||
socks5 *socks5.Socks5
|
socks5Server *socks5.Socks5
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -51,8 +38,15 @@ func NewMixed(s string, p proxy.Proxy) (*Mixed, error) {
|
|||||||
addr: u.Host,
|
addr: u.Host,
|
||||||
}
|
}
|
||||||
|
|
||||||
m.http, _ = http.NewHTTP(s, nil, p)
|
m.httpServer, err = http.NewHTTP(s, nil, p)
|
||||||
m.socks5, _ = socks5.NewSocks5(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
|
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.
|
// ListenAndServe listens on server's addr and serves connections.
|
||||||
func (m *Mixed) ListenAndServe() {
|
func (m *Mixed) ListenAndServe() {
|
||||||
go m.socks5.ListenAndServeUDP()
|
go m.socks5Server.ListenAndServeUDP()
|
||||||
|
|
||||||
l, err := net.Listen("tcp", m.addr)
|
l, err := net.Listen("tcp", m.addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -95,33 +89,30 @@ func (m *Mixed) Serve(c net.Conn) {
|
|||||||
|
|
||||||
cc := conn.NewConn(c)
|
cc := conn.NewConn(c)
|
||||||
|
|
||||||
if m.socks5 != nil {
|
head, err := cc.Peek(1)
|
||||||
head, err := cc.Peek(1)
|
if err != nil {
|
||||||
if err != nil {
|
// log.F("[mixed] socks5 peek error: %s", err)
|
||||||
// log.F("[mixed] socks5 peek error: %s", err)
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// check socks5, client send socksversion: 5 as the first byte
|
// check socks5, client send socksversion: 5 as the first byte
|
||||||
if head[0] == socks5.Version {
|
if head[0] == socks5.Version {
|
||||||
m.socks5.Serve(cc)
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.http != nil {
|
log.F("[mixed] unknown request from %s, ignored", c.RemoteAddr())
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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 implements a socks5 proxy.
|
||||||
package socks5
|
package socks5
|
||||||
|
|
||||||
@ -106,11 +103,16 @@ func (s *Socks5) ListenAndServeTCP() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serve serves a connection.
|
// Serve serves a connection.
|
||||||
func (s *Socks5) Serve(c net.Conn) {
|
func (s *Socks5) Serve(cc net.Conn) {
|
||||||
defer c.Close()
|
defer cc.Close()
|
||||||
|
|
||||||
if c, ok := c.(*net.TCPConn); ok {
|
var c *conn.Conn
|
||||||
c.SetKeepAlive(true)
|
switch ccc := cc.(type) {
|
||||||
|
case *net.TCPConn:
|
||||||
|
ccc.SetKeepAlive(true)
|
||||||
|
c = conn.NewConn(ccc)
|
||||||
|
case *conn.Conn:
|
||||||
|
c = ccc
|
||||||
}
|
}
|
||||||
|
|
||||||
tgt, err := s.handshake(c)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +255,7 @@ func (s *Socks5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send VER, NMETHODS, METHODS
|
// send VER, NMETHODS, METHODS
|
||||||
c.Write([]byte{5, 1, 0})
|
c.Write([]byte{Version, 1, 0})
|
||||||
|
|
||||||
buf := make([]byte, socks.MaxAddrLen)
|
buf := make([]byte, socks.MaxAddrLen)
|
||||||
// read VER METHOD
|
// read VER METHOD
|
||||||
@ -263,7 +265,7 @@ func (s *Socks5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A
|
|||||||
|
|
||||||
dstAddr := socks.ParseAddr(addr)
|
dstAddr := socks.ParseAddr(addr)
|
||||||
// write VER CMD RSV ATYP DST.ADDR DST.PORT
|
// 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
|
// read VER REP RSV ATYP BND.ADDR BND.PORT
|
||||||
if _, err := io.ReadFull(c, buf[:3]); err != nil {
|
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 {
|
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())
|
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])))
|
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||||
}
|
}
|
||||||
if buf[1] == 0xff {
|
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 {
|
if _, err := io.ReadFull(rw, buf[:2]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nmethods := buf[1]
|
nmethods := buf[1]
|
||||||
if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil {
|
if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// write VER METHOD
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// read VER CMD RSV ATYP DST.ADDR DST.PORT
|
// read VER CMD RSV ATYP DST.ADDR DST.PORT
|
||||||
if _, err := io.ReadFull(rw, buf[:3]); err != nil {
|
if _, err := io.ReadFull(rw, buf[:3]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -7,10 +7,10 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
shadowsocksr "github.com/sun8911879/shadowsocksR"
|
shadowsocksr "github.com/nadoo/shadowsocksR"
|
||||||
"github.com/sun8911879/shadowsocksR/obfs"
|
"github.com/nadoo/shadowsocksR/obfs"
|
||||||
"github.com/sun8911879/shadowsocksR/protocol"
|
"github.com/nadoo/shadowsocksR/protocol"
|
||||||
"github.com/sun8911879/shadowsocksR/ssr"
|
"github.com/nadoo/shadowsocksR/ssr"
|
||||||
|
|
||||||
"github.com/nadoo/glider/common/log"
|
"github.com/nadoo/glider/common/log"
|
||||||
"github.com/nadoo/glider/common/socks"
|
"github.com/nadoo/glider/common/socks"
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package tcptun
|
package tcptun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
@ -26,12 +27,15 @@ func init() {
|
|||||||
func NewTCPTun(s string, p proxy.Proxy) (*TCPTun, error) {
|
func NewTCPTun(s string, p proxy.Proxy) (*TCPTun, error) {
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.F("parse err: %s", err)
|
log.F("[tcptun] parse err: %s", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := u.Host
|
addr := u.Host
|
||||||
d := strings.Split(addr, "=")
|
d := strings.Split(addr, "=")
|
||||||
|
if len(d) < 2 {
|
||||||
|
return nil, errors.New("error in strings.Split")
|
||||||
|
}
|
||||||
|
|
||||||
t := &TCPTun{
|
t := &TCPTun{
|
||||||
proxy: p,
|
proxy: p,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package udptun
|
package udptun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
@ -34,6 +35,9 @@ func NewUDPTun(s string, p proxy.Proxy) (*UDPTun, error) {
|
|||||||
|
|
||||||
addr := u.Host
|
addr := u.Host
|
||||||
d := strings.Split(addr, "=")
|
d := strings.Split(addr, "=")
|
||||||
|
if len(d) < 2 {
|
||||||
|
return nil, errors.New("error in strings.Split")
|
||||||
|
}
|
||||||
|
|
||||||
ut := &UDPTun{
|
ut := &UDPTun{
|
||||||
proxy: p,
|
proxy: p,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package uottun
|
package uottun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -34,6 +35,9 @@ func NewUoTTun(s string, p proxy.Proxy) (*UoTTun, error) {
|
|||||||
|
|
||||||
addr := u.Host
|
addr := u.Host
|
||||||
d := strings.Split(addr, "=")
|
d := strings.Split(addr, "=")
|
||||||
|
if len(d) < 2 {
|
||||||
|
return nil, errors.New("error in strings.Split")
|
||||||
|
}
|
||||||
|
|
||||||
ut := &UoTTun{
|
ut := &UoTTun{
|
||||||
proxy: p,
|
proxy: p,
|
||||||
|
Loading…
Reference in New Issue
Block a user