Merge branch 'master' of https://github.com/nadoo/glider into HEAD

This commit is contained in:
Aero 2019-12-09 10:50:42 +08:00
commit 1c9df2d55c
19 changed files with 594 additions and 366 deletions

View File

@ -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

View File

@ -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 }}

View File

@ -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

View File

@ -1,7 +1,8 @@
# [glider](https://github.com/nadoo/glider) # [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) [![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&style=flat-square)](https://github.com/nadoo/glider/releases) [![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). 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.

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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
View 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")
}

View File

@ -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
View 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
View 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)
}

View File

@ -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
}
}
}
} }

View File

@ -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

View File

@ -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"

View File

@ -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,

View File

@ -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,

View File

@ -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,