fix http proxy
This commit is contained in:
parent
037a79a00d
commit
e19ff132f4
179
conn.go
179
conn.go
@ -2,18 +2,47 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"github.com/ginuerzh/gosocks5"
|
"github.com/ginuerzh/gosocks5"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConnHttp = "http"
|
||||||
|
ConnHttpConnect = "http-connect"
|
||||||
|
ConnSocks5 = "socks5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func listenAndServe(arg Args) error {
|
func listenAndServe(arg Args) error {
|
||||||
var ln net.Listener
|
var ln net.Listener
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
if glog.V(3) {
|
||||||
|
b := bytes.Buffer{}
|
||||||
|
b.WriteString("listen on %s, use %s tunnel and %s protocol for data transport. ")
|
||||||
|
if arg.EncMeth == "tls" {
|
||||||
|
b.WriteString("for socks5, tls encrypt method is supported.")
|
||||||
|
} else {
|
||||||
|
b.WriteString("for socks5, tls encrypt method is NOT supported.")
|
||||||
|
}
|
||||||
|
protocol := arg.Protocol
|
||||||
|
if protocol == "" {
|
||||||
|
protocol = "http/socks5"
|
||||||
|
}
|
||||||
|
glog.Infof(b.String(), arg.Addr, arg.Transport, protocol)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
switch arg.Transport {
|
switch arg.Transport {
|
||||||
case "ws": // websocket connection
|
case "ws": // websocket connection
|
||||||
err = NewWs(arg).ListenAndServe()
|
err = NewWs(arg).ListenAndServe()
|
||||||
@ -26,6 +55,8 @@ func listenAndServe(arg Args) error {
|
|||||||
case "tls": // tls connection
|
case "tls": // tls connection
|
||||||
ln, err = tls.Listen("tcp", arg.Addr,
|
ln, err = tls.Listen("tcp", arg.Addr,
|
||||||
&tls.Config{Certificates: []tls.Certificate{arg.Cert}})
|
&tls.Config{Certificates: []tls.Certificate{arg.Cert}})
|
||||||
|
case "tcp":
|
||||||
|
fallthrough
|
||||||
default:
|
default:
|
||||||
ln, err = net.Listen("tcp", arg.Addr)
|
ln, err = net.Listen("tcp", arg.Addr)
|
||||||
}
|
}
|
||||||
@ -36,6 +67,7 @@ func listenAndServe(arg Args) error {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer ln.Close()
|
defer ln.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -61,11 +93,14 @@ func handleConn(conn net.Conn, arg Args) {
|
|||||||
selector := &serverSelector{
|
selector := &serverSelector{
|
||||||
methods: []uint8{
|
methods: []uint8{
|
||||||
gosocks5.MethodNoAuth, gosocks5.MethodUserPass,
|
gosocks5.MethodNoAuth, gosocks5.MethodUserPass,
|
||||||
MethodTLS, MethodTLSAuth,
|
|
||||||
},
|
},
|
||||||
arg: arg,
|
arg: arg,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if arg.EncMeth == "tls" {
|
||||||
|
selector.methods = append(selector.methods, MethodTLS, MethodTLSAuth)
|
||||||
|
}
|
||||||
|
|
||||||
switch arg.Protocol {
|
switch arg.Protocol {
|
||||||
case "ss": // shadowsocks
|
case "ss": // shadowsocks
|
||||||
return
|
return
|
||||||
@ -174,3 +209,145 @@ func (r *reqReader) Read(p []byte) (n int, err error) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func connect(connType, addr string) (conn net.Conn, err error) {
|
||||||
|
if !strings.Contains(addr, ":") {
|
||||||
|
addr += ":80"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(forwardArgs) > 0 {
|
||||||
|
// TODO: multi-foward
|
||||||
|
forward := forwardArgs[0]
|
||||||
|
return connectForward(addr, forward)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(proxyArgs) > 0 {
|
||||||
|
proxy := proxyArgs[0]
|
||||||
|
return connectProxy(connType, addr, proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return net.Dial("tcp", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectProxy(connType, addr string, proxy Args) (conn net.Conn, err error) {
|
||||||
|
if glog.V(LINFO) {
|
||||||
|
glog.Infoln("connect proxy:", proxy.Addr)
|
||||||
|
}
|
||||||
|
conn, err = net.Dial("tcp", proxy.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch proxy.Transport {
|
||||||
|
case "ws": // websocket connection
|
||||||
|
c, err := wsClient(conn, proxy.Addr)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn = c
|
||||||
|
case "tls": // tls connection
|
||||||
|
conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
||||||
|
case "tcp":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
switch proxy.Protocol {
|
||||||
|
case "ss": // shadowsocks
|
||||||
|
conn.Close()
|
||||||
|
return nil, errors.New("Not implemented")
|
||||||
|
case "socks", "socks5":
|
||||||
|
selector := &clientSelector{
|
||||||
|
methods: []uint8{gosocks5.MethodNoAuth, gosocks5.MethodUserPass},
|
||||||
|
arg: proxy,
|
||||||
|
}
|
||||||
|
if proxy.EncMeth == "tls" {
|
||||||
|
selector.methods = []uint8{MethodTLS, MethodTLSAuth}
|
||||||
|
}
|
||||||
|
c := gosocks5.ClientConn(conn, selector)
|
||||||
|
if err := c.Handleshake(); err != nil {
|
||||||
|
c.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn = c
|
||||||
|
|
||||||
|
if connType == ConnHttp || connType == ConnHttpConnect {
|
||||||
|
host, port, _ := net.SplitHostPort(addr)
|
||||||
|
p, _ := strconv.ParseUint(port, 10, 16)
|
||||||
|
r := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{
|
||||||
|
Type: gosocks5.AddrDomain,
|
||||||
|
Host: host,
|
||||||
|
Port: uint16(p),
|
||||||
|
})
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
glog.Infoln(r.String())
|
||||||
|
}
|
||||||
|
if err := r.Write(conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rep, err := gosocks5.ReadReply(conn)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
glog.Infoln(rep.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if rep.Rep != gosocks5.Succeeded {
|
||||||
|
conn.Close()
|
||||||
|
return nil, errors.New("Service unavailable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "http":
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
if connType == ConnHttpConnect {
|
||||||
|
req := &http.Request{
|
||||||
|
Method: "CONNECT",
|
||||||
|
URL: &url.URL{Host: addr},
|
||||||
|
Host: addr,
|
||||||
|
ProtoMajor: 1,
|
||||||
|
ProtoMinor: 1,
|
||||||
|
Header: make(http.Header),
|
||||||
|
}
|
||||||
|
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||||
|
if proxy.User != nil {
|
||||||
|
req.Header.Set("Proxy-Authorization",
|
||||||
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(proxy.User.String())))
|
||||||
|
}
|
||||||
|
if err = req.Write(conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
dump, _ := httputil.DumpRequest(req, false)
|
||||||
|
glog.Infoln(string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
glog.Infoln(string(dump))
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
conn.Close()
|
||||||
|
//log.Println(resp.Status)
|
||||||
|
return nil, errors.New(resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectForward(addr string, forward Args) (net.Conn, error) {
|
||||||
|
return nil, errors.New("Not implemented")
|
||||||
|
}
|
||||||
|
52
http.go
52
http.go
@ -19,13 +19,18 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connType := ConnHttp
|
||||||
|
if req.Method == "CONNECT" {
|
||||||
|
connType = ConnHttpConnect
|
||||||
|
}
|
||||||
|
|
||||||
var username, password string
|
var username, password string
|
||||||
if arg.User != nil {
|
if arg.User != nil {
|
||||||
username = arg.User.Username()
|
username = arg.User.Username()
|
||||||
password, _ = arg.User.Password()
|
password, _ = arg.User.Password()
|
||||||
}
|
}
|
||||||
|
|
||||||
u, p, _ := proxyBasicAuth(req.Header.Get("Proxy-Authorization"))
|
u, p, _ := basicAuth(req.Header.Get("Proxy-Authorization"))
|
||||||
req.Header.Del("Proxy-Authorization")
|
req.Header.Del("Proxy-Authorization")
|
||||||
|
|
||||||
if (username != "" && u != username) || (password != "" && p != password) {
|
if (username != "" && u != username) || (password != "" && p != password) {
|
||||||
@ -46,9 +51,52 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c, err := connect(connType, req.Host)
|
||||||
|
if err != nil {
|
||||||
|
if glog.V(LWARNING) {
|
||||||
|
glog.Warningln(err)
|
||||||
|
}
|
||||||
|
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
||||||
|
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
glog.Infoln(string(b))
|
||||||
|
}
|
||||||
|
conn.Write(b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
if connType == ConnHttpConnect {
|
||||||
|
b := []byte("HTTP/1.1 200 Connection established\r\n" +
|
||||||
|
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||||
|
if glog.V(LDEBUG) {
|
||||||
|
glog.Infoln(string(b))
|
||||||
|
}
|
||||||
|
if _, err := conn.Write(b); err != nil {
|
||||||
|
if glog.V(LWARNING) {
|
||||||
|
glog.Warningln(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(proxyArgs) > 0 {
|
||||||
|
err = req.WriteProxy(c)
|
||||||
|
} else {
|
||||||
|
err = req.Write(c)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if glog.V(LWARNING) {
|
||||||
|
glog.Warningln(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Transport(conn, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func proxyBasicAuth(authInfo string) (username, password string, ok bool) {
|
func basicAuth(authInfo string) (username, password string, ok bool) {
|
||||||
if authInfo == "" {
|
if authInfo == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
20
main.go
20
main.go
@ -17,6 +17,7 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
listenUrl, proxyUrl, forwardUrl string
|
listenUrl, proxyUrl, forwardUrl string
|
||||||
|
pv bool // print version
|
||||||
|
|
||||||
listenArgs []Args
|
listenArgs []Args
|
||||||
proxyArgs []Args
|
proxyArgs []Args
|
||||||
@ -27,6 +28,7 @@ func init() {
|
|||||||
flag.StringVar(&listenUrl, "L", ":http", "local address")
|
flag.StringVar(&listenUrl, "L", ":http", "local address")
|
||||||
flag.StringVar(&forwardUrl, "S", "", "remote address")
|
flag.StringVar(&forwardUrl, "S", "", "remote address")
|
||||||
flag.StringVar(&proxyUrl, "P", "", "proxy address")
|
flag.StringVar(&proxyUrl, "P", "", "proxy address")
|
||||||
|
flag.BoolVar(&pv, "V", false, "print version")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -38,22 +40,22 @@ func init() {
|
|||||||
func main() {
|
func main() {
|
||||||
defer glog.Flush()
|
defer glog.Flush()
|
||||||
|
|
||||||
|
if pv {
|
||||||
|
printVersion()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if len(listenArgs) == 0 {
|
if len(listenArgs) == 0 {
|
||||||
glog.Fatalln("no listen addr")
|
glog.Fatalln("no listen addr")
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
for _, args := range listenArgs {
|
||||||
for _, arg := range listenArgs {
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func(arg Args) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if err := listenAndServe(arg); err != nil {
|
listenAndServe(arg)
|
||||||
if glog.V(LFATAL) {
|
}(args)
|
||||||
glog.Errorln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
5
socks.go
5
socks.go
@ -14,11 +14,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type clientSelector struct {
|
type clientSelector struct {
|
||||||
|
methods []uint8
|
||||||
arg Args
|
arg Args
|
||||||
}
|
}
|
||||||
|
|
||||||
func (selector *clientSelector) Methods() []uint8 {
|
func (selector *clientSelector) Methods() []uint8 {
|
||||||
return nil
|
return selector.methods
|
||||||
}
|
}
|
||||||
|
|
||||||
func (selector *clientSelector) Select(methods ...uint8) (method uint8) {
|
func (selector *clientSelector) Select(methods ...uint8) (method uint8) {
|
||||||
@ -187,7 +188,7 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn, arg Args) {
|
|||||||
if glog.V(LINFO) {
|
if glog.V(LINFO) {
|
||||||
glog.Infoln("socks5 connect:", req.Addr.String())
|
glog.Infoln("socks5 connect:", req.Addr.String())
|
||||||
}
|
}
|
||||||
tconn, err := connect(req.Addr.String())
|
tconn, err := connect(ConnSocks5, req.Addr.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if glog.V(LWARNING) {
|
if glog.V(LWARNING) {
|
||||||
glog.Warningln("socks5 connect:", err)
|
glog.Warningln("socks5 connect:", err)
|
||||||
|
55
util.go
55
util.go
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"io"
|
"io"
|
||||||
@ -11,10 +10,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// socks://admin:123456@localhost:8080
|
// socks://admin:123456@localhost:8080/tls
|
||||||
type Args struct {
|
type Args struct {
|
||||||
Addr string // host:port
|
Addr string // host:port
|
||||||
Protocol string // protocol: hs/http/socks/socks5/ss, default is hs(http+socks5)
|
Protocol string // protocol: http&socks5/http/socks/socks5/ss, default is http&socks5
|
||||||
Transport string // transport: tcp/ws/tls, default is tcp(raw tcp)
|
Transport string // transport: tcp/ws/tls, default is tcp(raw tcp)
|
||||||
User *url.Userinfo
|
User *url.Userinfo
|
||||||
EncMeth string // data encryption method
|
EncMeth string // data encryption method
|
||||||
@ -41,7 +40,7 @@ func parseArgs(rawurl string) (args []Args) {
|
|||||||
|
|
||||||
for _, s := range ss {
|
for _, s := range ss {
|
||||||
if !strings.Contains(s, "://") {
|
if !strings.Contains(s, "://") {
|
||||||
s = "hs://" + s
|
s = "tcp://" + s
|
||||||
}
|
}
|
||||||
u, err := url.Parse(s)
|
u, err := url.Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -58,23 +57,23 @@ func parseArgs(rawurl string) (args []Args) {
|
|||||||
|
|
||||||
schemes := strings.Split(u.Scheme, "+")
|
schemes := strings.Split(u.Scheme, "+")
|
||||||
if len(schemes) == 1 {
|
if len(schemes) == 1 {
|
||||||
switch schemes[0] {
|
|
||||||
case "http", "socks", "socks5", "ss":
|
|
||||||
arg.Protocol = schemes[0]
|
arg.Protocol = schemes[0]
|
||||||
case "ws", "tls", "tcp":
|
|
||||||
arg.Transport = schemes[0]
|
arg.Transport = schemes[0]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if len(schemes) == 2 {
|
if len(schemes) == 2 {
|
||||||
arg.Protocol = schemes[0]
|
arg.Protocol = schemes[0]
|
||||||
arg.Transport = schemes[1]
|
arg.Transport = schemes[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
arg.Cert, err = tls.LoadX509KeyPair("cert.pem", "key.pem")
|
switch arg.Protocol {
|
||||||
if err != nil {
|
case "http", "socks", "socks5", "ss":
|
||||||
if glog.V(LFATAL) {
|
default:
|
||||||
glog.Errorln(err, ", tls will not be supported")
|
arg.Protocol = ""
|
||||||
}
|
}
|
||||||
|
switch arg.Transport {
|
||||||
|
case "ws", "tls", "tcp":
|
||||||
|
default:
|
||||||
|
arg.Transport = "tcp"
|
||||||
}
|
}
|
||||||
|
|
||||||
mp := strings.Split(strings.Trim(u.Path, "/"), ":")
|
mp := strings.Split(strings.Trim(u.Path, "/"), ":")
|
||||||
@ -85,8 +84,15 @@ func parseArgs(rawurl string) (args []Args) {
|
|||||||
arg.EncMeth = mp[0]
|
arg.EncMeth = mp[0]
|
||||||
arg.EncPass = mp[1]
|
arg.EncPass = mp[1]
|
||||||
}
|
}
|
||||||
if glog.V(LINFO) {
|
|
||||||
glog.Infoln(arg)
|
if arg.Transport == "tls" || arg.EncMeth == "tls" {
|
||||||
|
arg.Cert, err = tls.LoadX509KeyPair("cert.pem", "key.pem")
|
||||||
|
if err != nil {
|
||||||
|
if glog.V(LFATAL) {
|
||||||
|
glog.Errorln(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
args = append(args, arg)
|
args = append(args, arg)
|
||||||
}
|
}
|
||||||
@ -94,27 +100,6 @@ func parseArgs(rawurl string) (args []Args) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect(addr string) (net.Conn, error) {
|
|
||||||
if !strings.Contains(addr, ":") {
|
|
||||||
addr += ":80"
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if proxyURL == nil {
|
|
||||||
return dial(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch proxyURL.Scheme {
|
|
||||||
case "socks": // socks5 proxy
|
|
||||||
return connectSocks5Proxy(addr)
|
|
||||||
case "http": // http proxy
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
return connectHTTPProxy(addr)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return nil, errors.New("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// based on io.Copy
|
// based on io.Copy
|
||||||
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
|
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
|
||||||
buf := make([]byte, 32*1024)
|
buf := make([]byte, 32*1024)
|
||||||
|
Loading…
Reference in New Issue
Block a user