Add http proxy support

This commit is contained in:
wweir
2019-01-16 18:42:03 +08:00
parent f6eddb9f96
commit 821cbc302e
8 changed files with 134 additions and 29 deletions
+4 -4
View File
@@ -27,10 +27,10 @@ Yet another cross platform transparent proxy tool
| | |
| 127.0.0.1 or other |
| | |
+-------^----------+---^----^---+
| | |
| | | +----->
| +----------+ | |
+-^-----^----------+---^----^---+
| | | |
| | | | +----->
http(s) proxy | +----------+ | |
2 1 1 2
+ + + +
blocked request normal request
+10 -8
View File
@@ -17,16 +17,17 @@ import (
var Conf = struct {
ConfigFile string
NetType string `toml:"net_type"`
Cipher string `toml:cipher`
Cipher string `toml:"cipher"`
Password string `toml:"password"`
ServerPort string `toml:"server_port"`
ServerAddr string `toml:"server_addr"`
ServerPort string `toml:"server_port"`
ServerAddr string `toml:"server_addr"`
HTTPProxyPort string `toml:"http_proxy_port"`
DnsServer string `toml:"dns_server"`
DNSServer string `toml:"dns_server"`
ClientIP string `toml:"client_ip"`
ClientIPNet net.IP `toml:"-"`
ClearDnsCache string `toml:"clear_dns_cache"`
ClearDNSCache string `toml:"clear_dns_cache"`
BlockList []string `toml:"blocklist"`
Suggestions []string `toml:"suggestions"`
@@ -40,7 +41,8 @@ func init() {
flag.StringVar(&Conf.Password, "p", "12345678", "password")
flag.StringVar(&Conf.ServerPort, "P", "5533", "server mode listen port")
flag.StringVar(&Conf.ServerAddr, "s", "", "server IP (run in client mode if set)")
flag.StringVar(&Conf.DnsServer, "d", "114.114.114.114", "client dns server")
flag.StringVar(&Conf.HTTPProxyPort, "H", "", "http proxy listen port")
flag.StringVar(&Conf.DNSServer, "d", "114.114.114.114", "client dns server")
flag.StringVar(&Conf.ClientIP, "c", "127.0.0.1", "client dns service redirect IP")
if !flag.Parsed() {
@@ -63,9 +65,9 @@ var OnRefreash = []func() error{func() error {
Conf.ClientIPNet = net.ParseIP(Conf.ClientIP)
// clear dns cache
if Conf.ClearDnsCache != "" {
if Conf.ClearDNSCache != "" {
ctx, _ := context.WithTimeout(context.TODO(), 5*time.Second)
if err := exec.CommandContext(ctx, "sh", "-c", Conf.ClearDnsCache).Run(); err != nil {
if err := exec.CommandContext(ctx, "sh", "-c", Conf.ClearDNSCache).Run(); err != nil {
glog.Errorln(err)
}
}
+2 -2
View File
@@ -3,6 +3,7 @@ cipher="AES_128_GCM"
password="12345678"
server_port="5533"
server_addr="remote-server:5533" # replce it to remote server
http_proxy_port="8080"
dns_server="223.5.5.5" # Alibaba public dns
client_ip="127.0.0.1"
# clear_dns_cache="killall -HUP mDNSResponder"
@@ -44,6 +45,7 @@ blocklist=[
"synchrony-cdn.atlassian.com",
"avatar-cdn.atlassian.com",
"*.medium.com",
"*.us-east-1.prod.public.atl-paas.net",
"accounts-static.cdn.mozilla.net", # firefox
"*.services.mozilla.com",
"pocket-image-cache.com", # pocket
@@ -73,8 +75,6 @@ blocklist=[
"download.apkpure.com",
"*.nytimes.com", # New York times
"*.nyt.com",
"bandwagonhost.com", # bandwagonhost
"www.bwh1.net",
"*.akadns.net", # suggestions
"*.haxx.se",
"shadowsocks.org",
+7 -1
View File
@@ -13,8 +13,14 @@ func main() {
if conf.ServerAddr == "" {
proxy.StartServer(conf.NetType, conf.ServerPort, conf.Cipher, conf.Password)
} else {
go dns.StartDNS(conf.DnsServer, conf.ClientIP, conf.ClientIPNet)
if conf.HTTPProxyPort != "" {
go proxy.StartHttpProxy(conf.NetType, conf.ServerAddr,
conf.Cipher, conf.Password, conf.ClientIP, conf.HTTPProxyPort)
}
go dns.StartDNS(conf.DNSServer, conf.ClientIP, conf.ClientIPNet)
proxy.StartClient(conf.NetType, conf.ServerAddr, conf.Cipher, conf.Password, conf.ClientIP)
}
}
+10 -7
View File
@@ -14,20 +14,23 @@ type Client interface {
Dial(server string) (net.Conn, error)
}
func StartClient(netType, server, cipher, password, listenIP string) {
var connCh = listenLocal(listenIP, []string{":80", ":443"})
var client Client
func NewClient(netType string) Client {
switch netType {
case QUIC.String():
client = quic.NewClient()
return quic.NewClient()
case KCP.String():
client = kcp.NewClient()
return kcp.NewClient()
case TCP.String():
client = tcp.NewClient()
return tcp.NewClient()
default:
glog.Fatalln("invalid net type: " + netType)
return nil
}
}
func StartClient(netType, server, cipher, password, listenIP string) {
connCh := listenLocal(listenIP, []string{":80", ":443"})
client := NewClient(netType)
glog.Infoln("Client started.")
for {
+91
View File
@@ -0,0 +1,91 @@
package proxy
import (
"context"
"crypto/tls"
"io"
"net"
"net/http"
"time"
"github.com/golang/glog"
"github.com/wweir/sower/shadow"
)
func StartHttpProxy(netType, server, cipher, password, listenIP, port string) {
if port[0] != ':' {
port = ":" + port
}
client := NewClient(netType)
srv := &http.Server{
Addr: listenIP + port,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodConnect {
httpsProxy(w, r, client, server, cipher, password)
} else {
httpProxy(w, r, client, server, cipher, password)
}
}),
// Disable HTTP/2.
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
}
glog.Fatalln(srv.ListenAndServe())
}
func httpsProxy(w http.ResponseWriter, r *http.Request, client Client, server, cipher, password string) {
// remote conn
rc, err := client.Dial(server)
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
rc = shadow.Shadow(rc, cipher, password)
// local conn
w.WriteHeader(http.StatusOK)
hijacker, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "Hijacking not supported", http.StatusInternalServerError)
return
}
conn, _, err := hijacker.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
}
relay(rc, conn)
}
func httpProxy(w http.ResponseWriter, req *http.Request, client Client, server, cipher, password string) {
roundTripper := &http.Transport{
DialContext: func(context.Context, string, string) (net.Conn, error) {
conn, err := client.Dial(server)
if err != nil {
return nil, err
}
return shadow.Shadow(conn, cipher, password), nil
},
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
resp, err := roundTripper.RoundTrip(req)
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
defer resp.Body.Close()
for k, vs := range resp.Header {
for _, v := range vs {
w.Header().Add(k, v)
}
}
w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body)
}
+1 -1
View File
@@ -15,7 +15,7 @@ type server struct {
SockBuf int
}
func NewServer(password string) *server {
func NewServer() *server {
return &server{
DataShard: 10,
ParityShard: 3,
+9 -6
View File
@@ -16,26 +16,29 @@ type Server interface {
Listen(port string) (<-chan net.Conn, error)
}
func StartServer(netType, port, cipher, password string) {
var server Server
func NewServer(netType string) Server {
switch netType {
case QUIC.String():
server = quic.NewServer()
return quic.NewServer()
case KCP.String():
server = kcp.NewServer(password)
return kcp.NewServer()
case TCP.String():
server = tcp.NewServer()
return tcp.NewServer()
default:
glog.Fatalln("invalid net type: " + netType)
return nil
}
}
func StartServer(netType, port, cipher, password string) {
if port == "" {
glog.Fatalln("port must set")
}
if !strings.Contains(port, ":") {
port = ":" + port
}
connCh, err := server.Listen(port)
connCh, err := NewServer(netType).Listen(port)
if err != nil {
glog.Fatalf("listen %v fail: %s", port, err)
}