/
client.go
167 lines (139 loc) · 4.14 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package main
import (
"flag"
"fmt"
"github.com/elazarl/goproxy"
"github.com/hidu/kx-proxy-client/client"
kxutil "github.com/hidu/kx-proxy/util"
"io/ioutil"
"log"
"net/http"
"net/url"
)
const (
kxKey = "KxKey"
kxEnc = "kxEnc"
)
var confPath = flag.String("conf", "conf.json", "json conf")
var verbose = flag.Bool("v", false, "should every proxy request be logged to stdout")
var addr = flag.String("addr", ":8080", "proxy listen address")
var conf *client.ClientConf
var mitmConnect *goproxy.ConnectAction
func init() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
func initMimtConnect() {
mitmConnect = &goproxy.ConnectAction{
Action: goproxy.ConnectMitm,
TLSConfig: goproxy.TLSConfigFromCA(&conf.SslCert),
}
}
var alwaysMitm goproxy.FuncHttpsHandler = func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
log.Println("https conn", host, ctx.Req.URL.String())
return mitmConnect, host
}
var httpMitmConnect = &goproxy.ConnectAction{
Action: goproxy.ConnectHTTPMitm,
}
var alwaysHTTPMitm goproxy.FuncHttpsHandler = func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
log.Println("https conn", host, ctx.Req.URL.String())
return httpMitmConnect, host
}
func responseHanderFunc(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
if resp == nil {
return resp
}
resp.Header.Set("Connection", "close")
kxEnc := resp.Header.Get("_kx_enc_")
kxutil.HeaderDec(resp.Header)
//goproxy 会对Content-Encoding =gzip 做处理
//HeaderDec 会对 Content-Encoding 做处理可以让这里的逻辑读取到原始的加密的数据流
if kxEnc == "1" {
body := resp.Body
skey := resp.Request.Header.Get(kxKey)
encodeURL := resp.Request.URL.Path[len("/p/"):]
r := kxutil.CipherStreamReader(skey, encodeURL, body)
resp.Body = ioutil.NopCloser(r)
}
// bd,err:=ioutil.ReadAll(resp.Body)
// fmt.Println("bd:",string(bd),"err:",err)
return resp
}
func copyHeaders(dst, src http.Header) {
for k, vs := range src {
for _, v := range vs {
dst.Add(k, v)
}
}
}
func requestHanderFunc(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
urlOld := r.URL.String()
log.Println("raw_url->", urlOld)
r.Header.Set("Connection", "Close")
r.Header.Del("Proxy-Connection")
if conf.IsProxyHost(urlOld) {
log.Println("direct IsProxyHost->", urlOld)
return r, nil
}
proxy := conf.GetOneProxy()
if proxy == nil {
log.Println("no proxy")
return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml, http.StatusBadGateway, "no proxy")
}
urlNew, _, err := proxy.GenReqUrl(urlOld)
if err != nil {
log.Println("encryptURL", urlOld, "failed", err)
return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml, http.StatusBadGateway, "encrypt url failed")
}
log.Println(urlOld, "--->", urlNew)
// var err error
r.URL, err = url.Parse(urlNew)
if err != nil {
log.Println("parse new url failed", err)
return r, goproxy.NewResponse(r, goproxy.ContentTypeHtml, http.StatusBadGateway, "create url failed,check proxy url")
}
r.Host = r.URL.Host
r.Header.Add("is_client", "1")
r.Header.Set(kxKey, proxy.SecertKey)
if conf.HiddenIp {
r.Header.Set("hidden_ip", "1")
}
kxutil.HeaderEnc(r.Header)
// body:=r.Body
// reader := kxutil.CipherStreamReader(proxy.SecertKey, encodeURL, body)
// r.Body = ioutil.NopCloser(reader)
// r.Header.Set("_kx_enc_","1")
// panic("a")
return r, nil
}
func init() {
df := flag.Usage
flag.Usage = func() {
df()
fmt.Println("\nkx-proxy client\n config eg:\n", client.Assest.GetContent("res/conf.json"))
}
}
func main() {
flag.Parse()
conf = client.LoadConf(*confPath)
initMimtConnect()
proxy := goproxy.NewProxyHttpServer()
proxy.Verbose = *verbose
if conf.SSlOn {
proxy.OnRequest().HandleConnectFunc(alwaysMitm)
} else {
proxy.OnRequest().HandleConnectFunc(alwaysHTTPMitm)
}
proxy.OnRequest().DoFunc(requestHanderFunc)
proxy.OnResponse().DoFunc(responseHanderFunc)
if conf.ParentProxy != "" {
proxy.Tr = &http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) {
return url.Parse(conf.ParentProxy)
},
}
}
log.Println("proxy client listen at ", *addr)
err := http.ListenAndServe(*addr, proxy)
log.Fatal(err)
}