package main
import (
"bytes"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"io"
"net"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/gomitmproxy"
"github.com/AdguardTeam/gomitmproxy/mitm"
"github.com/AdguardTeam/gomitmproxy/proxyutil"
_ "net/http/pprof"
)
func main() {
log.SetLevel(log.DEBUG)
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// Read the MITM cert and key.
tlsCert, err := tls.LoadX509KeyPair("demo.crt", "demo.key")
if err != nil {
log.Fatal(err)
}
privateKey := tlsCert.PrivateKey.(*rsa.PrivateKey)
x509c, err := x509.ParseCertificate(tlsCert.Certificate[0])
if err != nil {
log.Fatal(err)
}
mitmConfig, err := mitm.NewConfig(x509c, privateKey, &CustomCertsStorage{
certsCache: map[string]*tls.Certificate{}},
)
if err != nil {
log.Fatal(err)
}
// Generate certs valid for 7 days.
mitmConfig.SetValidity(time.Hour * 24 * 7)
// Set certs organization.
mitmConfig.SetOrganization("Jakobus")
// Generate a cert-key pair for the HTTP-over-TLS proxy.
/*proxyCert, err := mitmConfig.GetOrCreateCert("127.0.0.1")
if err != nil {
panic(err)
}
/*tlsConfig := &tls.Config{
Certificates: []tls.Certificate{*proxyCert},
}*/
// Prepare the proxy.
addr := &net.TCPAddr{
IP: net.IPv4(0, 0, 0, 0),
Port: 3333,
}
proxy := gomitmproxy.NewProxy(gomitmproxy.Config{
ListenAddr: addr,
MITMConfig: mitmConfig,
MITMExceptions: []string{"example.com"},
OnRequest: onRequest,
OnResponse: onResponse,
OnConnect: onConnect,
})
err = proxy.Start()
if err != nil {
log.Fatal(err)
}
signalChannel := make(chan os.Signal, 1)
signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM)
<-signalChannel
// Stop the proxy.
proxy.Close()
}
func onRequest(session *gomitmproxy.Session) (*http.Request, *http.Response) {
req := session.Request()
log.Printf("onRequest: %s %s", req.Method, req.URL.String())
if req.URL.Host == "testgomitmproxy" {
body := strings.NewReader("
Served by gomitmproxy
")
res := proxyutil.NewResponse(http.StatusOK, body, req)
res.Header.Set("Content-Type", "text/html")
return nil, res
}
return nil, req.Response
}
func onResponse(session *gomitmproxy.Session) *http.Response {
log.Printf("onResponse: %s", session.Request().URL.String())
if _, ok := session.GetProp("blocked"); ok {
log.Printf("onResponse: was blocked")
return nil
}
res := session.Response()
req := session.Request()
if strings.Index(res.Header.Get("Content-Type"), "text/html") != 0 {
// Do nothing with non-HTML responses
return nil
}
b, err := proxyutil.ReadDecompressedBody(res)
// Close the original body.
_ = res.Body.Close()
if err != nil {
return proxyutil.NewErrorResponse(req, err)
}
// Use latin1 before modifying the body. Using this 1-byte encoding will
// let us preserve all original characters regardless of what exactly is
// the encoding.
body, err := proxyutil.DecodeLatin1(bytes.NewReader(b))
if err != nil {
return proxyutil.NewErrorResponse(session.Request(), err)
}
// Modifying the original body.
modifiedBody, err := proxyutil.EncodeLatin1(body + "")
if err != nil {
return proxyutil.NewErrorResponse(session.Request(), err)
}
println("Modified body123:" + string(modifiedBody))
res.Body = io.NopCloser(bytes.NewReader(modifiedBody))
res.Header.Del("Content-Encoding")
res.ContentLength = int64(len(modifiedBody))
return res
}
func onConnect(_ *gomitmproxy.Session, _ string, addr string) (conn net.Conn) {
host, _, err := net.SplitHostPort(addr)
if err == nil && host == "testgomitmproxy" {
// Don't let it connecting there, we'll serve it by ourselves.
return &proxyutil.NoopConn{}
}
return nil
}
// CustomCertsStorage is an example of a custom cert storage.
type CustomCertsStorage struct {
// certsCache is a cache with the generated certificates.
certsCache map[string]*tls.Certificate
}
// Get gets the certificate from the storage.
func (c *CustomCertsStorage) Get(key string) (cert *tls.Certificate, ok bool) {
cert, ok = c.certsCache[key]
return cert, ok
}
// Set saves the certificate to the storage.
func (c *CustomCertsStorage) Set(key string, cert *tls.Certificate) {
c.certsCache[key] = cert
}