mirror of
https://github.com/dutchcoders/transfer.sh.git
synced 2025-01-15 21:20:19 +01:00
304 lines
7.9 KiB
Go
304 lines
7.9 KiB
Go
|
// Copyright 2017 Google Inc. All rights reserved.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package mobile
|
||
|
|
||
|
import (
|
||
|
"crypto/tls"
|
||
|
"crypto/x509"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"net"
|
||
|
"net/http"
|
||
|
"os"
|
||
|
"path"
|
||
|
"time"
|
||
|
|
||
|
"github.com/google/martian"
|
||
|
"github.com/google/martian/api"
|
||
|
"github.com/google/martian/cors"
|
||
|
"github.com/google/martian/cybervillains"
|
||
|
"github.com/google/martian/fifo"
|
||
|
"github.com/google/martian/har"
|
||
|
"github.com/google/martian/httpspec"
|
||
|
mlog "github.com/google/martian/log"
|
||
|
"github.com/google/martian/marbl"
|
||
|
"github.com/google/martian/martianhttp"
|
||
|
"github.com/google/martian/mitm"
|
||
|
"github.com/google/martian/servemux"
|
||
|
"github.com/google/martian/trafficshape"
|
||
|
"github.com/google/martian/verify"
|
||
|
|
||
|
// side-effect importing to register with JSON API
|
||
|
_ "github.com/google/martian/body"
|
||
|
_ "github.com/google/martian/cookie"
|
||
|
_ "github.com/google/martian/failure"
|
||
|
_ "github.com/google/martian/header"
|
||
|
_ "github.com/google/martian/martianurl"
|
||
|
_ "github.com/google/martian/method"
|
||
|
_ "github.com/google/martian/pingback"
|
||
|
_ "github.com/google/martian/port"
|
||
|
_ "github.com/google/martian/priority"
|
||
|
_ "github.com/google/martian/querystring"
|
||
|
_ "github.com/google/martian/skip"
|
||
|
_ "github.com/google/martian/stash"
|
||
|
_ "github.com/google/martian/static"
|
||
|
_ "github.com/google/martian/status"
|
||
|
)
|
||
|
|
||
|
// Martian is a wrapper for the initialized Martian proxy
|
||
|
type Martian struct {
|
||
|
proxy *martian.Proxy
|
||
|
listener net.Listener
|
||
|
apiListener net.Listener
|
||
|
mux *http.ServeMux
|
||
|
started bool
|
||
|
HARLogging bool
|
||
|
TrafficPort int
|
||
|
TrafficShaping bool
|
||
|
APIPort int
|
||
|
APIOverTLS bool
|
||
|
BindLocalhost bool
|
||
|
Cert string
|
||
|
Key string
|
||
|
AllowCORS bool
|
||
|
RoundTripper *http.Transport
|
||
|
}
|
||
|
|
||
|
// EnableCybervillains configures Martian to use the Cybervillians certificate.
|
||
|
func (m *Martian) EnableCybervillains() {
|
||
|
m.Cert = cybervillains.Cert
|
||
|
m.Key = cybervillains.Key
|
||
|
}
|
||
|
|
||
|
// NewProxy creates a new Martian struct for configuring and starting a martian.
|
||
|
func NewProxy() *Martian {
|
||
|
return &Martian{}
|
||
|
}
|
||
|
|
||
|
// Start starts the proxy given the configured values of the Martian struct.
|
||
|
func (m *Martian) Start() {
|
||
|
var err error
|
||
|
m.listener, err = net.Listen("tcp", m.bindAddress(m.TrafficPort))
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
mlog.Debugf("mobile: started listener on: %v", m.listener.Addr())
|
||
|
m.proxy = martian.NewProxy()
|
||
|
m.mux = http.NewServeMux()
|
||
|
|
||
|
if m.Cert != "" && m.Key != "" {
|
||
|
tlsc, err := tls.X509KeyPair([]byte(m.Cert), []byte(m.Key))
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
mlog.Debugf("mobile: loaded cert and key")
|
||
|
|
||
|
x509c, err := x509.ParseCertificate(tlsc.Certificate[0])
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
mlog.Debugf("mobile: parsed cert")
|
||
|
|
||
|
mc, err := mitm.NewConfig(x509c, tlsc.PrivateKey)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
mc.SetValidity(12 * time.Hour)
|
||
|
mc.SetOrganization("Martian Proxy")
|
||
|
|
||
|
m.proxy.SetMITM(mc)
|
||
|
|
||
|
if m.RoundTripper != nil {
|
||
|
m.proxy.SetRoundTripper(m.RoundTripper)
|
||
|
}
|
||
|
m.handle("/authority.cer", martianhttp.NewAuthorityHandler(x509c))
|
||
|
}
|
||
|
|
||
|
// Enable Traffic shaping if requested
|
||
|
if m.TrafficShaping {
|
||
|
tsl := trafficshape.NewListener(m.listener)
|
||
|
tsh := trafficshape.NewHandler(tsl)
|
||
|
m.handle("/shape-traffic", tsh)
|
||
|
m.listener = tsl
|
||
|
}
|
||
|
|
||
|
// Forward traffic that pattern matches in m.mux before applying
|
||
|
// httpspec modifiers (via modifier, specifically)
|
||
|
topg := fifo.NewGroup()
|
||
|
apif := servemux.NewFilter(m.mux)
|
||
|
apif.SetRequestModifier(api.NewForwarder("", m.APIPort))
|
||
|
topg.AddRequestModifier(apif)
|
||
|
|
||
|
stack, fg := httpspec.NewStack("martian.mobile")
|
||
|
topg.AddRequestModifier(stack)
|
||
|
topg.AddResponseModifier(stack)
|
||
|
|
||
|
m.proxy.SetRequestModifier(topg)
|
||
|
m.proxy.SetResponseModifier(topg)
|
||
|
|
||
|
if m.HARLogging {
|
||
|
// add HAR logger for unmodified logs.
|
||
|
uhl := har.NewLogger()
|
||
|
uhmuxf := servemux.NewFilter(m.mux)
|
||
|
uhmuxf.RequestWhenFalse(uhl)
|
||
|
uhmuxf.ResponseWhenFalse(uhl)
|
||
|
fg.AddRequestModifier(uhmuxf)
|
||
|
fg.AddResponseModifier(uhmuxf)
|
||
|
|
||
|
// add HAR logger
|
||
|
hl := har.NewLogger()
|
||
|
hmuxf := servemux.NewFilter(m.mux)
|
||
|
hmuxf.RequestWhenFalse(hl)
|
||
|
hmuxf.ResponseWhenFalse(hl)
|
||
|
stack.AddRequestModifier(hmuxf)
|
||
|
stack.AddResponseModifier(hmuxf)
|
||
|
|
||
|
// Retrieve Unmodified HAR logs
|
||
|
m.handle("/logs/original", har.NewExportHandler(uhl))
|
||
|
m.handle("/logs/original/reset", har.NewResetHandler(uhl))
|
||
|
|
||
|
// Retrieve HAR logs
|
||
|
m.handle("/logs", har.NewExportHandler(hl))
|
||
|
m.handle("/logs/reset", har.NewResetHandler(hl))
|
||
|
}
|
||
|
|
||
|
lsh := marbl.NewHandler()
|
||
|
// retrieve binary marbl logs
|
||
|
m.handle("/binlogs", lsh)
|
||
|
|
||
|
lsm := marbl.NewModifier(lsh)
|
||
|
muxf := servemux.NewFilter(m.mux)
|
||
|
muxf.RequestWhenFalse(lsm)
|
||
|
muxf.ResponseWhenFalse(lsm)
|
||
|
stack.AddRequestModifier(muxf)
|
||
|
stack.AddResponseModifier(muxf)
|
||
|
|
||
|
mod := martianhttp.NewModifier()
|
||
|
fg.AddRequestModifier(mod)
|
||
|
fg.AddResponseModifier(mod)
|
||
|
|
||
|
// Proxy specific handlers.
|
||
|
// These handlers take precendence over proxy traffic and will not be intercepted.
|
||
|
|
||
|
// Update modifiers.
|
||
|
m.handle("/configure", mod)
|
||
|
|
||
|
// Verify assertions.
|
||
|
vh := verify.NewHandler()
|
||
|
vh.SetRequestVerifier(mod)
|
||
|
vh.SetResponseVerifier(mod)
|
||
|
|
||
|
m.handle("/verify", vh)
|
||
|
|
||
|
// Reset verifications.
|
||
|
rh := verify.NewResetHandler()
|
||
|
rh.SetRequestVerifier(mod)
|
||
|
rh.SetResponseVerifier(mod)
|
||
|
m.handle("/verify/reset", rh)
|
||
|
|
||
|
mlog.Infof("mobile: starting Martian proxy on listener")
|
||
|
go m.proxy.Serve(m.listener)
|
||
|
|
||
|
// start the API server
|
||
|
apiAddr := m.bindAddress(m.APIPort)
|
||
|
m.apiListener, err = net.Listen("tcp", apiAddr)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
if m.APIOverTLS {
|
||
|
if m.Cert == "" || m.Key == "" {
|
||
|
log.Fatal("mobile: APIOverTLS cannot be true without valid cert and key")
|
||
|
}
|
||
|
|
||
|
cerfile, err := ioutil.TempFile("", "martian-api.cert")
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
keyfile, err := ioutil.TempFile("", "martian-api.key")
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if _, err := cerfile.Write([]byte(m.Cert)); err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if _, err := keyfile.Write([]byte(m.Key)); err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
go func() {
|
||
|
http.ServeTLS(m.apiListener, m.mux, cerfile.Name(), keyfile.Name())
|
||
|
defer os.Remove(cerfile.Name())
|
||
|
defer os.Remove(keyfile.Name())
|
||
|
}()
|
||
|
|
||
|
mlog.Infof("mobile: proxy API started on %s over TLS", apiAddr)
|
||
|
} else {
|
||
|
go http.Serve(m.apiListener, m.mux)
|
||
|
mlog.Infof("mobile: proxy API started on %s", apiAddr)
|
||
|
}
|
||
|
|
||
|
m.started = true
|
||
|
}
|
||
|
|
||
|
// IsStarted returns true if the proxy has finished starting.
|
||
|
func (m *Martian) IsStarted() bool {
|
||
|
return m.started
|
||
|
}
|
||
|
|
||
|
// Shutdown tells the Proxy to close. This function returns immediately, though
|
||
|
// there may still be connection threads hanging around until they time out
|
||
|
// depending on how the OS manages them.
|
||
|
func (m *Martian) Shutdown() {
|
||
|
mlog.Infof("mobile: shutting down proxy")
|
||
|
m.listener.Close()
|
||
|
m.apiListener.Close()
|
||
|
m.proxy.Close()
|
||
|
m.started = false
|
||
|
mlog.Infof("mobile: proxy shut down")
|
||
|
}
|
||
|
|
||
|
// SetLogLevel sets the Martian log level (Silent = 0, Error, Info, Debug), controlling which Martian
|
||
|
// log calls are displayed in the console
|
||
|
func SetLogLevel(l int) {
|
||
|
mlog.SetLevel(l)
|
||
|
}
|
||
|
|
||
|
func (m *Martian) handle(pattern string, handler http.Handler) {
|
||
|
if m.AllowCORS {
|
||
|
handler = cors.NewHandler(handler)
|
||
|
}
|
||
|
m.mux.Handle(pattern, handler)
|
||
|
mlog.Infof("mobile: handler registered for %s", pattern)
|
||
|
|
||
|
lhp := path.Join(fmt.Sprintf("localhost:%d", m.APIPort), pattern)
|
||
|
m.mux.Handle(lhp, handler)
|
||
|
mlog.Infof("mobile: handler registered for %s", lhp)
|
||
|
}
|
||
|
|
||
|
func (m *Martian) bindAddress(port int) string {
|
||
|
if m.BindLocalhost {
|
||
|
return fmt.Sprintf("[::1]:%d", port)
|
||
|
}
|
||
|
return fmt.Sprintf(":%d", port)
|
||
|
}
|