Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
snail007
GitHub Repository: snail007/goproxy
Path: blob/master/services/http.go
686 views
1
package services
2
3
import (
4
"fmt"
5
"io"
6
"log"
7
"net"
8
"github.com/snail007/goproxy/utils"
9
"runtime/debug"
10
"strconv"
11
)
12
13
type HTTP struct {
14
outPool utils.OutPool
15
cfg HTTPArgs
16
checker utils.Checker
17
basicAuth utils.BasicAuth
18
}
19
20
func NewHTTP() Service {
21
return &HTTP{
22
outPool: utils.OutPool{},
23
cfg: HTTPArgs{},
24
checker: utils.Checker{},
25
basicAuth: utils.BasicAuth{},
26
}
27
}
28
func (s *HTTP) InitService() {
29
s.InitBasicAuth()
30
if *s.cfg.Parent != "" {
31
s.checker = utils.NewChecker(*s.cfg.HTTPTimeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct)
32
}
33
}
34
func (s *HTTP) StopService() {
35
if s.outPool.Pool != nil {
36
s.outPool.Pool.ReleaseAll()
37
}
38
}
39
func (s *HTTP) Start(args interface{}) (err error) {
40
s.cfg = args.(HTTPArgs)
41
if *s.cfg.Parent != "" {
42
log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent)
43
s.InitOutConnPool()
44
}
45
46
s.InitService()
47
48
host, port, _ := net.SplitHostPort(*s.cfg.Local)
49
p, _ := strconv.Atoi(port)
50
sc := utils.NewServerChannel(host, p)
51
if *s.cfg.LocalType == TYPE_TCP {
52
err = sc.ListenTCP(s.callback)
53
} else {
54
err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, s.callback)
55
}
56
if err != nil {
57
return
58
}
59
log.Printf("%s http(s) proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr())
60
return
61
}
62
63
func (s *HTTP) Clean() {
64
s.StopService()
65
}
66
func (s *HTTP) callback(inConn net.Conn) {
67
defer func() {
68
if err := recover(); err != nil {
69
log.Printf("http(s) conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack()))
70
}
71
}()
72
req, err := utils.NewHTTPRequest(&inConn, 4096, s.IsBasicAuth(), &s.basicAuth)
73
if err != nil {
74
if err != io.EOF {
75
log.Printf("decoder error , form %s, ERR:%s", err, inConn.RemoteAddr())
76
}
77
utils.CloseConn(&inConn)
78
return
79
}
80
address := req.Host
81
82
useProxy := true
83
if *s.cfg.Parent == "" {
84
useProxy = false
85
} else if *s.cfg.Always {
86
useProxy = true
87
} else {
88
if req.IsHTTPS() {
89
s.checker.Add(address, true, req.Method, "", nil)
90
} else {
91
s.checker.Add(address, false, req.Method, req.URL, req.HeadBuf)
92
}
93
//var n, m uint
94
useProxy, _, _ = s.checker.IsBlocked(req.Host)
95
//log.Printf("blocked ? : %v, %s , fail:%d ,success:%d", useProxy, address, n, m)
96
}
97
log.Printf("use proxy : %v, %s", useProxy, address)
98
//os.Exit(0)
99
err = s.OutToTCP(useProxy, address, &inConn, &req)
100
if err != nil {
101
if *s.cfg.Parent == "" {
102
log.Printf("connect to %s fail, ERR:%s", address, err)
103
} else {
104
log.Printf("connect to %s parent %s fail", *s.cfg.ParentType, *s.cfg.Parent)
105
}
106
utils.CloseConn(&inConn)
107
}
108
}
109
func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *utils.HTTPRequest) (err error) {
110
inAddr := (*inConn).RemoteAddr().String()
111
inLocalAddr := (*inConn).LocalAddr().String()
112
//防止死循环
113
if s.IsDeadLoop(inLocalAddr, req.Host) {
114
utils.CloseConn(inConn)
115
err = fmt.Errorf("dead loop detected , %s", req.Host)
116
return
117
}
118
var outConn net.Conn
119
var _outConn interface{}
120
if useProxy {
121
_outConn, err = s.outPool.Pool.Get()
122
if err == nil {
123
outConn = _outConn.(net.Conn)
124
}
125
} else {
126
outConn, err = utils.ConnectHost(address, *s.cfg.Timeout)
127
}
128
if err != nil {
129
log.Printf("connect to %s , err:%s", *s.cfg.Parent, err)
130
utils.CloseConn(inConn)
131
return
132
}
133
134
outAddr := outConn.RemoteAddr().String()
135
outLocalAddr := outConn.LocalAddr().String()
136
137
if req.IsHTTPS() && !useProxy {
138
req.HTTPSReply()
139
} else {
140
outConn.Write(req.HeadBuf)
141
}
142
utils.IoBind((*inConn), outConn, func(isSrcErr bool, err error) {
143
log.Printf("conn %s - %s - %s -%s released [%s]", inAddr, inLocalAddr, outLocalAddr, outAddr, req.Host)
144
utils.CloseConn(inConn)
145
utils.CloseConn(&outConn)
146
}, func(n int, d bool) {}, 0)
147
log.Printf("conn %s - %s - %s - %s connected [%s]", inAddr, inLocalAddr, outLocalAddr, outAddr, req.Host)
148
return
149
}
150
func (s *HTTP) OutToUDP(inConn *net.Conn) (err error) {
151
return
152
}
153
func (s *HTTP) InitOutConnPool() {
154
if *s.cfg.ParentType == TYPE_TLS || *s.cfg.ParentType == TYPE_TCP {
155
//dur int, isTLS bool, certBytes, keyBytes []byte,
156
//parent string, timeout int, InitialCap int, MaxCap int
157
s.outPool = utils.NewOutPool(
158
*s.cfg.CheckParentInterval,
159
*s.cfg.ParentType == TYPE_TLS,
160
s.cfg.CertBytes, s.cfg.KeyBytes,
161
*s.cfg.Parent,
162
*s.cfg.Timeout,
163
*s.cfg.PoolSize,
164
*s.cfg.PoolSize*2,
165
)
166
}
167
}
168
func (s *HTTP) InitBasicAuth() (err error) {
169
s.basicAuth = utils.NewBasicAuth()
170
if *s.cfg.AuthFile != "" {
171
var n = 0
172
n, err = s.basicAuth.AddFromFile(*s.cfg.AuthFile)
173
if err != nil {
174
err = fmt.Errorf("auth-file ERR:%s", err)
175
return
176
}
177
log.Printf("auth data added from file %d , total:%d", n, s.basicAuth.Total())
178
}
179
if len(*s.cfg.Auth) > 0 {
180
n := s.basicAuth.Add(*s.cfg.Auth)
181
log.Printf("auth data added %d, total:%d", n, s.basicAuth.Total())
182
}
183
return
184
}
185
func (s *HTTP) IsBasicAuth() bool {
186
return *s.cfg.AuthFile != "" || len(*s.cfg.Auth) > 0
187
}
188
func (s *HTTP) IsDeadLoop(inLocalAddr string, host string) bool {
189
inIP, inPort, err := net.SplitHostPort(inLocalAddr)
190
if err != nil {
191
return false
192
}
193
outDomain, outPort, err := net.SplitHostPort(host)
194
if err != nil {
195
return false
196
}
197
if inPort == outPort {
198
var outIPs []net.IP
199
outIPs, err = net.LookupIP(outDomain)
200
if err == nil {
201
for _, ip := range outIPs {
202
if ip.String() == inIP {
203
return true
204
}
205
}
206
}
207
interfaceIPs, err := utils.GetAllInterfaceAddr()
208
if err == nil {
209
for _, localIP := range interfaceIPs {
210
for _, outIP := range outIPs {
211
if localIP.Equal(outIP) {
212
return true
213
}
214
}
215
}
216
}
217
}
218
return false
219
}
220
221