mirror of
https://github.com/xjasonlyu/tun2socks.git
synced 2025-10-07 09:41:09 +08:00
Feature: HTTP proxy support
This commit is contained in:
@@ -50,8 +50,10 @@ func parseProxy(s string) (proxy.Proxy, error) {
|
|||||||
return proxy.NewDirect(), nil
|
return proxy.NewDirect(), nil
|
||||||
case proto.Reject.String():
|
case proto.Reject.String():
|
||||||
return proxy.NewReject(), nil
|
return proxy.NewReject(), nil
|
||||||
|
case proto.HTTP.String():
|
||||||
|
return proxy.NewHTTP(parseAddrUser(u))
|
||||||
case proto.Socks5.String():
|
case proto.Socks5.String():
|
||||||
return proxy.NewSocks5(parseSocks(u))
|
return proxy.NewSocks5(parseAddrUser(u))
|
||||||
case proto.Shadowsocks.String():
|
case proto.Shadowsocks.String():
|
||||||
return proxy.NewShadowsocks(parseShadowsocks(u))
|
return proxy.NewShadowsocks(parseShadowsocks(u))
|
||||||
default:
|
default:
|
||||||
@@ -59,7 +61,7 @@ func parseProxy(s string) (proxy.Proxy, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSocks(u *url.URL) (address, username, password string) {
|
func parseAddrUser(u *url.URL) (address, username, password string) {
|
||||||
address = u.Host
|
address = u.Host
|
||||||
username = u.User.Username()
|
username = u.User.Username()
|
||||||
password, _ = u.User.Password()
|
password, _ = u.User.Password()
|
||||||
|
97
proxy/http.go
Normal file
97
proxy/http.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package proxy
|
||||||
|
|
||||||
|
// Ref: https://github.com/Dreamacro/clash/adapter/outbound/http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/xjasonlyu/tun2socks/component/dialer"
|
||||||
|
M "github.com/xjasonlyu/tun2socks/constant"
|
||||||
|
"github.com/xjasonlyu/tun2socks/proxy/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTTP struct {
|
||||||
|
*Base
|
||||||
|
|
||||||
|
user string
|
||||||
|
pass string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHTTP(addr, user, pass string) (*HTTP, error) {
|
||||||
|
return &HTTP{
|
||||||
|
Base: &Base{
|
||||||
|
addr: addr,
|
||||||
|
proto: proto.HTTP,
|
||||||
|
},
|
||||||
|
user: user,
|
||||||
|
pass: pass,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HTTP) DialContext(ctx context.Context, metadata *M.Metadata) (c net.Conn, err error) {
|
||||||
|
c, err = dialer.DialContext(ctx, "tcp", h.Addr())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("connect to %s: %w", h.Addr(), err)
|
||||||
|
}
|
||||||
|
setKeepAlive(c)
|
||||||
|
|
||||||
|
defer safeConnClose(c, err)
|
||||||
|
|
||||||
|
err = h.shakeHand(metadata, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HTTP) shakeHand(metadata *M.Metadata, rw io.ReadWriter) error {
|
||||||
|
addr := metadata.DestinationAddress()
|
||||||
|
req := &http.Request{
|
||||||
|
Method: http.MethodConnect,
|
||||||
|
URL: &url.URL{
|
||||||
|
Host: addr,
|
||||||
|
},
|
||||||
|
Host: addr,
|
||||||
|
Header: http.Header{
|
||||||
|
"Proxy-Connection": []string{"Keep-Alive"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.user != "" && h.pass != "" {
|
||||||
|
auth := h.user + ":" + h.pass
|
||||||
|
req.Header.Add("Proxy-Authorization",
|
||||||
|
fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(auth))))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := req.Write(rw); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(rw), req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusProxyAuthRequired {
|
||||||
|
return errors.New("HTTP need auth")
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusMethodNotAllowed {
|
||||||
|
return errors.New("CONNECT method not allowed by proxy")
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode >= http.StatusInternalServerError {
|
||||||
|
return errors.New(resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("HTTP connect status code: %d", resp.StatusCode)
|
||||||
|
}
|
@@ -5,8 +5,9 @@ import "fmt"
|
|||||||
const (
|
const (
|
||||||
Direct Proto = iota
|
Direct Proto = iota
|
||||||
Reject
|
Reject
|
||||||
Shadowsocks
|
HTTP
|
||||||
Socks5
|
Socks5
|
||||||
|
Shadowsocks
|
||||||
)
|
)
|
||||||
|
|
||||||
type Proto uint8
|
type Proto uint8
|
||||||
@@ -17,10 +18,12 @@ func (proto Proto) String() string {
|
|||||||
return "direct"
|
return "direct"
|
||||||
case Reject:
|
case Reject:
|
||||||
return "reject"
|
return "reject"
|
||||||
case Shadowsocks:
|
case HTTP:
|
||||||
return "ss"
|
return "http"
|
||||||
case Socks5:
|
case Socks5:
|
||||||
return "socks5"
|
return "socks5"
|
||||||
|
case Shadowsocks:
|
||||||
|
return "ss"
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("proto(%d)", proto)
|
return fmt.Sprintf("proto(%d)", proto)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user