You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

316 lines
6.5 KiB
Go

4 years ago
package proxy
import (
4 years ago
"bytes"
"context"
4 years ago
"io"
"net"
4 years ago
"net/http"
"time"
4 years ago
4 years ago
"github.com/lqqyt2423/go-mitmproxy/addon"
"github.com/lqqyt2423/go-mitmproxy/connection"
4 years ago
"github.com/lqqyt2423/go-mitmproxy/flow"
4 years ago
_log "github.com/sirupsen/logrus"
4 years ago
)
4 years ago
var log = _log.WithField("at", "proxy")
4 years ago
type Options struct {
2 years ago
Debug int
4 years ago
Addr string
StreamLargeBodies int64 // 当请求或响应体大于此字节时,转为 stream 模式
SslInsecure bool
3 years ago
CaRootPath string
4 years ago
}
4 years ago
4 years ago
type Proxy struct {
Opts *Options
Version string
Server *http.Server
Interceptor Interceptor
Addons []addon.Addon
activeConn map[net.Conn]*flow.ConnContext
4 years ago
}
3 years ago
func NewProxy(opts *Options) (*Proxy, error) {
4 years ago
proxy := new(Proxy)
proxy.Opts = opts
3 years ago
proxy.Version = "0.2.0"
4 years ago
proxy.Server = &http.Server{
Addr: opts.Addr,
Handler: proxy,
IdleTimeout: 5 * time.Second,
ConnContext: func(ctx context.Context, c net.Conn) context.Context {
client := connection.NewClient(c)
connCtx := &flow.ConnContext{
Client: client,
}
proxy.activeConn[c] = connCtx
return context.WithValue(ctx, flow.ConnContextKey, connCtx)
},
ConnState: func(c net.Conn, cs http.ConnState) {
if cs == http.StateNew {
client := proxy.activeConn[c].Client
for _, addon := range proxy.Addons {
addon.ClientConnected(client)
}
} else if cs == http.StateClosed {
proxy.whenClientConnClose(c)
}
},
4 years ago
}
3 years ago
interceptor, err := NewMiddle(proxy, opts.CaRootPath)
4 years ago
if err != nil {
return nil, err
}
proxy.Interceptor = interceptor
if opts.StreamLargeBodies <= 0 {
opts.StreamLargeBodies = 1024 * 1024 * 5 // default: 5mb
}
4 years ago
proxy.Addons = make([]addon.Addon, 0)
proxy.activeConn = make(map[net.Conn]*flow.ConnContext)
4 years ago
return proxy, nil
4 years ago
}
4 years ago
func (proxy *Proxy) AddAddon(addon addon.Addon) {
4 years ago
proxy.Addons = append(proxy.Addons, addon)
}
func (proxy *Proxy) Start() error {
errChan := make(chan error)
go func() {
log.Infof("Proxy start listen at %v\n", proxy.Server.Addr)
err := proxy.Server.ListenAndServe()
errChan <- err
}()
go func() {
4 years ago
err := proxy.Interceptor.Start()
errChan <- err
}()
err := <-errChan
return err
}
func (proxy *Proxy) ServeHTTP(res http.ResponseWriter, req *http.Request) {
if req.Method == "CONNECT" {
proxy.handleConnect(res, req)
return
}
4 years ago
log := log.WithFields(_log.Fields{
"in": "Proxy.ServeHTTP",
4 years ago
"url": req.URL,
"method": req.Method,
4 years ago
})
log.Debug("receive request")
if !req.URL.IsAbs() || req.URL.Host == "" {
res.WriteHeader(400)
_, err := io.WriteString(res, "此为代理服务器,不能直接发起请求")
if err != nil {
4 years ago
log.Error(err)
4 years ago
}
return
}
4 years ago
4 years ago
reply := func(response *flow.Response, body io.Reader) {
4 years ago
if response.Header != nil {
for key, value := range response.Header {
for _, v := range value {
res.Header().Add(key, v)
}
}
}
res.WriteHeader(response.StatusCode)
if body != nil {
_, err := io.Copy(res, body)
4 years ago
if err != nil {
LogErr(log, err)
4 years ago
}
} else if response.Body != nil && len(response.Body) > 0 {
_, err := res.Write(response.Body)
4 years ago
if err != nil {
LogErr(log, err)
4 years ago
}
}
}
// when addons panic
defer func() {
if err := recover(); err != nil {
log.Warnf("Recovered: %v\n", err)
}
}()
4 years ago
f := flow.NewFlow()
4 years ago
f.Request = flow.NewRequest(req)
f.ConnContext = req.Context().Value(flow.ConnContextKey).(*flow.ConnContext)
4 years ago
defer f.Finish()
4 years ago
// trigger addon event Requestheaders
for _, addon := range proxy.Addons {
4 years ago
addon.Requestheaders(f)
if f.Response != nil {
reply(f.Response, nil)
4 years ago
return
}
}
4 years ago
// Read request body
4 years ago
var reqBody io.Reader = req.Body
4 years ago
if !f.Stream {
reqBuf, r, err := ReaderToBuffer(req.Body, proxy.Opts.StreamLargeBodies)
4 years ago
reqBody = r
if err != nil {
log.Error(err)
res.WriteHeader(502)
return
}
4 years ago
4 years ago
if reqBuf == nil {
log.Warnf("request body size >= %v\n", proxy.Opts.StreamLargeBodies)
4 years ago
f.Stream = true
4 years ago
} else {
4 years ago
f.Request.Body = reqBuf
4 years ago
4 years ago
// trigger addon event Request
4 years ago
for _, addon := range proxy.Addons {
4 years ago
addon.Request(f)
if f.Response != nil {
reply(f.Response, nil)
4 years ago
return
}
}
4 years ago
reqBody = bytes.NewReader(f.Request.Body)
4 years ago
}
}
4 years ago
4 years ago
proxyReq, err := http.NewRequest(f.Request.Method, f.Request.URL.String(), reqBody)
if err != nil {
4 years ago
log.Error(err)
res.WriteHeader(502)
return
}
4 years ago
4 years ago
for key, value := range f.Request.Header {
for _, v := range value {
proxyReq.Header.Add(key, v)
}
}
f.ConnContext.InitHttpServer(proxy.Opts.SslInsecure)
proxyRes, err := f.ConnContext.Server.Client.Do(proxyReq)
if err != nil {
4 years ago
LogErr(log, err)
res.WriteHeader(502)
return
}
defer proxyRes.Body.Close()
4 years ago
4 years ago
f.Response = &flow.Response{
4 years ago
StatusCode: proxyRes.StatusCode,
Header: proxyRes.Header,
}
// trigger addon event Responseheaders
for _, addon := range proxy.Addons {
4 years ago
addon.Responseheaders(f)
if f.Response.Body != nil {
reply(f.Response, nil)
4 years ago
return
}
}
4 years ago
4 years ago
// Read response body
4 years ago
var resBody io.Reader = proxyRes.Body
4 years ago
if !f.Stream {
resBuf, r, err := ReaderToBuffer(proxyRes.Body, proxy.Opts.StreamLargeBodies)
4 years ago
resBody = r
if err != nil {
log.Error(err)
res.WriteHeader(502)
return
}
if resBuf == nil {
log.Warnf("response body size >= %v\n", proxy.Opts.StreamLargeBodies)
4 years ago
f.Stream = true
4 years ago
} else {
4 years ago
f.Response.Body = resBuf
4 years ago
4 years ago
// trigger addon event Response
4 years ago
for _, addon := range proxy.Addons {
4 years ago
addon.Response(f)
4 years ago
}
}
}
4 years ago
reply(f.Response, resBody)
}
func (proxy *Proxy) handleConnect(res http.ResponseWriter, req *http.Request) {
4 years ago
log := log.WithFields(_log.Fields{
"in": "Proxy.handleConnect",
4 years ago
"host": req.Host,
})
log.Debug("receive connect")
conn, err := proxy.Interceptor.Dial(req)
if err != nil {
4 years ago
log.Error(err)
res.WriteHeader(502)
return
}
defer conn.Close()
4 years ago
cconn, _, err := res.(http.Hijacker).Hijack()
if err != nil {
4 years ago
log.Error(err)
res.WriteHeader(502)
return
}
cconn.(*net.TCPConn).SetLinger(0) // send RST other than FIN when finished, to avoid TIME_WAIT state
cconn.(*net.TCPConn).SetKeepAlive(false)
defer func() {
cconn.Close()
proxy.whenClientConnClose(cconn)
}()
_, err = io.WriteString(cconn, "HTTP/1.1 200 Connection Established\r\n\r\n")
if err != nil {
4 years ago
log.Error(err)
return
}
4 years ago
Transfer(log, conn, cconn)
}
func (proxy *Proxy) whenClientConnClose(c net.Conn) {
connCtx := proxy.activeConn[c]
for _, addon := range proxy.Addons {
addon.ClientDisconnected(connCtx.Client)
}
connCtx.Server.Client.CloseIdleConnections()
delete(proxy.activeConn, c)
}