bugfixes, etc, this is super alpha branch so your patch notes are the diff

master
Rob Glew 7 years ago
parent d5dbf7b29f
commit 469cb9f52d
  1. 10
      README.md
  2. 22
      certs.go
  3. 26
      credits.go
  4. 40
      jobpool.go
  5. 29
      main.go
  6. 12
      messageserv.go
  7. 258
      proxy.go
  8. 293
      proxyhttp.go
  9. 17
      proxyhttp_test.go
  10. 45
      proxylistener.go
  11. 366
      proxymessages.go
  12. 72
      python/puppy/puppyproxy/config.py
  13. 7
      python/puppy/puppyproxy/interface/context.py
  14. 38
      python/puppy/puppyproxy/interface/decode.py
  15. 2
      python/puppy/puppyproxy/interface/misc.py
  16. 12
      python/puppy/puppyproxy/interface/repeater/repeater.py
  17. 101
      python/puppy/puppyproxy/interface/view.py
  18. 77
      python/puppy/puppyproxy/proxy.py
  19. 7
      python/puppy/puppyproxy/pup.py
  20. 5
      python/puppy/puppyproxy/util.py
  21. 42
      schema.go
  22. 10
      search.go
  23. 12
      search_test.go
  24. 1
      signer.go
  25. 104
      sqlitestorage.go
  26. 6
      sqlitestorage_test.go
  27. 19
      storage.go
  28. 4
      testutil.go
  29. 6
      util.go
  30. 169
      webui.go

@ -38,14 +38,8 @@ Then you can run puppy by running `puppy`. It will use the puppy binary in `~/$G
Missing Features From Pappy Missing Features From Pappy
--------------------------- ---------------------------
Here's what Pappy can do that this can't: All that's left is updating documentation!
- The `http://pappy` interface
- Upstream proxies
- Commands taking multiple requests
- Any and all documentation
- The macro API is totally different
Need more info? Need more info?
--------------- ---------------
Right now I haven't written any documentation, so feel free to contact me for help. Right now I haven't written any documentation, so feel free to contact me for help.

@ -3,9 +3,9 @@ package main
import ( import (
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha1"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"crypto/sha1"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"math/big" "math/big"
@ -14,7 +14,7 @@ import (
type CAKeyPair struct { type CAKeyPair struct {
Certificate []byte Certificate []byte
PrivateKey *rsa.PrivateKey PrivateKey *rsa.PrivateKey
} }
func bigIntHash(n *big.Int) []byte { func bigIntHash(n *big.Int) []byte {
@ -41,16 +41,16 @@ func GenerateCACerts() (*CAKeyPair, error) {
template := x509.Certificate{ template := x509.Certificate{
SerialNumber: serial, SerialNumber: serial,
Subject: pkix.Name{ Subject: pkix.Name{
CommonName: "Puppy Proxy", CommonName: "Puppy Proxy",
Organization: []string{"Puppy Proxy"}, Organization: []string{"Puppy Proxy"},
}, },
NotBefore: time.Now().Add(-5 * time.Minute).UTC(), NotBefore: time.Now().Add(-5 * time.Minute).UTC(),
NotAfter: end, NotAfter: end,
SubjectKeyId: bigIntHash(key.N), SubjectKeyId: bigIntHash(key.N),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true, BasicConstraintsValid: true,
IsCA: true, IsCA: true,
MaxPathLenZero: true, MaxPathLenZero: true,
} }
@ -61,23 +61,23 @@ func GenerateCACerts() (*CAKeyPair, error) {
return &CAKeyPair{ return &CAKeyPair{
Certificate: derBytes, Certificate: derBytes,
PrivateKey: key, PrivateKey: key,
}, nil }, nil
} }
func (pair *CAKeyPair) PrivateKeyPEM() ([]byte) { func (pair *CAKeyPair) PrivateKeyPEM() []byte {
return pem.EncodeToMemory( return pem.EncodeToMemory(
&pem.Block{ &pem.Block{
Type: "BEGIN PRIVATE KEY", Type: "BEGIN PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(pair.PrivateKey), Bytes: x509.MarshalPKCS1PrivateKey(pair.PrivateKey),
}, },
) )
} }
func (pair *CAKeyPair) CACertPEM() ([]byte) { func (pair *CAKeyPair) CACertPEM() []byte {
return pem.EncodeToMemory( return pem.EncodeToMemory(
&pem.Block{ &pem.Block{
Type: "CERTIFICATE", Type: "CERTIFICATE",
Bytes: pair.Certificate, Bytes: pair.Certificate,
}, },
) )

@ -5,22 +5,22 @@ List of info that is used to display credits
*/ */
type creditItem struct { type creditItem struct {
projectName string projectName string
url string url string
author string author string
year string year string
licenseType string licenseType string
longCopyright string longCopyright string
} }
var LIB_CREDITS = []creditItem { var LIB_CREDITS = []creditItem{
creditItem { creditItem{
"goproxy", "goproxy",
"https://github.com/elazarl/goproxy", "https://github.com/elazarl/goproxy",
"Elazar Leibovich", "Elazar Leibovich",
"2012", "2012",
"3-Clause BSD", "3-Clause BSD",
`Copyright (c) 2012 Elazar Leibovich. All rights reserved. `Copyright (c) 2012 Elazar Leibovich. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are
@ -49,13 +49,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`, OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`,
}, },
creditItem { creditItem{
"golang-set", "golang-set",
"https://github.com/deckarep/golang-set", "https://github.com/deckarep/golang-set",
"Ralph Caraveo", "Ralph Caraveo",
"2013", "2013",
"MIT", "MIT",
`Open Source Initiative OSI - The MIT License (MIT):Licensing `Open Source Initiative OSI - The MIT License (MIT):Licensing
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com) Copyright (c) 2013 Ralph Caraveo (deckarep@gmail.com)
@ -79,13 +79,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.`, SOFTWARE.`,
}, },
creditItem { creditItem{
"Gorilla WebSocket", "Gorilla WebSocket",
"https://github.com/gorilla/websocket", "https://github.com/gorilla/websocket",
"Gorilla WebSocket Authors", "Gorilla WebSocket Authors",
"2013", "2013",
"2-Clause BSD", "2-Clause BSD",
`Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. `Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:

@ -7,8 +7,8 @@ import (
// An interface which represents a job to be done by the pool // An interface which represents a job to be done by the pool
type Job interface { type Job interface {
Run() // Start the job Run() // Start the job
Abort() // Abort any work that needs to be completed and close the DoneChannel Abort() // Abort any work that needs to be completed and close the DoneChannel
DoneChannel() chan struct{} // Returns a channel that is closed when the job is done DoneChannel() chan struct{} // Returns a channel that is closed when the job is done
} }
@ -16,19 +16,19 @@ type Job interface {
type JobPool struct { type JobPool struct {
MaxThreads int MaxThreads int
jobQueue chan Job jobQueue chan Job
jobQueueDone chan struct{} jobQueueDone chan struct{}
jobQueueAborted chan struct{} jobQueueAborted chan struct{}
jobQueueShutDown chan struct{} jobQueueShutDown chan struct{}
} }
func NewJobPool(maxThreads int) (*JobPool) { func NewJobPool(maxThreads int) *JobPool {
q := JobPool { q := JobPool{
MaxThreads: maxThreads, MaxThreads: maxThreads,
jobQueue: make(chan Job), jobQueue: make(chan Job),
jobQueueDone: make(chan struct{}), // Closing will shut down workers and reject any incoming work jobQueueDone: make(chan struct{}), // Closing will shut down workers and reject any incoming work
jobQueueAborted: make(chan struct{}), // Closing tells workers to abort jobQueueAborted: make(chan struct{}), // Closing tells workers to abort
jobQueueShutDown: make(chan struct{}), // Closed when all workers are shut down jobQueueShutDown: make(chan struct{}), // Closed when all workers are shut down
} }
return &q return &q
@ -49,7 +49,7 @@ func (q *JobPool) Run() {
if q.MaxThreads > 0 { if q.MaxThreads > 0 {
// Create pool of routines that read from the queue and run jobs // Create pool of routines that read from the queue and run jobs
var w sync.WaitGroup var w sync.WaitGroup
for i:=0; i<q.MaxThreads; i++ { for i := 0; i < q.MaxThreads; i++ {
w.Add(1) w.Add(1)
go func() { go func() {
defer w.Done() defer w.Done()
@ -75,7 +75,7 @@ func (q *JobPool) Run() {
}() }()
} }
w.Wait() // Wait for workers to quit w.Wait() // Wait for workers to quit
close(q.jobQueueShutDown) // Flag that all workers quit close(q.jobQueueShutDown) // Flag that all workers quit
} else { } else {
// Create a thread any time we pull something out of the job queue // Create a thread any time we pull something out of the job queue
@ -103,15 +103,15 @@ func (q *JobPool) Run() {
} }
func (q *JobPool) Abort() { func (q *JobPool) Abort() {
close(q.jobQueueDone) // Stop accepting jobs and tell the workers to quit close(q.jobQueueDone) // Stop accepting jobs and tell the workers to quit
close(q.jobQueueAborted) // Tell the workers to abort close(q.jobQueueAborted) // Tell the workers to abort
<-q.jobQueueShutDown // Wait for all the workers to shut down <-q.jobQueueShutDown // Wait for all the workers to shut down
close(q.jobQueue) // Clean up the job queue close(q.jobQueue) // Clean up the job queue
} }
func (q *JobPool) CompleteAndClose() { func (q *JobPool) CompleteAndClose() {
close(q.jobQueueDone) // Stop accepting jobs and tell the workers to quit close(q.jobQueueDone) // Stop accepting jobs and tell the workers to quit
<-q.jobQueueShutDown // Wait for all the workers to shut down <-q.jobQueueShutDown // Wait for all the workers to shut down
close(q.jobQueue) // Clean up the job queue close(q.jobQueue) // Clean up the job queue
close(q.jobQueueAborted) // Clean up abort channel close(q.jobQueueAborted) // Clean up abort channel
} }

@ -6,11 +6,11 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"strings"
"syscall"
"os/signal"
"net" "net"
"os" "os"
"os/signal"
"strings"
"syscall"
"time" "time"
) )
@ -101,7 +101,7 @@ func main() {
flag.Parse() flag.Parse()
if *debugFlag { if *debugFlag {
logfile, err := os.OpenFile("log.log", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) logfile, err := os.OpenFile("log.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
checkErr(err) checkErr(err)
logger = log.New(logfile, "[*] ", log.Lshortfile) logger = log.New(logfile, "[*] ", log.Lshortfile)
} else { } else {
@ -151,28 +151,9 @@ func main() {
} }
} }
// Set up storage
// if *storageFname == "" && *inMemStorage == false {
// quitErr("storage file or -inmem flag required")
// }
// if *storageFname != "" && *inMemStorage == true {
// quitErr("cannot provide both a storage file and -inmem flag")
// }
// var storage MessageStorage
// if *inMemStorage {
// var err error
// storage, err = InMemoryStorage(logger)
// checkErr(err)
// } else {
// var err error
// storage, err = OpenSQLiteStorage(*storageFname, logger)
// checkErr(err)
// }
// Set up the intercepting proxy // Set up the intercepting proxy
iproxy := NewInterceptingProxy(logger) iproxy := NewInterceptingProxy(logger)
// sid := iproxy.AddMessageStorage(storage) iproxy.AddHTTPHandler("puppy", WebUIHandler)
// iproxy.SetProxyStorage(sid)
// Create a message server and have it serve for the iproxy // Create a message server and have it serve for the iproxy
mserv := NewProxyMessageListener(logger, iproxy) mserv := NewProxyMessageListener(logger, iproxy)

@ -4,10 +4,10 @@ import (
"bufio" "bufio"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings"
"io" "io"
"log" "log"
"net" "net"
"strings"
) )
/* /*
@ -18,8 +18,8 @@ type MessageHandler func([]byte, net.Conn, *log.Logger, *InterceptingProxy)
type MessageListener struct { type MessageListener struct {
handlers map[string]MessageHandler handlers map[string]MessageHandler
iproxy *InterceptingProxy iproxy *InterceptingProxy
Logger *log.Logger Logger *log.Logger
} }
type commandData struct { type commandData struct {
@ -28,14 +28,14 @@ type commandData struct {
type errorMessage struct { type errorMessage struct {
Success bool Success bool
Reason string Reason string
} }
func NewMessageListener(l *log.Logger, iproxy *InterceptingProxy) *MessageListener { func NewMessageListener(l *log.Logger, iproxy *InterceptingProxy) *MessageListener {
m := &MessageListener{ m := &MessageListener{
handlers: make(map[string]MessageHandler), handlers: make(map[string]MessageHandler),
iproxy: iproxy, iproxy: iproxy,
Logger: l, Logger: l,
} }
return m return m
} }

@ -2,6 +2,7 @@ package main
import ( import (
"crypto/tls" "crypto/tls"
"encoding/base64"
"fmt" "fmt"
"log" "log"
"net" "net"
@ -15,61 +16,80 @@ import (
var getNextSubId = IdCounter() var getNextSubId = IdCounter()
var getNextStorageId = IdCounter() var getNextStorageId = IdCounter()
// Working on using this for webui
type proxyWebUIHandler func(http.ResponseWriter, *http.Request, *InterceptingProxy)
type savedStorage struct { type savedStorage struct {
storage MessageStorage storage MessageStorage
description string description string
} }
type InterceptingProxy struct { type InterceptingProxy struct {
slistener *ProxyListener slistener *ProxyListener
server *http.Server server *http.Server
mtx sync.Mutex mtx sync.Mutex
logger *log.Logger logger *log.Logger
proxyStorage int proxyStorage int
netDial NetDialer
usingProxy bool
proxyHost string
proxyPort int
proxyIsSOCKS bool
proxyCreds *ProxyCredentials
requestInterceptor RequestInterceptor requestInterceptor RequestInterceptor
responseInterceptor ResponseInterceptor responseInterceptor ResponseInterceptor
wSInterceptor WSInterceptor wSInterceptor WSInterceptor
scopeChecker RequestChecker scopeChecker RequestChecker
scopeQuery MessageQuery scopeQuery MessageQuery
reqSubs []*ReqIntSub reqSubs []*ReqIntSub
rspSubs []*RspIntSub rspSubs []*RspIntSub
wsSubs []*WSIntSub wsSubs []*WSIntSub
httpHandlers map[string]proxyWebUIHandler
messageStorage map[int]*savedStorage messageStorage map[int]*savedStorage
} }
type ProxyCredentials struct {
Username string
Password string
}
type RequestInterceptor func(req *ProxyRequest) (*ProxyRequest, error) type RequestInterceptor func(req *ProxyRequest) (*ProxyRequest, error)
type ResponseInterceptor func(req *ProxyRequest, rsp *ProxyResponse) (*ProxyResponse, error) type ResponseInterceptor func(req *ProxyRequest, rsp *ProxyResponse) (*ProxyResponse, error)
type WSInterceptor func(req *ProxyRequest, rsp *ProxyResponse, msg *ProxyWSMessage) (*ProxyWSMessage, error) type WSInterceptor func(req *ProxyRequest, rsp *ProxyResponse, msg *ProxyWSMessage) (*ProxyWSMessage, error)
type proxyHandler struct {
Logger *log.Logger
IProxy *InterceptingProxy
}
type ReqIntSub struct { type ReqIntSub struct {
id int id int
Interceptor RequestInterceptor Interceptor RequestInterceptor
} }
type RspIntSub struct { type RspIntSub struct {
id int id int
Interceptor ResponseInterceptor Interceptor ResponseInterceptor
} }
type WSIntSub struct { type WSIntSub struct {
id int id int
Interceptor WSInterceptor Interceptor WSInterceptor
} }
func (creds *ProxyCredentials) SerializeHeader() string {
toEncode := []byte(fmt.Sprintf("%s:%s", creds.Username, creds.Password))
encoded := base64.StdEncoding.EncodeToString(toEncode)
return fmt.Sprintf("Basic %s", encoded)
}
func NewInterceptingProxy(logger *log.Logger) *InterceptingProxy { func NewInterceptingProxy(logger *log.Logger) *InterceptingProxy {
var iproxy InterceptingProxy var iproxy InterceptingProxy
iproxy.messageStorage = make(map[int]*savedStorage) iproxy.messageStorage = make(map[int]*savedStorage)
iproxy.slistener = NewProxyListener(logger) iproxy.slistener = NewProxyListener(logger)
iproxy.server = newProxyServer(logger, &iproxy) iproxy.server = newProxyServer(logger, &iproxy)
iproxy.logger = logger iproxy.logger = logger
iproxy.httpHandlers = make(map[string]proxyWebUIHandler)
go func() { go func() {
iproxy.server.Serve(iproxy.slistener) iproxy.server.Serve(iproxy.slistener)
@ -82,7 +102,7 @@ func (iproxy *InterceptingProxy) Close() {
// Will throw errors when the server finally shuts down and tries to call iproxy.slistener.Close a second time // Will throw errors when the server finally shuts down and tries to call iproxy.slistener.Close a second time
iproxy.mtx.Lock() iproxy.mtx.Lock()
defer iproxy.mtx.Unlock() defer iproxy.mtx.Unlock()
iproxy.slistener.Close() iproxy.slistener.Close()
//iproxy.server.Close() // Coming eventually... I hope //iproxy.server.Close() // Coming eventually... I hope
} }
@ -93,7 +113,7 @@ func (iproxy *InterceptingProxy) SetCACertificate(caCert *tls.Certificate) {
iproxy.slistener.SetCACertificate(caCert) iproxy.slistener.SetCACertificate(caCert)
} }
func (iproxy *InterceptingProxy) GetCACertificate() (*tls.Certificate) { func (iproxy *InterceptingProxy) GetCACertificate() *tls.Certificate {
return iproxy.slistener.GetCACertificate() return iproxy.slistener.GetCACertificate()
} }
@ -119,7 +139,7 @@ func (iproxy *InterceptingProxy) GetMessageStorage(id int) (MessageStorage, stri
return savedStorage.storage, savedStorage.description return savedStorage.storage, savedStorage.description
} }
func (iproxy *InterceptingProxy) AddMessageStorage(storage MessageStorage, description string) (int) { func (iproxy *InterceptingProxy) AddMessageStorage(storage MessageStorage, description string) int {
iproxy.mtx.Lock() iproxy.mtx.Lock()
defer iproxy.mtx.Unlock() defer iproxy.mtx.Unlock()
id := getNextStorageId() id := getNextStorageId()
@ -139,8 +159,8 @@ func (iproxy *InterceptingProxy) CloseMessageStorage(id int) {
} }
type SavedStorage struct { type SavedStorage struct {
Id int Id int
Storage MessageStorage Storage MessageStorage
Description string Description string
} }
@ -190,7 +210,7 @@ func (iproxy *InterceptingProxy) LoadScope(storageId int) error {
return nil return nil
} }
func (iproxy *InterceptingProxy) GetScopeChecker() (RequestChecker) { func (iproxy *InterceptingProxy) GetScopeChecker() RequestChecker {
iproxy.mtx.Lock() iproxy.mtx.Lock()
defer iproxy.mtx.Unlock() defer iproxy.mtx.Unlock()
return iproxy.scopeChecker return iproxy.scopeChecker
@ -212,19 +232,19 @@ func (iproxy *InterceptingProxy) SetScopeChecker(checker RequestChecker) error {
return nil return nil
} }
func (iproxy *InterceptingProxy) GetScopeQuery() (MessageQuery) { func (iproxy *InterceptingProxy) GetScopeQuery() MessageQuery {
iproxy.mtx.Lock() iproxy.mtx.Lock()
defer iproxy.mtx.Unlock() defer iproxy.mtx.Unlock()
return iproxy.scopeQuery return iproxy.scopeQuery
} }
func (iproxy *InterceptingProxy) SetScopeQuery(query MessageQuery) (error) { func (iproxy *InterceptingProxy) SetScopeQuery(query MessageQuery) error {
iproxy.mtx.Lock() iproxy.mtx.Lock()
defer iproxy.mtx.Unlock() defer iproxy.mtx.Unlock()
return iproxy.setScopeQuery(query) return iproxy.setScopeQuery(query)
} }
func (iproxy *InterceptingProxy) setScopeQuery(query MessageQuery) (error) { func (iproxy *InterceptingProxy) setScopeQuery(query MessageQuery) error {
checker, err := CheckerFromMessageQuery(query) checker, err := CheckerFromMessageQuery(query)
if err != nil { if err != nil {
return err return err
@ -244,7 +264,48 @@ func (iproxy *InterceptingProxy) setScopeQuery(query MessageQuery) (error) {
return nil return nil
} }
func (iproxy *InterceptingProxy) ClearScope() (error) { func (iproxy *InterceptingProxy) SetNetDial(dialer NetDialer) {
iproxy.mtx.Lock()
defer iproxy.mtx.Unlock()
iproxy.netDial = dialer
}
func (iproxy *InterceptingProxy) NetDial() NetDialer {
iproxy.mtx.Lock()
defer iproxy.mtx.Unlock()
return iproxy.netDial
}
func (iproxy *InterceptingProxy) ClearUpstreamProxy() {
iproxy.mtx.Lock()
defer iproxy.mtx.Unlock()
iproxy.usingProxy = false
iproxy.proxyHost = ""
iproxy.proxyPort = 0
iproxy.proxyIsSOCKS = false
}
func (iproxy *InterceptingProxy) SetUpstreamProxy(proxyHost string, proxyPort int, creds *ProxyCredentials) {
iproxy.mtx.Lock()
defer iproxy.mtx.Unlock()
iproxy.usingProxy = true
iproxy.proxyHost = proxyHost
iproxy.proxyPort = proxyPort
iproxy.proxyIsSOCKS = false
iproxy.proxyCreds = creds
}
func (iproxy *InterceptingProxy) SetUpstreamSOCKSProxy(proxyHost string, proxyPort int, creds *ProxyCredentials) {
iproxy.mtx.Lock()
defer iproxy.mtx.Unlock()
iproxy.usingProxy = true
iproxy.proxyHost = proxyHost
iproxy.proxyPort = proxyPort
iproxy.proxyIsSOCKS = true
iproxy.proxyCreds = creds
}
func (iproxy *InterceptingProxy) ClearScope() error {
iproxy.mtx.Lock() iproxy.mtx.Lock()
defer iproxy.mtx.Unlock() defer iproxy.mtx.Unlock()
iproxy.scopeChecker = nil iproxy.scopeChecker = nil
@ -262,12 +323,42 @@ func (iproxy *InterceptingProxy) ClearScope() (error) {
return nil return nil
} }
func (iproxy *InterceptingProxy) AddReqIntSub(f RequestInterceptor) (*ReqIntSub) { func (iproxy *InterceptingProxy) SubmitRequest(req *ProxyRequest) error {
oldDial := req.NetDial
defer func() { req.NetDial = oldDial }()
req.NetDial = iproxy.NetDial()
if iproxy.usingProxy {
if iproxy.proxyIsSOCKS {
return SubmitRequestSOCKSProxy(req, iproxy.proxyHost, iproxy.proxyPort, iproxy.proxyCreds)
} else {
return SubmitRequestProxy(req, iproxy.proxyHost, iproxy.proxyPort, iproxy.proxyCreds)
}
}
return SubmitRequest(req)
}
func (iproxy *InterceptingProxy) WSDial(req *ProxyRequest) (*WSSession, error) {
oldDial := req.NetDial
defer func() { req.NetDial = oldDial }()
req.NetDial = iproxy.NetDial()
if iproxy.usingProxy {
if iproxy.proxyIsSOCKS {
return WSDialSOCKSProxy(req, iproxy.proxyHost, iproxy.proxyPort, iproxy.proxyCreds)
} else {
return WSDialProxy(req, iproxy.proxyHost, iproxy.proxyPort, iproxy.proxyCreds)
}
}
return WSDial(req)
}
func (iproxy *InterceptingProxy) AddReqIntSub(f RequestInterceptor) *ReqIntSub {
iproxy.mtx.Lock() iproxy.mtx.Lock()
defer iproxy.mtx.Unlock() defer iproxy.mtx.Unlock()
sub := &ReqIntSub{ sub := &ReqIntSub{
id: getNextSubId(), id: getNextSubId(),
Interceptor: f, Interceptor: f,
} }
iproxy.reqSubs = append(iproxy.reqSubs, sub) iproxy.reqSubs = append(iproxy.reqSubs, sub)
@ -286,12 +377,12 @@ func (iproxy *InterceptingProxy) RemoveReqIntSub(sub *ReqIntSub) {
} }
} }
func (iproxy *InterceptingProxy) AddRspIntSub(f ResponseInterceptor) (*RspIntSub) { func (iproxy *InterceptingProxy) AddRspIntSub(f ResponseInterceptor) *RspIntSub {
iproxy.mtx.Lock() iproxy.mtx.Lock()
defer iproxy.mtx.Unlock() defer iproxy.mtx.Unlock()
sub := &RspIntSub{ sub := &RspIntSub{
id: getNextSubId(), id: getNextSubId(),
Interceptor: f, Interceptor: f,
} }
iproxy.rspSubs = append(iproxy.rspSubs, sub) iproxy.rspSubs = append(iproxy.rspSubs, sub)
@ -310,12 +401,12 @@ func (iproxy *InterceptingProxy) RemoveRspIntSub(sub *RspIntSub) {
} }
} }
func (iproxy *InterceptingProxy) AddWSIntSub(f WSInterceptor) (*WSIntSub) { func (iproxy *InterceptingProxy) AddWSIntSub(f WSInterceptor) *WSIntSub {
iproxy.mtx.Lock() iproxy.mtx.Lock()
defer iproxy.mtx.Unlock() defer iproxy.mtx.Unlock()
sub := &WSIntSub{ sub := &WSIntSub{
id: getNextSubId(), id: getNextSubId(),
Interceptor: f, Interceptor: f,
} }
iproxy.wsSubs = append(iproxy.wsSubs, sub) iproxy.wsSubs = append(iproxy.wsSubs, sub)
@ -360,6 +451,28 @@ func (iproxy *InterceptingProxy) GetProxyStorage() MessageStorage {
return savedStorage.storage return savedStorage.storage
} }
func (iproxy *InterceptingProxy) AddHTTPHandler(host string, handler proxyWebUIHandler) {
iproxy.mtx.Lock()
defer iproxy.mtx.Unlock()
iproxy.httpHandlers[host] = handler
}
func (iproxy *InterceptingProxy) GetHTTPHandler(host string) (proxyWebUIHandler, error) {
iproxy.mtx.Lock()
defer iproxy.mtx.Unlock()
handler, ok := iproxy.httpHandlers[host]
if !ok {
return nil, fmt.Errorf("no handler for host %s", host)
}
return handler, nil
}
func (iproxy *InterceptingProxy) RemoveHTTPHandler(host string) {
iproxy.mtx.Lock()
defer iproxy.mtx.Unlock()
delete(iproxy.httpHandlers, host)
}
func ParseProxyRequest(r *http.Request) (*ProxyRequest, error) { func ParseProxyRequest(r *http.Request) (*ProxyRequest, error) {
host, port, useTLS, err := DecodeRemoteAddr(r.RemoteAddr) host, port, useTLS, err := DecodeRemoteAddr(r.RemoteAddr)
if err != nil { if err != nil {
@ -382,14 +495,19 @@ func ErrResponse(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
} }
func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (iproxy *InterceptingProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
handler, err := iproxy.GetHTTPHandler(r.Host)
if err == nil {
handler(w, r, iproxy)
return
}
req, _ := ParseProxyRequest(r) req, _ := ParseProxyRequest(r)
p.Logger.Println("Received request to", req.FullURL().String()) iproxy.logger.Println("Received request to", req.FullURL().String())
req.StripProxyHeaders() req.StripProxyHeaders()
ms := p.IProxy.GetProxyStorage() ms := iproxy.GetProxyStorage()
scopeChecker := p.IProxy.GetScopeChecker() scopeChecker := iproxy.GetScopeChecker()
// Helper functions // Helper functions
checkScope := func(req *ProxyRequest) bool { checkScope := func(req *ProxyRequest) bool {
@ -409,16 +527,16 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
/* /*
functions to mangle messages using the iproxy's manglers functions to mangle messages using the iproxy's manglers
each return the new message, whether it was modified, and an error each return the new message, whether it was modified, and an error
*/ */
mangleRequest := func(req *ProxyRequest) (*ProxyRequest, bool, error) { mangleRequest := func(req *ProxyRequest) (*ProxyRequest, bool, error) {
newReq := req.Clone() newReq := req.Clone()
reqSubs := p.IProxy.getRequestSubs() reqSubs := iproxy.getRequestSubs()
for _, sub := range reqSubs { for _, sub := range reqSubs {
var err error = nil var err error = nil
newReq, err := sub.Interceptor(newReq) newReq, err = sub.Interceptor(newReq)
if err != nil { if err != nil {
e := fmt.Errorf("error with request interceptor: %s", err) e := fmt.Errorf("error with request interceptor: %s", err)
return nil, false, e return nil, false, e
@ -431,7 +549,7 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if newReq != nil { if newReq != nil {
newReq.StartDatetime = time.Now() newReq.StartDatetime = time.Now()
if !req.Eq(newReq) { if !req.Eq(newReq) {
p.Logger.Println("Request modified by interceptor") iproxy.logger.Println("Request modified by interceptor")
return newReq, true, nil return newReq, true, nil
} }
} else { } else {
@ -443,10 +561,10 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
mangleResponse := func(req *ProxyRequest, rsp *ProxyResponse) (*ProxyResponse, bool, error) { mangleResponse := func(req *ProxyRequest, rsp *ProxyResponse) (*ProxyResponse, bool, error) {
reqCopy := req.Clone() reqCopy := req.Clone()
newRsp := rsp.Clone() newRsp := rsp.Clone()
rspSubs := p.IProxy.getResponseSubs() rspSubs := iproxy.getResponseSubs()
p.Logger.Printf("%d interceptors", len(rspSubs)) iproxy.logger.Printf("%d interceptors", len(rspSubs))
for _, sub := range rspSubs { for _, sub := range rspSubs {
p.Logger.Println("mangling rsp...") iproxy.logger.Println("mangling rsp...")
var err error = nil var err error = nil
newRsp, err = sub.Interceptor(reqCopy, newRsp) newRsp, err = sub.Interceptor(reqCopy, newRsp)
if err != nil { if err != nil {
@ -460,7 +578,7 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if newRsp != nil { if newRsp != nil {
if !rsp.Eq(newRsp) { if !rsp.Eq(newRsp) {
p.Logger.Println("Response for", req.FullURL(), "modified by interceptor") iproxy.logger.Println("Response for", req.FullURL(), "modified by interceptor")
// it was mangled // it was mangled
return newRsp, true, nil return newRsp, true, nil
} }
@ -477,7 +595,7 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
newMsg := ws.Clone() newMsg := ws.Clone()
reqCopy := req.Clone() reqCopy := req.Clone()
rspCopy := rsp.Clone() rspCopy := rsp.Clone()
wsSubs := p.IProxy.getWSSubs() wsSubs := iproxy.getWSSubs()
for _, sub := range wsSubs { for _, sub := range wsSubs {
var err error = nil var err error = nil
newMsg, err = sub.Interceptor(reqCopy, rspCopy, newMsg) newMsg, err = sub.Interceptor(reqCopy, rspCopy, newMsg)
@ -494,7 +612,7 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !ws.Eq(newMsg) { if !ws.Eq(newMsg) {
newMsg.Timestamp = time.Now() newMsg.Timestamp = time.Now()
newMsg.Direction = ws.Direction newMsg.Direction = ws.Direction
p.Logger.Println("Message modified by interceptor") iproxy.logger.Println("Message modified by interceptor")
return newMsg, true, nil return newMsg, true, nil
} }
} else { } else {
@ -503,7 +621,6 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return ws, false, nil return ws, false, nil
} }
req.StartDatetime = time.Now() req.StartDatetime = time.Now()
if checkScope(req) { if checkScope(req) {
@ -537,12 +654,12 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
if req.IsWSUpgrade() { if req.IsWSUpgrade() {
p.Logger.Println("Detected websocket request. Upgrading...") iproxy.logger.Println("Detected websocket request. Upgrading...")
rc, err := req.WSDial() rc, err := iproxy.WSDial(req)
if err != nil { if err != nil {
p.Logger.Println("error dialing ws server:", err) iproxy.logger.Println("error dialing ws server:", err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, fmt.Sprintf("error dialing websocket server: %s", err.Error()), http.StatusInternalServerError)
return return
} }
defer rc.Close() defer rc.Close()
@ -560,8 +677,8 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
lc, err := upgrader.Upgrade(w, r, nil) lc, err := upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
p.Logger.Println("error upgrading connection:", err) iproxy.logger.Println("error upgrading connection:", err)
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, fmt.Sprintf("error upgrading connection: %s", err.Error()), http.StatusInternalServerError)
return return
} }
defer lc.Close() defer lc.Close()
@ -581,13 +698,13 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
mtype, msg, err := rc.ReadMessage() mtype, msg, err := rc.ReadMessage()
if err != nil { if err != nil {
lc.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) lc.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
p.Logger.Println("error with receiving server message:", err) iproxy.logger.Println("error with receiving server message:", err)
wg.Done() wg.Done()
return return
} }
pws, err := NewProxyWSMessage(mtype, msg, ToClient) pws, err := NewProxyWSMessage(mtype, msg, ToClient)
if err != nil { if err != nil {
p.Logger.Println("error creating ws object:", err.Error()) iproxy.logger.Println("error creating ws object:", err.Error())
continue continue
} }
pws.Timestamp = time.Now() pws.Timestamp = time.Now()
@ -595,7 +712,7 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if checkScope(req) { if checkScope(req) {
newMsg, mangled, err := mangleWS(req, req.ServerResponse, pws) newMsg, mangled, err := mangleWS(req, req.ServerResponse, pws)
if err != nil { if err != nil {
p.Logger.Println("error mangling ws:", err) iproxy.logger.Println("error mangling ws:", err)
return return
} }
if mangled { if mangled {
@ -611,7 +728,7 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
addWSMessage(req, pws) addWSMessage(req, pws)
if err := saveIfExists(req); err != nil { if err := saveIfExists(req); err != nil {
p.Logger.Println("error saving request:", err) iproxy.logger.Println("error saving request:", err)
continue continue
} }
lc.WriteMessage(pws.Type, pws.Message) lc.WriteMessage(pws.Type, pws.Message)
@ -625,13 +742,13 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
mtype, msg, err := lc.ReadMessage() mtype, msg, err := lc.ReadMessage()
if err != nil { if err != nil {
rc.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) rc.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
p.Logger.Println("error with receiving client message:", err) iproxy.logger.Println("error with receiving client message:", err)
wg.Done() wg.Done()
return return
} }
pws, err := NewProxyWSMessage(mtype, msg, ToServer) pws, err := NewProxyWSMessage(mtype, msg, ToServer)
if err != nil { if err != nil {
p.Logger.Println("error creating ws object:", err.Error()) iproxy.logger.Println("error creating ws object:", err.Error())
continue continue
} }
pws.Timestamp = time.Now() pws.Timestamp = time.Now()
@ -639,7 +756,7 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if checkScope(req) { if checkScope(req) {
newMsg, mangled, err := mangleWS(req, req.ServerResponse, pws) newMsg, mangled, err := mangleWS(req, req.ServerResponse, pws)
if err != nil { if err != nil {
p.Logger.Println("error mangling ws:", err) iproxy.logger.Println("error mangling ws:", err)
return return
} }
if mangled { if mangled {
@ -655,18 +772,18 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
addWSMessage(req, pws) addWSMessage(req, pws)
if err := saveIfExists(req); err != nil { if err := saveIfExists(req); err != nil {
p.Logger.Println("error saving request:", err) iproxy.logger.Println("error saving request:", err)
continue continue
} }
rc.WriteMessage(pws.Type, pws.Message) rc.WriteMessage(pws.Type, pws.Message)
} }
}() }()
wg.Wait() wg.Wait()
p.Logger.Println("Websocket session complete!") iproxy.logger.Println("Websocket session complete!")
} else { } else {
err := req.Submit() err := iproxy.SubmitRequest(req)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, fmt.Sprintf("error submitting request: %s", err.Error()), http.StatusInternalServerError)
return return
} }
req.EndDatetime = time.Now() req.EndDatetime = time.Now()
@ -699,7 +816,7 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
} }
} }
for k, v := range req.ServerResponse.Header { for k, v := range req.ServerResponse.Header {
for _, vv := range v { for _, vv := range v {
w.Header().Add(k, vv) w.Header().Add(k, vv)
@ -713,10 +830,7 @@ func (p proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func newProxyServer(logger *log.Logger, iproxy *InterceptingProxy) *http.Server { func newProxyServer(logger *log.Logger, iproxy *InterceptingProxy) *http.Server {
server := &http.Server{ server := &http.Server{
Handler: proxyHandler{ Handler: iproxy,
Logger: logger,
IProxy: iproxy,
},
ErrorLog: logger, ErrorLog: logger,
} }
return server return server

@ -15,12 +15,13 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
"strings"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/deckarep/golang-set" "github.com/deckarep/golang-set"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"golang.org/x/net/proxy"
) )
const ( const (
@ -28,10 +29,12 @@ const (
ToClient ToClient
) )
type NetDialer func(network, addr string) (net.Conn, error)
type ProxyResponse struct { type ProxyResponse struct {
http.Response http.Response
bodyBytes []byte bodyBytes []byte
DbId string // ID used by storage implementation. Blank string = unsaved DbId string // ID used by storage implementation. Blank string = unsaved
Unmangled *ProxyResponse Unmangled *ProxyResponse
} }
@ -49,32 +52,47 @@ type ProxyRequest struct {
Unmangled *ProxyRequest Unmangled *ProxyRequest
// Additional data // Additional data
bodyBytes []byte bodyBytes []byte
DbId string // ID used by storage implementation. Blank string = unsaved DbId string // ID used by storage implementation. Blank string = unsaved
StartDatetime time.Time StartDatetime time.Time
EndDatetime time.Time EndDatetime time.Time
tags mapset.Set tags mapset.Set
NetDial NetDialer
} }
type WSSession struct { type WSSession struct {
websocket.Conn websocket.Conn
Request *ProxyRequest // Request used for handshake Request *ProxyRequest // Request used for handshake
} }
type ProxyWSMessage struct { type ProxyWSMessage struct {
Type int Type int
Message []byte Message []byte
Direction int Direction int
Unmangled *ProxyWSMessage Unmangled *ProxyWSMessage
Timestamp time.Time Timestamp time.Time
Request *ProxyRequest Request *ProxyRequest
DbId string // ID used by storage implementation. Blank string = unsaved DbId string // ID used by storage implementation. Blank string = unsaved
} }
func NewProxyRequest(r *http.Request, destHost string, destPort int, destUseTLS bool) (*ProxyRequest) { func PerformConnect(conn net.Conn, destHost string, destPort int) error {
connStr := []byte(fmt.Sprintf("CONNECT %s:%d HTTP/1.1\r\nHost: %s\r\nProxy-Connection: Keep-Alive\r\n\r\n", destHost, destPort, destHost))
conn.Write(connStr)
rsp, err := http.ReadResponse(bufio.NewReader(conn), nil)
if err != nil {
return fmt.Errorf("error performing CONNECT handshake: %s", err.Error())
}
if rsp.StatusCode != 200 {
return fmt.Errorf("error performing CONNECT handshake")
}
return nil
}
func NewProxyRequest(r *http.Request, destHost string, destPort int, destUseTLS bool) *ProxyRequest {
var retReq *ProxyRequest var retReq *ProxyRequest
if r != nil { if r != nil {
// Write/reread the request to make sure we get all the extra headers Go adds into req.Header // Write/reread the request to make sure we get all the extra headers Go adds into req.Header
@ -98,6 +116,7 @@ func NewProxyRequest(r *http.Request, destHost string, destPort int, destUseTLS
time.Unix(0, 0), time.Unix(0, 0),
time.Unix(0, 0), time.Unix(0, 0),
mapset.NewSet(), mapset.NewSet(),
nil,
} }
} else { } else {
newReq, _ := http.NewRequest("GET", "/", nil) // Ignore error since this should be run the same every time and shouldn't error newReq, _ := http.NewRequest("GET", "/", nil) // Ignore error since this should be run the same every time and shouldn't error
@ -116,6 +135,7 @@ func NewProxyRequest(r *http.Request, destHost string, destPort int, destUseTLS
time.Unix(0, 0), time.Unix(0, 0),
time.Unix(0, 0), time.Unix(0, 0),
mapset.NewSet(), mapset.NewSet(),
nil,
} }
} }
@ -125,7 +145,7 @@ func NewProxyRequest(r *http.Request, destHost string, destPort int, destUseTLS
return retReq return retReq
} }
func ProxyRequestFromBytes(b []byte, destHost string, destPort int, destUseTLS bool) (*ProxyRequest, error) { func ProxyRequestFromBytes(b []byte, destHost string, destPort int, destUseTLS bool) (*ProxyRequest, error) {
buf := bytes.NewBuffer(b) buf := bytes.NewBuffer(b)
httpReq, err := http.ReadRequest(bufio.NewReader(buf)) httpReq, err := http.ReadRequest(bufio.NewReader(buf))
if err != nil { if err != nil {
@ -135,7 +155,7 @@ func ProxyRequestFromBytes(b []byte, destHost string, destPort int, destUseTLS
return NewProxyRequest(httpReq, destHost, destPort, destUseTLS), nil return NewProxyRequest(httpReq, destHost, destPort, destUseTLS), nil
} }
func NewProxyResponse(r *http.Response) (*ProxyResponse) { func NewProxyResponse(r *http.Response) *ProxyResponse {
// Write/reread the request to make sure we get all the extra headers Go adds into req.Header // Write/reread the request to make sure we get all the extra headers Go adds into req.Header
oldClose := r.Close oldClose := r.Close
r.Close = false r.Close = false
@ -170,12 +190,12 @@ func ProxyResponseFromBytes(b []byte) (*ProxyResponse, error) {
func NewProxyWSMessage(mtype int, message []byte, direction int) (*ProxyWSMessage, error) { func NewProxyWSMessage(mtype int, message []byte, direction int) (*ProxyWSMessage, error) {
return &ProxyWSMessage{ return &ProxyWSMessage{
Type: mtype, Type: mtype,
Message: message, Message: message,
Direction: direction, Direction: direction,
Unmangled: nil, Unmangled: nil,
Timestamp: time.Unix(0, 0), Timestamp: time.Unix(0, 0),
DbId: "", DbId: "",
}, nil }, nil
} }
@ -197,7 +217,7 @@ func (req *ProxyRequest) DestScheme() string {
func (req *ProxyRequest) FullURL() *url.URL { func (req *ProxyRequest) FullURL() *url.URL {
// Same as req.URL but guarantees it will include the scheme, host, and port if necessary // Same as req.URL but guarantees it will include the scheme, host, and port if necessary
var u url.URL var u url.URL
u = *(req.URL) // Copy the original req.URL u = *(req.URL) // Copy the original req.URL
u.Host = req.Host u.Host = req.Host
@ -207,7 +227,7 @@ func (req *ProxyRequest) FullURL() *url.URL {
func (req *ProxyRequest) DestURL() *url.URL { func (req *ProxyRequest) DestURL() *url.URL {
// Same as req.FullURL() but uses DestHost and DestPort for the host and port // Same as req.FullURL() but uses DestHost and DestPort for the host and port
var u url.URL var u url.URL
u = *(req.URL) // Copy the original req.URL u = *(req.URL) // Copy the original req.URL
u.Scheme = req.DestScheme() u.Scheme = req.DestScheme()
@ -221,33 +241,38 @@ func (req *ProxyRequest) DestURL() *url.URL {
return &u return &u
} }
func (req *ProxyRequest) Submit() error { func (req *ProxyRequest) Submit(conn net.Conn) error {
// Connect to the remote server return req.submit(conn, false, nil)
var conn net.Conn }
var err error
dest := fmt.Sprintf("%s:%d", req.DestHost, req.DestPort) func (req *ProxyRequest) SubmitProxy(conn net.Conn, creds *ProxyCredentials) error {
if req.DestUseTLS { return req.submit(conn, true, creds)
// Use TLS }
conn, err = tls.Dial("tcp", dest, nil)
if err != nil { func (req *ProxyRequest) submit(conn net.Conn, forProxy bool, proxyCreds *ProxyCredentials) error {
// Write the request to the connection
req.StartDatetime = time.Now()
if forProxy {
if req.DestUseTLS {
req.URL.Scheme = "https"
} else {
req.URL.Scheme = "http"
}
req.URL.Opaque = ""
if err := req.RepeatableProxyWrite(conn, proxyCreds); err != nil {
return err return err
} }
} else { } else {
// Use plaintext if err := req.RepeatableWrite(conn); err != nil {
conn, err = net.Dial("tcp", dest)
if err != nil {
return err return err
} }
} }
// Write the request to the connection
req.StartDatetime = time.Now()
req.RepeatableWrite(conn)
// Read a response from the server // Read a response from the server
httpRsp, err := http.ReadResponse(bufio.NewReader(conn), nil) httpRsp, err := http.ReadResponse(bufio.NewReader(conn), nil)
if err != nil { if err != nil {
return err return fmt.Errorf("error reading response: %s", err.Error())
} }
req.EndDatetime = time.Now() req.EndDatetime = time.Now()
@ -256,7 +281,7 @@ func (req *ProxyRequest) Submit() error {
return nil return nil
} }
func (req *ProxyRequest) WSDial() (*WSSession, error) { func (req *ProxyRequest) WSDial(conn net.Conn) (*WSSession, error) {
if !req.IsWSUpgrade() { if !req.IsWSUpgrade() {
return nil, fmt.Errorf("could not start websocket session: request is not a websocket handshake request") return nil, fmt.Errorf("could not start websocket session: request is not a websocket handshake request")
} }
@ -276,18 +301,91 @@ func (req *ProxyRequest) WSDial() (*WSSession, error) {
} }
dialer := &websocket.Dialer{} dialer := &websocket.Dialer{}
conn, rsp, err := dialer.Dial(req.DestURL().String(), upgradeHeaders) dialer.NetDial = func(network, address string) (net.Conn, error) {
return conn, nil
}
wsconn, rsp, err := dialer.Dial(req.DestURL().String(), upgradeHeaders)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not dial WebSocket server: %s", err) return nil, fmt.Errorf("could not dial WebSocket server: %s", err)
} }
req.ServerResponse = NewProxyResponse(rsp) req.ServerResponse = NewProxyResponse(rsp)
wsession := &WSSession{ wsession := &WSSession{
*conn, *wsconn,
req, req,
} }
return wsession, nil return wsession, nil
} }
func WSDial(req *ProxyRequest) (*WSSession, error) {
return wsDial(req, false, "", 0, nil, false)
}
func WSDialProxy(req *ProxyRequest, proxyHost string, proxyPort int, creds *ProxyCredentials) (*WSSession, error) {
return wsDial(req, true, proxyHost, proxyPort, creds, false)
}
func WSDialSOCKSProxy(req *ProxyRequest, proxyHost string, proxyPort int, creds *ProxyCredentials) (*WSSession, error) {
return wsDial(req, true, proxyHost, proxyPort, creds, true)
}
func wsDial(req *ProxyRequest, useProxy bool, proxyHost string, proxyPort int, proxyCreds *ProxyCredentials, proxyIsSOCKS bool) (*WSSession, error) {
var conn net.Conn
var dialer NetDialer
var err error
if req.NetDial != nil {
dialer = req.NetDial
} else {
dialer = net.Dial
}
if useProxy {
if proxyIsSOCKS {
var socksCreds *proxy.Auth
if proxyCreds != nil {
socksCreds = &proxy.Auth{
User: proxyCreds.Username,
Password: proxyCreds.Password,
}
}
socksDialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort), socksCreds, proxy.Direct)
if err != nil {
return nil, fmt.Errorf("error creating SOCKS dialer: %s", err.Error())
}
conn, err = socksDialer.Dial("tcp", fmt.Sprintf("%s:%d", req.DestHost, req.DestPort))
if err != nil {
return nil, fmt.Errorf("error dialing host: %s", err.Error())
}
defer conn.Close()
} else {
conn, err = dialer("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort))
if err != nil {
return nil, fmt.Errorf("error dialing proxy: %s", err.Error())
}
// always perform a CONNECT for websocket regardless of SSL
if err := PerformConnect(conn, req.DestHost, req.DestPort); err != nil {
return nil, err
}
}
} else {
conn, err = dialer("tcp", fmt.Sprintf("%s:%d", req.DestHost, req.DestPort))
if err != nil {
return nil, fmt.Errorf("error dialing host: %s", err.Error())
}
}
if req.DestUseTLS {
tls_conn := tls.Client(conn, &tls.Config{
InsecureSkipVerify: true,
})
conn = tls_conn
}
return req.WSDial(conn)
}
func (req *ProxyRequest) IsWSUpgrade() bool { func (req *ProxyRequest) IsWSUpgrade() bool {
for k, v := range req.Header { for k, v := range req.Header {
for _, vv := range v { for _, vv := range v {
@ -322,7 +420,7 @@ func (req *ProxyRequest) Eq(other *ProxyRequest) bool {
return true return true
} }
func (req *ProxyRequest) Clone() (*ProxyRequest) { func (req *ProxyRequest) Clone() *ProxyRequest {
buf := bytes.NewBuffer(make([]byte, 0)) buf := bytes.NewBuffer(make([]byte, 0))
req.RepeatableWrite(buf) req.RepeatableWrite(buf)
newReq, err := ProxyRequestFromBytes(buf.Bytes(), req.DestHost, req.DestPort, req.DestUseTLS) newReq, err := ProxyRequestFromBytes(buf.Bytes(), req.DestHost, req.DestPort, req.DestUseTLS)
@ -336,7 +434,7 @@ func (req *ProxyRequest) Clone() (*ProxyRequest) {
return newReq return newReq
} }
func (req *ProxyRequest) DeepClone() (*ProxyRequest) { func (req *ProxyRequest) DeepClone() *ProxyRequest {
// Returns a request with the same request, response, and associated websocket messages // Returns a request with the same request, response, and associated websocket messages
newReq := req.Clone() newReq := req.Clone()
newReq.DbId = req.DbId newReq.DbId = req.DbId
@ -344,7 +442,7 @@ func (req *ProxyRequest) DeepClone() (*ProxyRequest) {
if req.Unmangled != nil { if req.Unmangled != nil {
newReq.Unmangled = req.Unmangled.DeepClone() newReq.Unmangled = req.Unmangled.DeepClone()
} }
if req.ServerResponse != nil { if req.ServerResponse != nil {
newReq.ServerResponse = req.ServerResponse.DeepClone() newReq.ServerResponse = req.ServerResponse.DeepClone()
} }
@ -361,9 +459,19 @@ func (req *ProxyRequest) resetBodyReader() {
req.Body = ioutil.NopCloser(bytes.NewBuffer(req.BodyBytes())) req.Body = ioutil.NopCloser(bytes.NewBuffer(req.BodyBytes()))
} }
func (req *ProxyRequest) RepeatableWrite(w io.Writer) { func (req *ProxyRequest) RepeatableWrite(w io.Writer) error {
req.Write(w) defer req.resetBodyReader()
req.resetBodyReader() return req.Write(w)
}
func (req *ProxyRequest) RepeatableProxyWrite(w io.Writer, proxyCreds *ProxyCredentials) error {
defer req.resetBodyReader()
if proxyCreds != nil {
authHeader := proxyCreds.SerializeHeader()
req.Header.Set("Proxy-Authorization", authHeader)
defer func() { req.Header.Del("Proxy-Authorization") }()
}
return req.WriteProxy(w)
} }
func (req *ProxyRequest) BodyBytes() []byte { func (req *ProxyRequest) BodyBytes() []byte {
@ -376,7 +484,7 @@ func (req *ProxyRequest) SetBodyBytes(bs []byte) {
req.resetBodyReader() req.resetBodyReader()
// Parse the form if we can, ignore errors // Parse the form if we can, ignore errors
req.ParseMultipartForm(1024*1024*1024) // 1GB for no good reason req.ParseMultipartForm(1024 * 1024 * 1024) // 1GB for no good reason
req.ParseForm() req.ParseForm()
req.resetBodyReader() req.resetBodyReader()
req.Header.Set("Content-Length", strconv.Itoa(len(bs))) req.Header.Set("Content-Length", strconv.Itoa(len(bs)))
@ -418,7 +526,7 @@ func (req *ProxyRequest) SetURLParameter(key string, value string) {
req.ParseForm() req.ParseForm()
} }
func (req *ProxyRequest) URLParameters() (url.Values) { func (req *ProxyRequest) URLParameters() url.Values {
vals := req.URL.Query() vals := req.URL.Query()
return vals return vals
} }
@ -479,7 +587,7 @@ func (req *ProxyRequest) StatusLine() string {
return fmt.Sprintf("%s %s %s", req.Method, req.HTTPPath(), req.Proto) return fmt.Sprintf("%s %s %s", req.Method, req.HTTPPath(), req.Proto)
} }
func (req *ProxyRequest) HeaderSection() (string) { func (req *ProxyRequest) HeaderSection() string {
retStr := req.StatusLine() retStr := req.StatusLine()
retStr += "\r\n" retStr += "\r\n"
for k, vs := range req.Header { for k, vs := range req.Header {
@ -495,9 +603,9 @@ func (rsp *ProxyResponse) resetBodyReader() {
rsp.Body = ioutil.NopCloser(bytes.NewBuffer(rsp.BodyBytes())) rsp.Body = ioutil.NopCloser(bytes.NewBuffer(rsp.BodyBytes()))
} }
func (rsp *ProxyResponse) RepeatableWrite(w io.Writer) { func (rsp *ProxyResponse) RepeatableWrite(w io.Writer) error {
rsp.Write(w) defer rsp.resetBodyReader()
rsp.resetBodyReader() return rsp.Write(w)
} }
func (rsp *ProxyResponse) BodyBytes() []byte { func (rsp *ProxyResponse) BodyBytes() []byte {
@ -510,7 +618,7 @@ func (rsp *ProxyResponse) SetBodyBytes(bs []byte) {
rsp.Header.Set("Content-Length", strconv.Itoa(len(bs))) rsp.Header.Set("Content-Length", strconv.Itoa(len(bs)))
} }
func (rsp *ProxyResponse) Clone() (*ProxyResponse) { func (rsp *ProxyResponse) Clone() *ProxyResponse {
buf := bytes.NewBuffer(make([]byte, 0)) buf := bytes.NewBuffer(make([]byte, 0))
rsp.RepeatableWrite(buf) rsp.RepeatableWrite(buf)
newRsp, err := ProxyResponseFromBytes(buf.Bytes()) newRsp, err := ProxyResponseFromBytes(buf.Bytes())
@ -520,7 +628,7 @@ func (rsp *ProxyResponse) Clone() (*ProxyResponse) {
return newRsp return newRsp
} }
func (rsp *ProxyResponse) DeepClone() (*ProxyResponse) { func (rsp *ProxyResponse) DeepClone() *ProxyResponse {
newRsp := rsp.Clone() newRsp := rsp.Clone()
newRsp.DbId = rsp.DbId newRsp.DbId = rsp.DbId
if rsp.Unmangled != nil { if rsp.Unmangled != nil {
@ -565,7 +673,7 @@ func (rsp *ProxyResponse) StatusLine() string {
return fmt.Sprintf("HTTP/%d.%d %03d %s", rsp.ProtoMajor, rsp.ProtoMinor, rsp.StatusCode, rsp.HTTPStatus()) return fmt.Sprintf("HTTP/%d.%d %03d %s", rsp.ProtoMajor, rsp.ProtoMinor, rsp.StatusCode, rsp.HTTPStatus())
} }
func (rsp *ProxyResponse) HeaderSection() (string) { func (rsp *ProxyResponse) HeaderSection() string {
retStr := rsp.StatusLine() retStr := rsp.StatusLine()
retStr += "\r\n" retStr += "\r\n"
for k, vs := range rsp.Header { for k, vs := range rsp.Header {
@ -585,7 +693,7 @@ func (msg *ProxyWSMessage) String() string {
return fmt.Sprintf("{WS Message msg=\"%s\", type=%d, dir=%s}", string(msg.Message), msg.Type, dirStr) return fmt.Sprintf("{WS Message msg=\"%s\", type=%d, dir=%s}", string(msg.Message), msg.Type, dirStr)
} }
func (msg *ProxyWSMessage) Clone() (*ProxyWSMessage) { func (msg *ProxyWSMessage) Clone() *ProxyWSMessage {
var retMsg ProxyWSMessage var retMsg ProxyWSMessage
retMsg.Type = msg.Type retMsg.Type = msg.Type
retMsg.Message = msg.Message retMsg.Message = msg.Message
@ -595,7 +703,7 @@ func (msg *ProxyWSMessage) Clone() (*ProxyWSMessage) {
return &retMsg return &retMsg
} }
func (msg *ProxyWSMessage) DeepClone() (*ProxyWSMessage) { func (msg *ProxyWSMessage) DeepClone() *ProxyWSMessage {
retMsg := msg.Clone() retMsg := msg.Clone()
retMsg.DbId = msg.DbId retMsg.DbId = msg.DbId
if msg.Unmangled != nil { if msg.Unmangled != nil {
@ -613,7 +721,7 @@ func (msg *ProxyWSMessage) Eq(other *ProxyWSMessage) bool {
return true return true
} }
func CopyHeader(hd http.Header) (http.Header) { func CopyHeader(hd http.Header) http.Header {
var ret http.Header = make(http.Header) var ret http.Header = make(http.Header)
for k, vs := range hd { for k, vs := range hd {
for _, v := range vs { for _, v := range vs {
@ -622,3 +730,80 @@ func CopyHeader(hd http.Header) (http.Header) {
} }
return ret return ret
} }
func submitRequest(req *ProxyRequest, useProxy bool, proxyHost string,
proxyPort int, proxyCreds *ProxyCredentials, proxyIsSOCKS bool) error {
var dialer NetDialer = req.NetDial
if dialer == nil {
dialer = net.Dial
}
var conn net.Conn
var err error
var proxyFormat bool = false
if useProxy {
if proxyIsSOCKS {
var socksCreds *proxy.Auth
if proxyCreds != nil {
socksCreds = &proxy.Auth{
User: proxyCreds.Username,
Password: proxyCreds.Password,
}
}
socksDialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort), socksCreds, proxy.Direct)
if err != nil {
return fmt.Errorf("error creating SOCKS dialer: %s", err.Error())
}
conn, err = socksDialer.Dial("tcp", fmt.Sprintf("%s:%d", req.DestHost, req.DestPort))
if err != nil {
return fmt.Errorf("error dialing host: %s", err.Error())
}
defer conn.Close()
} else {
conn, err = dialer("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort))
if err != nil {
return fmt.Errorf("error dialing proxy: %s", err.Error())
}
defer conn.Close()
if req.DestUseTLS {
if err := PerformConnect(conn, req.DestHost, req.DestPort); err != nil {
return err
}
proxyFormat = false
} else {
proxyFormat = true
}
}
} else {
conn, err = dialer("tcp", fmt.Sprintf("%s:%d", req.DestHost, req.DestPort))
if err != nil {
return fmt.Errorf("error dialing host: %s", err.Error())
}
defer conn.Close()
}
if req.DestUseTLS {
tls_conn := tls.Client(conn, &tls.Config{
InsecureSkipVerify: true,
})
conn = tls_conn
}
if proxyFormat {
return req.SubmitProxy(conn, proxyCreds)
} else {
return req.Submit(conn)
}
}
func SubmitRequest(req *ProxyRequest) error {
return submitRequest(req, false, "", 0, nil, false)
}
func SubmitRequestProxy(req *ProxyRequest, proxyHost string, proxyPort int, creds *ProxyCredentials) error {
return submitRequest(req, true, proxyHost, proxyPort, creds, false)
}
func SubmitRequestSOCKSProxy(req *ProxyRequest, proxyHost string, proxyPort int, creds *ProxyCredentials) error {
return submitRequest(req, true, proxyHost, proxyPort, creds, true)
}

@ -4,7 +4,6 @@ import (
"net/url" "net/url"
"runtime" "runtime"
"testing" "testing"
// "bytes" // "bytes"
// "net/http" // "net/http"
// "bufio" // "bufio"
@ -80,13 +79,13 @@ func TestEq(t *testing.T) {
} }
req1.Header = map[string][]string{ req1.Header = map[string][]string{
"Foo": []string{"Bar", "Baz"}, "Foo": []string{"Bar", "Baz"},
"Foo2": []string{"Bar2", "Baz2"}, "Foo2": []string{"Bar2", "Baz2"},
"Cookie": []string{"cookie=cocks"}, "Cookie": []string{"cookie=cocks"},
} }
req2.Header = map[string][]string{ req2.Header = map[string][]string{
"Foo": []string{"Bar", "Baz"}, "Foo": []string{"Bar", "Baz"},
"Foo2": []string{"Bar2", "Baz2"}, "Foo2": []string{"Bar2", "Baz2"},
"Cookie": []string{"cookie=cocks"}, "Cookie": []string{"cookie=cocks"},
} }
@ -95,8 +94,8 @@ func TestEq(t *testing.T) {
} }
req2.Header = map[string][]string{ req2.Header = map[string][]string{
"Foo": []string{"Baz", "Bar"}, "Foo": []string{"Baz", "Bar"},
"Foo2": []string{"Bar2", "Baz2"}, "Foo2": []string{"Bar2", "Baz2"},
"Cookie": []string{"cookie=cocks"}, "Cookie": []string{"cookie=cocks"},
} }
if req1.Eq(req2) { if req1.Eq(req2) {
@ -104,8 +103,8 @@ func TestEq(t *testing.T) {
} }
req2.Header = map[string][]string{ req2.Header = map[string][]string{
"Foo": []string{"Bar", "Baz"}, "Foo": []string{"Bar", "Baz"},
"Foo2": []string{"Bar2", "Baz2"}, "Foo2": []string{"Bar2", "Baz2"},
"Cookie": []string{"cookiee=cocks"}, "Cookie": []string{"cookiee=cocks"},
} }
if req1.Eq(req2) { if req1.Eq(req2) {

@ -54,7 +54,7 @@ type ProxyConn interface {
net.Conn net.Conn
Id() int Id() int
Logger() (*log.Logger) Logger() *log.Logger
SetCACertificate(*tls.Certificate) SetCACertificate(*tls.Certificate)
StartMaybeTLS(hostname string) (bool, error) StartMaybeTLS(hostname string) (bool, error)
@ -67,12 +67,12 @@ type proxyAddr struct {
} }
type proxyConn struct { type proxyConn struct {
Addr *proxyAddr Addr *proxyAddr
logger *log.Logger logger *log.Logger
id int id int
conn net.Conn // Wrapped connection conn net.Conn // Wrapped connection
readReq *http.Request // A replaced request readReq *http.Request // A replaced request
caCert *tls.Certificate caCert *tls.Certificate
} }
// ProxyAddr implementations/functions // ProxyAddr implementations/functions
@ -123,7 +123,6 @@ func (a *proxyAddr) String() string {
return EncodeRemoteAddr(a.Host, a.Port, a.UseTLS) return EncodeRemoteAddr(a.Host, a.Port, a.UseTLS)
} }
//// bufferedConn and wrappers //// bufferedConn and wrappers
type bufferedConn struct { type bufferedConn struct {
reader *bufio.Reader reader *bufio.Reader
@ -227,7 +226,7 @@ func (pconn *proxyConn) StartMaybeTLS(hostname string) (bool, error) {
config := &tls.Config{ config := &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
Certificates: []tls.Certificate{cert}, Certificates: []tls.Certificate{cert},
} }
tlsConn := tls.Server(bufConn, config) tlsConn := tls.Server(bufConn, config)
pconn.conn = tlsConn pconn.conn = tlsConn
@ -239,8 +238,8 @@ func (pconn *proxyConn) StartMaybeTLS(hostname string) (bool, error) {
} }
func NewProxyConn(c net.Conn, l *log.Logger) *proxyConn { func NewProxyConn(c net.Conn, l *log.Logger) *proxyConn {
a := proxyAddr{Host:"", Port:-1, UseTLS:false} a := proxyAddr{Host: "", Port: -1, UseTLS: false}
p := proxyConn{Addr:&a, logger:l, conn:c, readReq:nil} p := proxyConn{Addr: &a, logger: l, conn: c, readReq: nil}
p.id = getNextConnId() p.id = getNextConnId()
return &p return &p
} }
@ -262,15 +261,15 @@ type ProxyListener struct {
State int State int
inputListeners mapset.Set inputListeners mapset.Set
mtx sync.Mutex mtx sync.Mutex
logger *log.Logger logger *log.Logger
outputConns chan ProxyConn outputConns chan ProxyConn
inputConns chan net.Conn inputConns chan net.Conn
outputConnDone chan struct{} outputConnDone chan struct{}
inputConnDone chan struct{} inputConnDone chan struct{}
listenWg sync.WaitGroup listenWg sync.WaitGroup
caCert *tls.Certificate caCert *tls.Certificate
} }
type listenerData struct { type listenerData struct {
@ -278,7 +277,7 @@ type listenerData struct {
Listener net.Listener Listener net.Listener
} }
func newListenerData(listener net.Listener) (*listenerData) { func newListenerData(listener net.Listener) *listenerData {
l := listenerData{} l := listenerData{}
l.Id = getNextListenerId() l.Id = getNextListenerId()
l.Listener = listener l.Listener = listener
@ -317,7 +316,7 @@ func NewProxyListener(logger *log.Logger) *ProxyListener {
l.State = ProxyRunning l.State = ProxyRunning
l.logger.Println("Proxy Started") l.logger.Println("Proxy Started")
return &l return &l
} }
@ -342,7 +341,7 @@ func (listener *ProxyListener) Accept() (net.Conn, error) {
func (listener *ProxyListener) Close() error { func (listener *ProxyListener) Close() error {
listener.mtx.Lock() listener.mtx.Lock()
defer listener.mtx.Unlock() defer listener.mtx.Unlock()
listener.logger.Println("Closing ProxyListener...") listener.logger.Println("Closing ProxyListener...")
listener.State = ProxyStopped listener.State = ProxyStopped
close(listener.outputConnDone) close(listener.outputConnDone)
@ -369,10 +368,10 @@ func (listener *ProxyListener) Addr() net.Addr {
func (listener *ProxyListener) AddListener(inlisten net.Listener) error { func (listener *ProxyListener) AddListener(inlisten net.Listener) error {
listener.mtx.Lock() listener.mtx.Lock()
defer listener.mtx.Unlock() defer listener.mtx.Unlock()
listener.logger.Println("Adding listener to ProxyListener:", inlisten) listener.logger.Println("Adding listener to ProxyListener:", inlisten)
il := newListenerData(inlisten) il := newListenerData(inlisten)
l := listener l := listener
listener.listenWg.Add(1) listener.listenWg.Add(1)
go func() { go func() {
defer l.listenWg.Done() defer l.listenWg.Done()

@ -1,9 +1,9 @@
package main package main
import ( import (
"crypto/tls"
"bufio" "bufio"
"bytes" "bytes"
"crypto/tls"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
@ -52,53 +52,54 @@ func NewProxyMessageListener(logger *log.Logger, iproxy *InterceptingProxy) *Mes
l.AddHandler("closestorage", closeStorageHandler) l.AddHandler("closestorage", closeStorageHandler)
l.AddHandler("setproxystorage", setProxyStorageHandler) l.AddHandler("setproxystorage", setProxyStorageHandler)
l.AddHandler("liststorage", listProxyStorageHandler) l.AddHandler("liststorage", listProxyStorageHandler)
l.AddHandler("setproxy", setProxyHandler)
return l return l
} }
// Message input structs // Message input structs
type RequestJSON struct { type RequestJSON struct {
DestHost string DestHost string
DestPort int DestPort int
UseTLS bool UseTLS bool
Method string Method string
Path string Path string
ProtoMajor int ProtoMajor int
ProtoMinor int ProtoMinor int
Headers map[string][]string Headers map[string][]string
Body string Body string
Tags []string Tags []string
StartTime int64 `json:"StartTime,omitempty"` StartTime int64 `json:"StartTime,omitempty"`
EndTime int64 `json:"EndTime,omitempty"` EndTime int64 `json:"EndTime,omitempty"`
Unmangled *RequestJSON `json:"Unmangled,omitempty"` Unmangled *RequestJSON `json:"Unmangled,omitempty"`
Response *ResponseJSON `json:"Response,omitempty"` Response *ResponseJSON `json:"Response,omitempty"`
WSMessages []*WSMessageJSON `json:"WSMessages,omitempty"` WSMessages []*WSMessageJSON `json:"WSMessages,omitempty"`
DbId string `json:"DbId,omitempty"` DbId string `json:"DbId,omitempty"`
} }
type ResponseJSON struct { type ResponseJSON struct {
ProtoMajor int ProtoMajor int
ProtoMinor int ProtoMinor int
StatusCode int StatusCode int
Reason string Reason string
Headers map[string][]string Headers map[string][]string
Body string Body string
Unmangled *ResponseJSON `json:"Unmangled,omitempty"` Unmangled *ResponseJSON `json:"Unmangled,omitempty"`
DbId string DbId string
} }
type WSMessageJSON struct { type WSMessageJSON struct {
Message string Message string
IsBinary bool IsBinary bool
ToServer bool ToServer bool
Timestamp int64 Timestamp int64
Unmangled *WSMessageJSON `json:"Unmangled,omitempty"` Unmangled *WSMessageJSON `json:"Unmangled,omitempty"`
DbId string DbId string
} }
func (reqd *RequestJSON) Validate() error { func (reqd *RequestJSON) Validate() error {
@ -162,7 +163,7 @@ func (reqd *RequestJSON) Parse() (*ProxyRequest, error) {
req.EndDatetime = time.Unix(0, reqd.EndTime) req.EndDatetime = time.Unix(0, reqd.EndTime)
} }
for _, tag := range(reqd.Tags) { for _, tag := range reqd.Tags {
req.AddTag(tag) req.AddTag(tag)
} }
@ -186,7 +187,7 @@ func (reqd *RequestJSON) Parse() (*ProxyRequest, error) {
return req, nil return req, nil
} }
func NewRequestJSON(req *ProxyRequest, headersOnly bool) (*RequestJSON) { func NewRequestJSON(req *ProxyRequest, headersOnly bool) *RequestJSON {
newHeaders := make(map[string][]string) newHeaders := make(map[string][]string)
for k, vs := range req.Header { for k, vs := range req.Header {
@ -217,23 +218,23 @@ func NewRequestJSON(req *ProxyRequest, headersOnly bool) (*RequestJSON) {
} }
ret := &RequestJSON{ ret := &RequestJSON{
DestHost: req.DestHost, DestHost: req.DestHost,
DestPort: req.DestPort, DestPort: req.DestPort,
UseTLS: req.DestUseTLS, UseTLS: req.DestUseTLS,
Method: req.Method, Method: req.Method,
Path: req.HTTPPath(), Path: req.HTTPPath(),
ProtoMajor: req.ProtoMajor, ProtoMajor: req.ProtoMajor,
ProtoMinor: req.ProtoMinor, ProtoMinor: req.ProtoMinor,
Headers: newHeaders, Headers: newHeaders,
Tags: req.Tags(), Tags: req.Tags(),
StartTime: req.StartDatetime.UnixNano(), StartTime: req.StartDatetime.UnixNano(),
EndTime: req.EndDatetime.UnixNano(), EndTime: req.EndDatetime.UnixNano(),
Unmangled: unmangled, Unmangled: unmangled,
Response: rsp, Response: rsp,
WSMessages: wsms, WSMessages: wsms,
DbId: req.DbId, DbId: req.DbId,
} }
if !headersOnly { if !headersOnly {
ret.Body = base64.StdEncoding.EncodeToString(req.BodyBytes()) ret.Body = base64.StdEncoding.EncodeToString(req.BodyBytes())
@ -294,7 +295,7 @@ func (rspd *ResponseJSON) Parse() (*ProxyResponse, error) {
return rsp, nil return rsp, nil
} }
func NewResponseJSON(rsp *ProxyResponse, headersOnly bool) (*ResponseJSON) { func NewResponseJSON(rsp *ProxyResponse, headersOnly bool) *ResponseJSON {
newHeaders := make(map[string][]string) newHeaders := make(map[string][]string)
for k, vs := range rsp.Header { for k, vs := range rsp.Header {
for _, v := range vs { for _, v := range vs {
@ -317,10 +318,10 @@ func NewResponseJSON(rsp *ProxyResponse, headersOnly bool) (*ResponseJSON) {
ProtoMajor: rsp.ProtoMajor, ProtoMajor: rsp.ProtoMajor,
ProtoMinor: rsp.ProtoMinor, ProtoMinor: rsp.ProtoMinor,
StatusCode: rsp.StatusCode, StatusCode: rsp.StatusCode,
Reason: rsp.HTTPStatus(), Reason: rsp.HTTPStatus(),
Headers: newHeaders, Headers: newHeaders,
DbId: rsp.DbId, DbId: rsp.DbId,
Unmangled: unmangled, Unmangled: unmangled,
} }
if !headersOnly { if !headersOnly {
@ -359,23 +360,23 @@ func (wsmd *WSMessageJSON) Parse() (*ProxyWSMessage, error) {
} }
retData := &ProxyWSMessage{ retData := &ProxyWSMessage{
Message: message, Message: message,
Type: mtype, Type: mtype,
Direction: Direction, Direction: Direction,
Timestamp: time.Unix(0, wsmd.Timestamp), Timestamp: time.Unix(0, wsmd.Timestamp),
Unmangled: unmangled, Unmangled: unmangled,
DbId: wsmd.DbId, DbId: wsmd.DbId,
} }
return retData, nil return retData, nil
} }
func NewWSMessageJSON(wsm *ProxyWSMessage) (*WSMessageJSON) { func NewWSMessageJSON(wsm *ProxyWSMessage) *WSMessageJSON {
isBinary := false isBinary := false
if wsm.Type == websocket.BinaryMessage { if wsm.Type == websocket.BinaryMessage {
isBinary = true isBinary = true
} }
toServer := false toServer := false
if wsm.Direction == ToServer { if wsm.Direction == ToServer {
toServer = true toServer = true
@ -387,12 +388,12 @@ func NewWSMessageJSON(wsm *ProxyWSMessage) (*WSMessageJSON) {
} }
ret := &WSMessageJSON{ ret := &WSMessageJSON{
Message: base64.StdEncoding.EncodeToString(wsm.Message), Message: base64.StdEncoding.EncodeToString(wsm.Message),
IsBinary: isBinary, IsBinary: isBinary,
ToServer: toServer, ToServer: toServer,
Timestamp: wsm.Timestamp.UnixNano(), Timestamp: wsm.Timestamp.UnixNano(),
Unmangled: unmangled, Unmangled: unmangled,
DbId: wsm.DbId, DbId: wsm.DbId,
} }
return ret return ret
@ -427,11 +428,11 @@ type successResult struct {
/* /*
Ping Ping
*/ */
type pingMessage struct {} type pingMessage struct{}
type pingResponse struct { type pingResponse struct {
Success bool Success bool
Ping string Ping string
} }
func pingHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) { func pingHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
@ -448,7 +449,7 @@ type submitMessage struct {
} }
type submitResponse struct { type submitResponse struct {
Success bool Success bool
SubmittedRequest *RequestJSON SubmittedRequest *RequestJSON
} }
@ -475,8 +476,8 @@ func submitHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Interceptin
} }
SaveNewRequest(storage, req) SaveNewRequest(storage, req)
} }
logger.Println("Submitting request to", req.FullURL(),"...") logger.Println("Submitting request to", req.FullURL(), "...")
if err := req.Submit(); err != nil { if err := iproxy.SubmitRequest(req); err != nil {
ErrorResponse(c, err.Error()) ErrorResponse(c, err.Error())
return return
} }
@ -504,7 +505,7 @@ type saveNewMessage struct {
type saveNewResponse struct { type saveNewResponse struct {
Success bool Success bool
DbId string DbId string
} }
func saveNewHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) { func saveNewHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
@ -540,7 +541,7 @@ func saveNewHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercepti
response := &saveNewResponse{ response := &saveNewResponse{
Success: true, Success: true,
DbId: req.DbId, DbId: req.DbId,
} }
MessageResponse(c, response) MessageResponse(c, response)
} }
@ -549,10 +550,10 @@ func saveNewHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercepti
QueryRequests QueryRequests
*/ */
type storageQueryMessage struct { type storageQueryMessage struct {
Query StrMessageQuery Query StrMessageQuery
HeadersOnly bool HeadersOnly bool
MaxResults int64 MaxResults int64
Storage int Storage int
} }
type storageQueryResult struct { type storageQueryResult struct {
@ -562,9 +563,9 @@ type storageQueryResult struct {
func storageQueryHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) { func storageQueryHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
mreq := storageQueryMessage{ mreq := storageQueryMessage{
Query: nil, Query: nil,
HeadersOnly: false, HeadersOnly: false,
MaxResults: 0, MaxResults: 0,
} }
if err := json.Unmarshal(b, &mreq); err != nil { if err := json.Unmarshal(b, &mreq); err != nil {
@ -658,10 +659,9 @@ func validateQueryHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Inte
ErrorResponse(c, err.Error()) ErrorResponse(c, err.Error())
return return
} }
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
/* /*
SetScope SetScope
*/ */
@ -672,7 +672,7 @@ type setScopeMessage struct {
func setScopeHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) { func setScopeHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
mreq := setScopeMessage{} mreq := setScopeMessage{}
if err := json.Unmarshal(b, &mreq); err != nil { if err := json.Unmarshal(b, &mreq); err != nil {
ErrorResponse(c, "error parsing query message") ErrorResponse(c, "error parsing query message")
return return
@ -690,7 +690,7 @@ func setScopeHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercept
return return
} }
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
/* /*
@ -701,9 +701,9 @@ type viewScopeMessage struct {
} }
type viewScopeResult struct { type viewScopeResult struct {
Success bool Success bool
IsCustom bool IsCustom bool
Query StrMessageQuery Query StrMessageQuery
} }
func viewScopeHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) { func viewScopeHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
@ -712,7 +712,7 @@ func viewScopeHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercep
if scopeQuery == nil && scopeChecker != nil { if scopeQuery == nil && scopeChecker != nil {
MessageResponse(c, &viewScopeResult{ MessageResponse(c, &viewScopeResult{
Success: true, Success: true,
IsCustom: true, IsCustom: true,
}) })
return return
@ -726,9 +726,9 @@ func viewScopeHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercep
} }
MessageResponse(c, &viewScopeResult{ MessageResponse(c, &viewScopeResult{
Success: true, Success: true,
IsCustom: false, IsCustom: false,
Query: strQuery, Query: strQuery,
}) })
} }
@ -737,8 +737,8 @@ Tag messages
*/ */
type addTagMessage struct { type addTagMessage struct {
ReqId string ReqId string
Tag string Tag string
Storage int Storage int
} }
@ -765,7 +765,7 @@ func addTagHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Interceptin
ErrorResponse(c, "both request id and tag are required") ErrorResponse(c, "both request id and tag are required")
return return
} }
req, err := storage.LoadRequest(mreq.ReqId) req, err := storage.LoadRequest(mreq.ReqId)
if err != nil { if err != nil {
ErrorResponse(c, fmt.Sprintf("error loading request: %s", err.Error())) ErrorResponse(c, fmt.Sprintf("error loading request: %s", err.Error()))
@ -779,12 +779,12 @@ func addTagHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Interceptin
return return
} }
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
type removeTagMessage struct { type removeTagMessage struct {
ReqId string ReqId string
Tag string Tag string
Storage int Storage int
} }
@ -811,7 +811,7 @@ func removeTagHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercep
ErrorResponse(c, "both request id and tag are required") ErrorResponse(c, "both request id and tag are required")
return return
} }
req, err := storage.LoadRequest(mreq.ReqId) req, err := storage.LoadRequest(mreq.ReqId)
if err != nil { if err != nil {
ErrorResponse(c, fmt.Sprintf("error loading request: %s", err.Error())) ErrorResponse(c, fmt.Sprintf("error loading request: %s", err.Error()))
@ -825,11 +825,11 @@ func removeTagHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercep
return return
} }
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
type clearTagsMessage struct { type clearTagsMessage struct {
ReqId string ReqId string
Storage int Storage int
} }
@ -866,7 +866,7 @@ func clearTagHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercept
return return
} }
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
/* /*
@ -874,12 +874,12 @@ Intercept
*/ */
type interceptMessage struct { type interceptMessage struct {
InterceptRequests bool InterceptRequests bool
InterceptResponses bool InterceptResponses bool
InterceptWS bool InterceptWS bool
UseQuery bool UseQuery bool
Query MessageQuery Query MessageQuery
} }
type intHandshakeResult struct { type intHandshakeResult struct {
@ -891,49 +891,49 @@ var getNextIntId = IdCounter()
type intRequest struct { type intRequest struct {
// A request to have a message mangled // A request to have a message mangled
Id int Id int
Type string Type string
Success bool Success bool
Result chan *intResponse `json:"-"` Result chan *intResponse `json:"-"`
Request *RequestJSON `json:"Request,omitempty"` Request *RequestJSON `json:"Request,omitempty"`
Response *ResponseJSON `json:"Response,omitempty"` Response *ResponseJSON `json:"Response,omitempty"`
WSMessage *WSMessageJSON `json:"WSMessage,omitempty"` WSMessage *WSMessageJSON `json:"WSMessage,omitempty"`
} }
type intResponse struct { type intResponse struct {
// response from the client with a mangled http request // response from the client with a mangled http request
Id int Id int
Dropped bool Dropped bool
Request *RequestJSON `json:"Request,omitempty"` Request *RequestJSON `json:"Request,omitempty"`
Response *ResponseJSON `json:"Response,omitempty"` Response *ResponseJSON `json:"Response,omitempty"`
WSMessage *WSMessageJSON `json:"WSMessage,omitempty"` WSMessage *WSMessageJSON `json:"WSMessage,omitempty"`
} }
type intErrorMessage struct { type intErrorMessage struct {
// a message template for sending an error to client if there is an error // a message template for sending an error to client if there is an error
// with the mangled message they sent // with the mangled message they sent
Id int Id int
Success bool Success bool
Reason string Reason string
} }
func intErrorResponse(id int, conn net.Conn, reason string) { func intErrorResponse(id int, conn net.Conn, reason string) {
m := &intErrorMessage{ m := &intErrorMessage{
Id: id, Id: id,
Success: false, Success: false,
Reason: reason, Reason: reason,
} }
MessageResponse(conn, m) MessageResponse(conn, m)
} }
func interceptHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) { func interceptHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
mreq := interceptMessage{ mreq := interceptMessage{
InterceptRequests: false, InterceptRequests: false,
InterceptResponses: false, InterceptResponses: false,
InterceptWS: false, InterceptWS: false,
UseQuery: false, UseQuery: false,
} }
if err := json.Unmarshal(b, &mreq); err != nil { if err := json.Unmarshal(b, &mreq); err != nil {
@ -1002,9 +1002,9 @@ func interceptHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercep
// convert request data to an intRequest // convert request data to an intRequest
intReq := &intRequest{ intReq := &intRequest{
Id: getNextIntId(), Id: getNextIntId(),
Type: "httprequest", Type: "httprequest",
Result: make(chan *intResponse), Result: make(chan *intResponse),
Success: true, Success: true,
Request: reqData, Request: reqData,
@ -1054,17 +1054,17 @@ func interceptHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercep
reqData := NewRequestJSON(req, false) reqData := NewRequestJSON(req, false)
CleanReqJSON(reqData) CleanReqJSON(reqData)
rspData := NewResponseJSON(rsp, false) rspData := NewResponseJSON(rsp, false)
CleanRspJSON(rspData) CleanRspJSON(rspData)
intReq := &intRequest{ intReq := &intRequest{
Id: getNextIntId(), Id: getNextIntId(),
Type: "httpresponse", Type: "httpresponse",
Result: make(chan *intResponse), Result: make(chan *intResponse),
Success: true, Success: true,
Request: reqData, Request: reqData,
Response: rspData, Response: rspData,
} }
@ -1120,19 +1120,18 @@ func interceptHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercep
reqData := NewRequestJSON(req, false) reqData := NewRequestJSON(req, false)
CleanReqJSON(reqData) CleanReqJSON(reqData)
rspData := NewResponseJSON(rsp, false) rspData := NewResponseJSON(rsp, false)
CleanRspJSON(rspData) CleanRspJSON(rspData)
intReq := &intRequest{ intReq := &intRequest{
Id: getNextIntId(), Id: getNextIntId(),
Type: msgType, Type: msgType,
Result: make(chan *intResponse), Result: make(chan *intResponse),
Success: true, Success: true,
Request: reqData, Request: reqData,
Response: rspData, Response: rspData,
WSMessage: wsData, WSMessage: wsData,
} }
@ -1296,7 +1295,7 @@ func allSavedQueriesHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *In
savedQueries := make([]*StrSavedQuery, 0) savedQueries := make([]*StrSavedQuery, 0)
for _, q := range goQueries { for _, q := range goQueries {
strSavedQuery := &StrSavedQuery{ strSavedQuery := &StrSavedQuery{
Name: q.Name, Name: q.Name,
Query: nil, Query: nil,
} }
sq, err := GoQueryToStrQuery(q.Query) sq, err := GoQueryToStrQuery(q.Query)
@ -1312,14 +1311,14 @@ func allSavedQueriesHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *In
} }
type saveQueryMessage struct { type saveQueryMessage struct {
Name string Name string
Query StrMessageQuery Query StrMessageQuery
Storage int Storage int
} }
func saveQueryHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) { func saveQueryHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
mreq := saveQueryMessage{} mreq := saveQueryMessage{}
if err := json.Unmarshal(b, &mreq); err != nil { if err := json.Unmarshal(b, &mreq); err != nil {
ErrorResponse(c, "error parsing message") ErrorResponse(c, "error parsing message")
return return
@ -1358,17 +1357,17 @@ func saveQueryHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercep
return return
} }
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
type loadQueryMessage struct { type loadQueryMessage struct {
Name string Name string
Storage int Storage int
} }
type loadQueryResult struct { type loadQueryResult struct {
Success bool Success bool
Query StrMessageQuery Query StrMessageQuery
} }
func loadQueryHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) { func loadQueryHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
@ -1408,14 +1407,14 @@ func loadQueryHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Intercep
result := &loadQueryResult{ result := &loadQueryResult{
Success: true, Success: true,
Query: strQuery, Query: strQuery,
} }
MessageResponse(c, result) MessageResponse(c, result)
} }
type deleteQueryMessage struct { type deleteQueryMessage struct {
Name string Name string
Storage int Storage int
} }
@ -1442,7 +1441,7 @@ func deleteQueryHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Interc
ErrorResponse(c, err.Error()) ErrorResponse(c, err.Error())
return return
} }
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
/* /*
@ -1450,10 +1449,10 @@ Listener management
*/ */
type activeListener struct { type activeListener struct {
Id int Id int
Listener net.Listener `json:"-"` Listener net.Listener `json:"-"`
Type string Type string
Addr string Addr string
} }
type addListenerMessage struct { type addListenerMessage struct {
@ -1463,7 +1462,7 @@ type addListenerMessage struct {
type addListenerResult struct { type addListenerResult struct {
Success bool Success bool
Id int Id int
} }
var getNextMsgListenerId = IdCounter() var getNextMsgListenerId = IdCounter()
@ -1500,10 +1499,10 @@ func addListenerHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Interc
iproxy.AddListener(listener) iproxy.AddListener(listener)
alistener := &activeListener{ alistener := &activeListener{
Id: getNextMsgListenerId(), Id: getNextMsgListenerId(),
Listener: listener, Listener: listener,
Type: mreq.Type, Type: mreq.Type,
Addr: mreq.Addr, Addr: mreq.Addr,
} }
msgActiveListenersMtx.Lock() msgActiveListenersMtx.Lock()
@ -1511,7 +1510,7 @@ func addListenerHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Interc
msgActiveListeners[alistener.Id] = alistener msgActiveListeners[alistener.Id] = alistener
result := &addListenerResult{ result := &addListenerResult{
Success: true, Success: true,
Id: alistener.Id, Id: alistener.Id,
} }
MessageResponse(c, result) MessageResponse(c, result)
@ -1538,11 +1537,10 @@ func removeListenerHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Int
iproxy.RemoveListener(alistener.Listener) iproxy.RemoveListener(alistener.Listener)
delete(msgActiveListeners, alistener.Id) delete(msgActiveListeners, alistener.Id)
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
type getListenersMessage struct{}
type getListenersMessage struct {}
type getListenersResult struct { type getListenersResult struct {
Success bool Success bool
@ -1567,7 +1565,7 @@ Certificate Management
*/ */
type loadCertificatesMessage struct { type loadCertificatesMessage struct {
KeyFile string KeyFile string
CertificateFile string CertificateFile string
} }
@ -1583,18 +1581,18 @@ func loadCertificatesHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *I
return return
} }
caCert, err := tls.LoadX509KeyPair(mreq.CertificateFile, mreq.KeyFile) caCert, err := tls.LoadX509KeyPair(mreq.CertificateFile, mreq.KeyFile)
if err != nil { if err != nil {
ErrorResponse(c, err.Error()) ErrorResponse(c, err.Error())
return return
} }
iproxy.SetCACertificate(&caCert) iproxy.SetCACertificate(&caCert)
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
type setCertificatesMessage struct { type setCertificatesMessage struct {
KeyPEMData []byte KeyPEMData []byte
CertificatePEMData []byte CertificatePEMData []byte
} }
@ -1610,23 +1608,23 @@ func setCertificatesHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *In
return return
} }
caCert, err := tls.X509KeyPair(mreq.CertificatePEMData, mreq.KeyPEMData) caCert, err := tls.X509KeyPair(mreq.CertificatePEMData, mreq.KeyPEMData)
if err != nil { if err != nil {
ErrorResponse(c, err.Error()) ErrorResponse(c, err.Error())
return return
} }
iproxy.SetCACertificate(&caCert) iproxy.SetCACertificate(&caCert)
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
func clearCertificatesHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) { func clearCertificatesHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
iproxy.SetCACertificate(nil) iproxy.SetCACertificate(nil)
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
type generateCertificatesMessage struct { type generateCertificatesMessage struct {
KeyFile string KeyFile string
CertFile string CertFile string
} }
@ -1639,13 +1637,13 @@ func generateCertificatesHandler(b []byte, c net.Conn, logger *log.Logger, iprox
pair, err := GenerateCACerts() pair, err := GenerateCACerts()
if err != nil { if err != nil {
ErrorResponse(c, "error generating certificates: " + err.Error()) ErrorResponse(c, "error generating certificates: "+err.Error())
return return
} }
pkeyFile, err := os.OpenFile(mreq.KeyFile, os.O_RDWR|os.O_CREATE, 0600) pkeyFile, err := os.OpenFile(mreq.KeyFile, os.O_RDWR|os.O_CREATE, 0600)
if err != nil { if err != nil {
ErrorResponse(c, "could not save private key: " + err.Error()) ErrorResponse(c, "could not save private key: "+err.Error())
return return
} }
pkeyFile.Write(pair.PrivateKeyPEM()) pkeyFile.Write(pair.PrivateKeyPEM())
@ -1656,7 +1654,7 @@ func generateCertificatesHandler(b []byte, c net.Conn, logger *log.Logger, iprox
certFile, err := os.OpenFile(mreq.CertFile, os.O_RDWR|os.O_CREATE, 0600) certFile, err := os.OpenFile(mreq.CertFile, os.O_RDWR|os.O_CREATE, 0600)
if err != nil { if err != nil {
ErrorResponse(c, "could not save private key: " + err.Error()) ErrorResponse(c, "could not save private key: "+err.Error())
return return
} }
certFile.Write(pair.CACertPEM()) certFile.Write(pair.CACertPEM())
@ -1665,12 +1663,12 @@ func generateCertificatesHandler(b []byte, c net.Conn, logger *log.Logger, iprox
return return
} }
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
type generatePEMCertificatesResult struct { type generatePEMCertificatesResult struct {
Success bool Success bool
KeyPEMData []byte KeyPEMData []byte
CertificatePEMData []byte CertificatePEMData []byte
} }
@ -1683,13 +1681,13 @@ func generatePEMCertificatesHandler(b []byte, c net.Conn, logger *log.Logger, ip
pair, err := GenerateCACerts() pair, err := GenerateCACerts()
if err != nil { if err != nil {
ErrorResponse(c, "error generating certificates: " + err.Error()) ErrorResponse(c, "error generating certificates: "+err.Error())
return return
} }
result := &generatePEMCertificatesResult{ result := &generatePEMCertificatesResult{
Success: true, Success: true,
KeyPEMData: pair.PrivateKeyPEM(), KeyPEMData: pair.PrivateKeyPEM(),
CertificatePEMData: pair.CACertPEM(), CertificatePEMData: pair.CACertPEM(),
} }
MessageResponse(c, result) MessageResponse(c, result)
@ -1700,12 +1698,12 @@ Storage functions
*/ */
type addSQLiteStorageMessage struct { type addSQLiteStorageMessage struct {
Path string Path string
Description string Description string
} }
type addSQLiteStorageResult struct { type addSQLiteStorageResult struct {
Success bool Success bool
StorageId int StorageId int
} }
@ -1723,13 +1721,13 @@ func addSQLiteStorageHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *I
storage, err := OpenSQLiteStorage(mreq.Path, logger) storage, err := OpenSQLiteStorage(mreq.Path, logger)
if err != nil { if err != nil {
ErrorResponse(c, "error opening SQLite databae: " + err.Error()) ErrorResponse(c, "error opening SQLite databae: "+err.Error())
return return
} }
sid := iproxy.AddMessageStorage(storage, mreq.Description) sid := iproxy.AddMessageStorage(storage, mreq.Description)
result := &addSQLiteStorageResult{ result := &addSQLiteStorageResult{
Success: true, Success: true,
StorageId: sid, StorageId: sid,
} }
MessageResponse(c, result) MessageResponse(c, result)
@ -1740,7 +1738,7 @@ type addInMemoryStorageMessage struct {
} }
type addInMemoryStorageResult struct { type addInMemoryStorageResult struct {
Success bool Success bool
StorageId int StorageId int
} }
@ -1753,13 +1751,13 @@ func addInMemoryStorageHandler(b []byte, c net.Conn, logger *log.Logger, iproxy
storage, err := InMemoryStorage(logger) storage, err := InMemoryStorage(logger)
if err != nil { if err != nil {
ErrorResponse(c, "error creating in memory storage: " + err.Error()) ErrorResponse(c, "error creating in memory storage: "+err.Error())
return return
} }
sid := iproxy.AddMessageStorage(storage, mreq.Description) sid := iproxy.AddMessageStorage(storage, mreq.Description)
result := &addInMemoryStorageResult{ result := &addInMemoryStorageResult{
Success: true, Success: true,
StorageId: sid, StorageId: sid,
} }
MessageResponse(c, result) MessageResponse(c, result)
@ -1786,7 +1784,7 @@ func closeStorageHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *Inter
} }
iproxy.CloseMessageStorage(mreq.StorageId) iproxy.CloseMessageStorage(mreq.StorageId)
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
type setProxyStorageMessage struct { type setProxyStorageMessage struct {
@ -1811,17 +1809,17 @@ func setProxyStorageHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *In
return return
} }
MessageResponse(c, &successResult{Success:true}) MessageResponse(c, &successResult{Success: true})
} }
type savedStorageJSON struct { type savedStorageJSON struct {
Id int Id int
Description string Description string
} }
type listProxyStorageResult struct { type listProxyStorageResult struct {
Storages []*savedStorageJSON Storages []*savedStorageJSON
Success bool Success bool
} }
func listProxyStorageHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) { func listProxyStorageHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
@ -1832,7 +1830,49 @@ func listProxyStorageHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *I
} }
result := &listProxyStorageResult{ result := &listProxyStorageResult{
Storages: storagesJSON, Storages: storagesJSON,
Success: true, Success: true,
} }
MessageResponse(c, result) MessageResponse(c, result)
} }
/*
SetProxy
*/
type setProxyMessage struct {
UseProxy bool
ProxyHost string
ProxyPort int
ProxyIsSOCKS bool
UseCredentials bool
Username string
Password string
}
func setProxyHandler(b []byte, c net.Conn, logger *log.Logger, iproxy *InterceptingProxy) {
mreq := setProxyMessage{}
if err := json.Unmarshal(b, &mreq); err != nil {
ErrorResponse(c, "error parsing message")
return
}
var creds *ProxyCredentials = nil
if mreq.UseCredentials {
creds = &ProxyCredentials{
Username: mreq.Username,
Password: mreq.Password,
}
}
if !mreq.UseProxy {
iproxy.ClearUpstreamProxy()
} else {
if mreq.ProxyIsSOCKS {
iproxy.SetUpstreamSOCKSProxy(mreq.ProxyHost, mreq.ProxyPort, creds)
} else {
iproxy.SetUpstreamProxy(mreq.ProxyHost, mreq.ProxyPort, creds)
}
}
MessageResponse(c, &successResult{Success: true})
}

@ -4,7 +4,8 @@ import json
default_config = """{ default_config = """{
"listeners": [ "listeners": [
{"iface": "127.0.0.1", "port": 8080} {"iface": "127.0.0.1", "port": 8080}
] ],
"proxy": {"use_proxy": false, "host": "", "port": 0, "is_socks": false}
}""" }"""
@ -12,6 +13,7 @@ class ProxyConfig:
def __init__(self): def __init__(self):
self._listeners = [('127.0.0.1', '8080')] self._listeners = [('127.0.0.1', '8080')]
self._proxy = {'use_proxy': False, 'host': '', 'port': 0, 'is_socks': False}
def load(self, fname): def load(self, fname):
try: try:
@ -39,6 +41,10 @@ class ProxyConfig:
iface = '127.0.0.1' iface = '127.0.0.1'
self._listeners.append((iface, port)) self._listeners.append((iface, port))
if 'proxy' in config_info:
self._proxy = config_info['proxy']
@property @property
def listeners(self): def listeners(self):
@ -47,3 +53,67 @@ class ProxyConfig:
@listeners.setter @listeners.setter
def listeners(self, val): def listeners(self, val):
self._listeners = val self._listeners = val
@property
def proxy(self):
# don't use this, use the getters to get the parsed values
return self._proxy
@proxy.setter
def proxy(self, val):
self._proxy = val
@property
def use_proxy(self):
if self._proxy is None:
return False
if 'use_proxy' in self._proxy:
if self._proxy['use_proxy']:
return True
return False
@property
def proxy_host(self):
if self._proxy is None:
return ''
if 'host' in self._proxy:
return self._proxy['host']
return ''
@property
def proxy_port(self):
if self._proxy is None:
return ''
if 'port' in self._proxy:
return self._proxy['port']
return ''
@property
def proxy_username(self):
if self._proxy is None:
return ''
if 'username' in self._proxy:
return self._proxy['username']
return ''
@property
def proxy_password(self):
if self._proxy is None:
return ''
if 'password' in self._proxy:
return self._proxy['password']
return ''
@property
def use_proxy_creds(self):
return ('username' in self._proxy or 'password' in self._proxy)
@property
def is_socks_proxy(self):
if self._proxy is None:
return False
if 'is_socks' in self._proxy:
if self._proxy['is_socks']:
return True
return False

@ -1,6 +1,6 @@
from itertools import groupby from itertools import groupby
from ..proxy import InvalidQuery from ..proxy import InvalidQuery, time_to_nsecs
from ..colors import Colors, Styles from ..colors import Colors, Styles
# class BuiltinFilters(object): # class BuiltinFilters(object):
@ -71,6 +71,11 @@ def filtercmd(client, args):
""" """
try: try:
phrases = [list(group) for k, group in groupby(args, lambda x: x == "OR") if not k] phrases = [list(group) for k, group in groupby(args, lambda x: x == "OR") if not k]
for phrase in phrases:
# we do before/after by id not by timestamp
if phrase[0] in ('before', 'b4', 'after', 'af') and len(phrase) > 1:
r = client.req_by_id(phrase[1], headers_only=True)
phrase[1] = str(time_to_nsecs(r.time_start))
client.context.apply_phrase(phrases) client.context.apply_phrase(phrases)
except InvalidQuery as e: except InvalidQuery as e:
print(e) print(e)

@ -7,31 +7,32 @@ import string
import urllib import urllib
from ..util import hexdump, printable_data, copy_to_clipboard, clipboard_contents, encode_basic_auth, parse_basic_auth from ..util import hexdump, printable_data, copy_to_clipboard, clipboard_contents, encode_basic_auth, parse_basic_auth
from ..console import CommandError
from io import StringIO from io import StringIO
def print_maybe_bin(s): def print_maybe_bin(s):
binary = False binary = False
for c in s: for c in s:
if str(c) not in string.printable: if chr(c) not in string.printable:
binary = True binary = True
break break
if binary: if binary:
print(hexdump(s)) print(hexdump(s))
else: else:
print(s) print(s.decode())
def asciihex_encode_helper(s): def asciihex_encode_helper(s):
return ''.join('{0:x}'.format(c) for c in s) return ''.join('{0:x}'.format(c) for c in s).encode()
def asciihex_decode_helper(s): def asciihex_decode_helper(s):
ret = [] ret = []
try: try:
for a, b in zip(s[0::2], s[1::2]): for a, b in zip(s[0::2], s[1::2]):
c = a+b c = chr(a)+chr(b)
ret.append(chr(int(c, 16))) ret.append(chr(int(c, 16)))
return ''.join(ret) return ''.join(ret).encode()
except Exception as e: except Exception as e:
raise PappyException(e) raise CommandError(e)
def gzip_encode_helper(s): def gzip_encode_helper(s):
out = StringIO.StringIO() out = StringIO.StringIO()
@ -54,13 +55,21 @@ def base64_decode_helper(s):
return s_padded return s_padded
except: except:
pass pass
raise PappyException("Unable to base64 decode string") raise CommandError("Unable to base64 decode string")
def url_decode_helper(s):
bs = s.decode()
return urllib.parse.unquote(bs).encode()
def url_encode_helper(s):
bs = s.decode()
return urllib.parse.quote_plus(bs).encode()
def html_encode_helper(s): def html_encode_helper(s):
return ''.join(['&#x{0:x};'.format(c) for c in s]) return ''.join(['&#x{0:x};'.format(c) for c in s]).encode()
def html_decode_helper(s): def html_decode_helper(s):
return html.unescape(s) return html.unescape(s.decode()).encode()
def _code_helper(args, func, copy=True): def _code_helper(args, func, copy=True):
if len(args) == 0: if len(args) == 0:
@ -107,7 +116,7 @@ def url_decode(client, args):
If no string is given, will decode the contents of the clipboard. If no string is given, will decode the contents of the clipboard.
Results are copied to the clipboard. Results are copied to the clipboard.
""" """
print_maybe_bin(_code_helper(args, urllib.unquote)) print_maybe_bin(_code_helper(args, url_decode_helper))
def url_encode(client, args): def url_encode(client, args):
""" """
@ -115,7 +124,7 @@ def url_encode(client, args):
If no string is given, will encode the contents of the clipboard. If no string is given, will encode the contents of the clipboard.
Results are copied to the clipboard. Results are copied to the clipboard.
""" """
print_maybe_bin(_code_helper(args, urllib.quote_plus)) print_maybe_bin(_code_helper(args, url_encode_helper))
def asciihex_decode(client, args): def asciihex_decode(client, args):
""" """
@ -187,7 +196,7 @@ def url_decode_raw(client, args):
results will not be copied. It is suggested you redirect the output results will not be copied. It is suggested you redirect the output
to a file. to a file.
""" """
print(_code_helper(args, urllib.unquote, copy=False)) print(_code_helper(args, url_decode_helper, copy=False))
def url_encode_raw(client, args): def url_encode_raw(client, args):
""" """
@ -195,7 +204,7 @@ def url_encode_raw(client, args):
results will not be copied. It is suggested you redirect the output results will not be copied. It is suggested you redirect the output
to a file. to a file.
""" """
print(_code_helper(args, urllib.quote_plus, copy=False)) print(_code_helper(args, url_encode_helper, copy=False))
def asciihex_decode_raw(client, args): def asciihex_decode_raw(client, args):
""" """
@ -254,9 +263,8 @@ def unix_time_decode(client, args):
print(_code_helper(args, unix_time_decode_helper)) print(_code_helper(args, unix_time_decode_helper))
def http_auth_encode(client, args): def http_auth_encode(client, args):
args = shlex.split(args[0])
if len(args) != 2: if len(args) != 2:
raise PappyException('Usage: http_auth_encode <username> <password>') raise CommandError('Usage: http_auth_encode <username> <password>')
username, password = args username, password = args
print(encode_basic_auth(username, password)) print(encode_basic_auth(username, password))

@ -23,7 +23,7 @@ class WatchMacro(InterceptMacro):
printstr = "< " printstr = "< "
printstr += verb_color(request.method) + request.method + Colors.ENDC + ' ' printstr += verb_color(request.method) + request.method + Colors.ENDC + ' '
printstr += url_formatter(request, colored=True) printstr += url_formatter(request, colored=True)
printstr += " -> " printstr += " \u2192 "
response_code = str(response.status_code) + ' ' + response.reason response_code = str(response.status_code) + ' ' + response.reason
response_code = scode_color(response_code) + response_code + Colors.ENDC response_code = scode_color(response_code) + response_code + Colors.ENDC
printstr += response_code printstr += response_code

@ -1524,7 +1524,7 @@ def update_buffers(req):
# Save the port, ssl, host setting # Save the port, ssl, host setting
vim.command("let s:dest_port=%d" % req.dest_port) vim.command("let s:dest_port=%d" % req.dest_port)
vim.command("let s:dest_host='%s'" % req.dest_host) vim.command("let s:dest_host='%s'" % escape(req.dest_host))
if req.use_tls: if req.use_tls:
vim.command("let s:use_tls=1") vim.command("let s:use_tls=1")
@ -1544,6 +1544,8 @@ def set_up_windows():
reqid = vim.eval("a:2") reqid = vim.eval("a:2")
storage_id = vim.eval("a:3") storage_id = vim.eval("a:3")
msg_addr = vim.eval("a:4") msg_addr = vim.eval("a:4")
vim.command("let s:storage_id=%d" % int(storage_id))
# Get the left buffer # Get the left buffer
vim.command("new") vim.command("new")
@ -1568,11 +1570,12 @@ def dest_loc():
dest_host = vim.eval("s:dest_host") dest_host = vim.eval("s:dest_host")
dest_port = int(vim.eval("s:dest_port")) dest_port = int(vim.eval("s:dest_port"))
tls_num = vim.eval("s:use_tls") tls_num = vim.eval("s:use_tls")
storage_id = int(vim.eval("s:storage_id"))
if tls_num == "1": if tls_num == "1":
use_tls = True use_tls = True
else: else:
use_tls = False use_tls = False
return (dest_host, dest_port, use_tls) return (dest_host, dest_port, use_tls, storage_id)
def submit_current_buffer(): def submit_current_buffer():
curbuf = vim.current.buffer curbuf = vim.current.buffer
@ -1586,14 +1589,15 @@ def submit_current_buffer():
full_request = '\n'.join(curbuf) full_request = '\n'.join(curbuf)
req = parse_request(full_request) req = parse_request(full_request)
dest_host, dest_port, use_tls = dest_loc() dest_host, dest_port, use_tls, storage_id = dest_loc()
req.dest_host = dest_host req.dest_host = dest_host
req.dest_port = dest_port req.dest_port = dest_port
req.use_tls = use_tls req.use_tls = use_tls
comm_type, comm_addr = get_conn_addr() comm_type, comm_addr = get_conn_addr()
with ProxyConnection(kind=comm_type, addr=comm_addr) as conn: with ProxyConnection(kind=comm_type, addr=comm_addr) as conn:
new_req = conn.submit(req) new_req = conn.submit(req, storage=storage_id)
conn.add_tag(new_req.db_id, "repeater", storage_id)
update_buffers(new_req) update_buffers(new_req)
# (left, right) = set_up_windows() # (left, right) = set_up_windows()

@ -481,17 +481,23 @@ def site_map(client, args):
paths = True paths = True
else: else:
paths = False paths = False
reqs = client.in_context_requests(headers_only=True) all_reqs = client.in_context_requests(headers_only=True)
paths_set = set() reqs_by_host = {}
for req in reqs: for req in all_reqs:
if req.response and req.response.status_code != 404: reqs_by_host.setdefault(req.dest_host, []).append(req)
paths_set.add(path_tuple(req.url)) for host, reqs in reqs_by_host.items():
tree = sorted(list(paths_set)) paths_set = set()
if paths: for req in reqs:
for p in tree: if req.response and req.response.status_code != 404:
print ('/'.join(list(p))) paths_set.add(path_tuple(req.url))
else: tree = sorted(list(paths_set))
print_tree(tree) print(host)
if paths:
for p in tree:
print ('/'.join(list(p)))
else:
print_tree(tree)
print("")
def dump_response(client, args): def dump_response(client, args):
""" """
@ -515,6 +521,78 @@ def dump_response(client, args):
else: else:
print('Request {} does not have a response'.format(req.reqid)) print('Request {} does not have a response'.format(req.reqid))
def get_surrounding_lines(s, n, lines):
left = n
right = n
lines_left = 0
lines_right = 0
# move left until we find enough lines or hit the edge
while left > 0 and lines_left < lines:
if s[left] == '\n':
lines_left += 1
left -= 1
# move right until we find enough lines or hit the edge
while right < len(s) and lines_right < lines:
if s[right] == '\n':
lines_right += 1
right += 1
return s[left:right]
def print_search_header(reqid, locstr):
printstr = Styles.TABLE_HEADER
printstr += "Result(s) for request {} ({})".format(reqid, locstr)
printstr += Colors.ENDC
print(printstr)
def highlight_str(s, substr):
highlighted = Colors.BGYELLOW + Colors.BLACK + Colors.BOLD + substr + Colors.ENDC
return s.replace(substr, highlighted)
def search_message(mes, substr, lines, reqid, locstr):
header_printed = False
for m in re.finditer(substr, mes):
if not header_printed:
print_search_header(reqid, locstr)
header_printed = True
n = m.start()
linestr = get_surrounding_lines(mes, n, lines)
linelist = linestr.split('\n')
linestr = '\n'.join(line[:500] for line in linelist)
toprint = highlight_str(linestr, substr)
print(toprint)
print('-'*50)
def search(client, args):
search_str = args[0]
lines = 2
if len(args) > 1:
lines = int(args[1])
for req in client.in_context_requests_iter():
reqid = client.get_reqid(req)
reqheader_printed = False
try:
mes = req.full_message().decode()
search_message(mes, search_str, lines, reqid, "Request")
except UnicodeDecodeError:
pass
if req.response:
try:
mes = req.response.full_message().decode()
search_message(mes, search_str, lines, reqid, "Response")
except UnicodeDecodeError:
pass
wsheader_printed = False
for wsm in req.ws_messages:
if not wsheader_printed:
print_search_header(client.get_reqid(req), reqid, "Websocket Messages")
wsheader_printed = True
if search_str in wsm.message:
print(highlight_str(wsm.message, search_str))
# @crochet.wait_for(timeout=None) # @crochet.wait_for(timeout=None)
# @defer.inlineCallbacks # @defer.inlineCallbacks
@ -572,6 +650,7 @@ def load_cmds(cmd):
'urls': (find_urls, None), 'urls': (find_urls, None),
'site_map': (site_map, None), 'site_map': (site_map, None),
'dump_response': (dump_response, None), 'dump_response': (dump_response, None),
'search': (search, None),
# 'view_request_bytes': (view_request_bytes, None), # 'view_request_bytes': (view_request_bytes, None),
# 'view_response_bytes': (view_response_bytes, None), # 'view_response_bytes': (view_response_bytes, None),
}) })

@ -85,17 +85,23 @@ class SockBuffer:
class Headers: class Headers:
def __init__(self, headers=None): def __init__(self, headers=None):
if headers is None: self.headers = {}
self.headers = {} if headers is not None:
else: if isinstance(headers, Headers):
self.headers = headers for _, pairs in headers.headers.items():
for k, v in pairs:
self.add(k, v)
else:
for k, vs in headers.items():
for v in vs:
self.add(k, v)
def __contains__(self, hd): def __contains__(self, hd):
for k, _ in self.headers.items(): for k, _ in self.headers.items():
if k.lower() == hd.lower(): if k.lower() == hd.lower():
return True return True
return False return False
def add(self, k, v): def add(self, k, v):
try: try:
l = self.headers[k.lower()] l = self.headers[k.lower()]
@ -265,11 +271,7 @@ class HTTPRequest:
self.proto_major = proto_major self.proto_major = proto_major
self.proto_minor = proto_minor self.proto_minor = proto_minor
self.headers = Headers() self.headers = Headers(headers)
if headers is not None:
for k, vs in headers.items():
for v in vs:
self.headers.add(k, v)
self.headers_only = headers_only self.headers_only = headers_only
self._body = bytes() self._body = bytes()
@ -280,8 +282,8 @@ class HTTPRequest:
self.dest_host = dest_host self.dest_host = dest_host
self.dest_port = dest_port self.dest_port = dest_port
self.use_tls = use_tls self.use_tls = use_tls
self.time_start = time_start or datetime.datetime(1970, 1, 1) self.time_start = time_start
self.time_end = time_end or datetime.datetime(1970, 1, 1) self.time_end = time_end
self.response = None self.response = None
self.unmangled = None self.unmangled = None
@ -412,7 +414,7 @@ class HTTPRequest:
path=self.url.geturl(), path=self.url.geturl(),
proto_major=self.proto_major, proto_major=self.proto_major,
proto_minor=self.proto_minor, proto_minor=self.proto_minor,
headers=self.headers.headers, headers=self.headers,
body=self.body, body=self.body,
dest_host=self.dest_host, dest_host=self.dest_host,
dest_port=self.dest_port, dest_port=self.dest_port,
@ -928,6 +930,21 @@ class ProxyConnection:
for ss in result["Storages"]: for ss in result["Storages"]:
ret.append(SavedStorage(ss["Id"], ss["Description"])) ret.append(SavedStorage(ss["Id"], ss["Description"]))
return ret return ret
@messagingFunction
def set_proxy(self, use_proxy=False, proxy_host="", proxy_port=0, use_creds=False,
username="", password="", is_socks=False):
cmd = {
"Command": "SetProxy",
"UseProxy": use_proxy,
"ProxyHost": proxy_host,
"ProxyPort": proxy_port,
"ProxyIsSOCKS": is_socks,
"UseCredentials": use_creds,
"Username": username,
"Password": password,
}
self.reqrsp_cmd(cmd)
@messagingFunction @messagingFunction
def intercept(self, macro): def intercept(self, macro):
@ -1086,6 +1103,7 @@ class ProxyClient:
# "add_in_memory_storage", # "add_in_memory_storage",
# "close_storage", # "close_storage",
# "set_proxy_storage", # "set_proxy_storage",
"set_proxy"
} }
def __enter__(self): def __enter__(self):
@ -1162,7 +1180,7 @@ class ProxyClient:
stype, prefix = s.description.split("|") stype, prefix = s.description.split("|")
storage = ActiveStorage(stype, s.storage_id, prefix) storage = ActiveStorage(stype, s.storage_id, prefix)
self._add_storage(storage, prefix) self._add_storage(storage, prefix)
def parse_reqid(self, reqid): def parse_reqid(self, reqid):
if reqid[0].isalpha(): if reqid[0].isalpha():
prefix = reqid[0] prefix = reqid[0]
@ -1172,6 +1190,10 @@ class ProxyClient:
realid = reqid realid = reqid
storage = self.storage_by_prefix[prefix] storage = self.storage_by_prefix[prefix]
return storage, realid return storage, realid
def get_reqid(self, req):
storage = self.storage_by_id[req.storage_id]
return storage.prefix + req.db_id
def storage_iter(self): def storage_iter(self):
for _, s in self.storage_by_id.items(): for _, s in self.storage_by_id.items():
@ -1190,6 +1212,17 @@ class ProxyClient:
if max_results > 0 and len(results) > max_results: if max_results > 0 and len(results) > max_results:
ret = results[:max_results] ret = results[:max_results]
return ret return ret
def in_context_requests_iter(self, headers_only=False, max_results=0):
results = self.query_storage(self.context.query,
headers_only=headers_only,
max_results=max_results)
ret = results
if max_results > 0 and len(results) > max_results:
ret = results[:max_results]
for reqh in ret:
req = self.req_by_id(reqh.db_id, storage_id=reqh.storage_id)
yield req
def prefixed_reqid(self, req): def prefixed_reqid(self, req):
prefix = "" prefix = ""
@ -1246,10 +1279,14 @@ class ProxyClient:
results = [r for r in reversed(results)] results = [r for r in reversed(results)]
return results return results
def req_by_id(self, reqid, headers_only=False): def req_by_id(self, reqid, storage_id=None, headers_only=False):
storage, rid = self.parse_reqid(reqid) if storage_id is None:
return self.msg_conn.req_by_id(rid, headers_only=headers_only, storage, db_id = self.parse_reqid(reqid)
storage=storage.storage_id) storage_id = storage.storage_id
else:
db_id = reqid
return self.msg_conn.req_by_id(db_id, headers_only=headers_only,
storage=storage_id)
# for these and submit, might need storage stored on the request itself # for these and submit, might need storage stored on the request itself
def add_tag(self, reqid, tag, storage=None): def add_tag(self, reqid, tag, storage=None):
@ -1275,12 +1312,12 @@ class ProxyClient:
def decode_req(result, headers_only=False): def decode_req(result, headers_only=False):
if "StartTime" in result: if "StartTime" in result and result["StartTime"] > 0:
time_start = time_from_nsecs(result["StartTime"]) time_start = time_from_nsecs(result["StartTime"])
else: else:
time_start = None time_start = None
if "EndTime" in result: if "EndTime" in result and result["EndTime"] > 0:
time_end = time_from_nsecs(result["EndTime"]) time_end = time_from_nsecs(result["EndTime"])
else: else:
time_end = None time_end = None

@ -114,6 +114,13 @@ def main():
client.add_listener(iface, port) client.add_listener(iface, port)
except MessageError as e: except MessageError as e:
print(str(e)) print(str(e))
# Set upstream proxy
if config.use_proxy:
client.set_proxy(config.use_proxy,
config.proxy_host,
config.proxy_port,
config.is_socks_proxy)
interface_loop(client) interface_loop(client)
except MessageError as e: except MessageError as e:
print(str(e)) print(str(e))

@ -2,6 +2,7 @@ import sys
import string import string
import time import time
import datetime import datetime
import base64
from pygments.formatters import TerminalFormatter from pygments.formatters import TerminalFormatter
from pygments.lexers import get_lexer_for_mimetype, HttpLexer from pygments.lexers import get_lexer_for_mimetype, HttpLexer
from pygments import highlight from pygments import highlight
@ -275,8 +276,8 @@ def clipboard_contents():
def encode_basic_auth(username, password): def encode_basic_auth(username, password):
decoded = '%s:%s' % (username, password) decoded = '%s:%s' % (username, password)
encoded = base64.b64encode(decoded) encoded = base64.b64encode(decoded.encode())
header = 'Basic %s' % encoded header = 'Basic %s' % encoded.decode()
return header return header
def parse_basic_auth(header): def parse_basic_auth(header):

@ -6,8 +6,8 @@ import (
"fmt" "fmt"
"log" "log"
"runtime" "runtime"
"strings"
"sort" "sort"
"strings"
) )
type schemaUpdater func(tx *sql.Tx) error type schemaUpdater func(tx *sql.Tx) error
@ -110,19 +110,19 @@ SCHEMA 8 / INITIAL
func schema8(tx *sql.Tx) error { func schema8(tx *sql.Tx) error {
// Create a schema that is the same as pappy's last version // Create a schema that is the same as pappy's last version
cmds := []string { cmds := []string{
` `
CREATE TABLE schema_meta ( CREATE TABLE schema_meta (
version INTEGER NOT NULL version INTEGER NOT NULL
); );
`, `,
` `
INSERT INTO "schema_meta" VALUES(8); INSERT INTO "schema_meta" VALUES(8);
`, `,
` `
CREATE TABLE responses ( CREATE TABLE responses (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
full_response BLOB NOT NULL, full_response BLOB NOT NULL,
@ -130,28 +130,28 @@ func schema8(tx *sql.Tx) error {
); );
`, `,
` `
CREATE TABLE scope ( CREATE TABLE scope (
filter_order INTEGER NOT NULL, filter_order INTEGER NOT NULL,
filter_string TEXT NOT NULL filter_string TEXT NOT NULL
); );
`, `,
` `
CREATE TABLE tags ( CREATE TABLE tags (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
tag TEXT NOT NULL tag TEXT NOT NULL
); );
`, `,
` `
CREATE TABLE tagged ( CREATE TABLE tagged (
reqid INTEGER, reqid INTEGER,
tagid INTEGER tagid INTEGER
); );
`, `,
` `
CREATE TABLE "requests" ( CREATE TABLE "requests" (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
full_request BLOB NOT NULL, full_request BLOB NOT NULL,
@ -167,7 +167,7 @@ func schema8(tx *sql.Tx) error {
); );
`, `,
` `
CREATE TABLE saved_contexts ( CREATE TABLE saved_contexts (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
context_name TEXT UNIQUE, context_name TEXT UNIQUE,
@ -175,7 +175,7 @@ func schema8(tx *sql.Tx) error {
); );
`, `,
` `
CREATE TABLE websocket_messages ( CREATE TABLE websocket_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
parent_request INTEGER REFERENCES requests(id), parent_request INTEGER REFERENCES requests(id),
@ -187,7 +187,7 @@ func schema8(tx *sql.Tx) error {
); );
`, `,
` `
CREATE INDEX ind_start_time ON requests(start_datetime); CREATE INDEX ind_start_time ON requests(start_datetime);
`, `,
} }
@ -240,7 +240,7 @@ func pappyListToStrMessageQuery(f []string) (StrMessageQuery, error) {
} }
type s9ScopeStr struct { type s9ScopeStr struct {
Order int64 Order int64
Filter string Filter string
} }
@ -260,8 +260,8 @@ func (ls s9ScopeSort) Less(i int, j int) bool {
func schema9(tx *sql.Tx) error { func schema9(tx *sql.Tx) error {
/* /*
Converts the floating point timestamps into integers representing nanoseconds from jan 1 1970 Converts the floating point timestamps into integers representing nanoseconds from jan 1 1970
*/ */
// Rename the old requests table // Rename the old requests table
if err := execute(tx, "ALTER TABLE requests RENAME TO requests_old"); err != nil { if err := execute(tx, "ALTER TABLE requests RENAME TO requests_old"); err != nil {
@ -289,13 +289,13 @@ func schema9(tx *sql.Tx) error {
); );
`, `,
` `
INSERT INTO requests INSERT INTO requests
SELECT id, full_request, submitted, response_id, unmangled_id, port, is_ssl, host, plugin_data, 0, 0 SELECT id, full_request, submitted, response_id, unmangled_id, port, is_ssl, host, plugin_data, 0, 0
FROM requests_old FROM requests_old
`, `,
` `
CREATE TABLE websocket_messages ( CREATE TABLE websocket_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
parent_request INTEGER REFERENCES requests(id), parent_request INTEGER REFERENCES requests(id),
@ -307,7 +307,7 @@ func schema9(tx *sql.Tx) error {
); );
`, `,
` `
INSERT INTO websocket_messages INSERT INTO websocket_messages
SELECT id, parent_request, unmangled_id, is_binary, direction, 0, contents SELECT id, parent_request, unmangled_id, is_binary, direction, 0, contents
FROM websocket_messages_old FROM websocket_messages_old
@ -337,13 +337,13 @@ func schema9(tx *sql.Tx) error {
if startDT.Valid { if startDT.Valid {
// Convert to nanoseconds // Convert to nanoseconds
newStartDT = int64(startDT.Float64*1000000000) newStartDT = int64(startDT.Float64 * 1000000000)
} else { } else {
newStartDT = 0 newStartDT = 0
} }
if endDT.Valid { if endDT.Valid {
newEndDT = int64(endDT.Float64*1000000000) newEndDT = int64(endDT.Float64 * 1000000000)
} else { } else {
newEndDT = 0 newEndDT = 0
} }
@ -378,7 +378,7 @@ func schema9(tx *sql.Tx) error {
if sentDT.Valid { if sentDT.Valid {
// Convert to nanoseconds // Convert to nanoseconds
newSentDT = int64(startDT.Float64*1000000000) newSentDT = int64(startDT.Float64 * 1000000000)
} else { } else {
newSentDT = 0 newSentDT = 0
} }

@ -68,12 +68,12 @@ const (
// A struct representing the data to be searched for a pair such as a header or url param // A struct representing the data to be searched for a pair such as a header or url param
type PairValue struct { type PairValue struct {
key string key string
value string value string
} }
type QueryPhrase [][]interface{} // A list of queries. Will match if any queries match the request type QueryPhrase [][]interface{} // A list of queries. Will match if any queries match the request
type MessageQuery []QueryPhrase // A list of phrases. Will match if all the phrases match the request type MessageQuery []QueryPhrase // A list of phrases. Will match if all the phrases match the request
type StrQueryPhrase [][]string type StrQueryPhrase [][]string
type StrMessageQuery []StrQueryPhrase type StrMessageQuery []StrQueryPhrase
@ -451,7 +451,7 @@ func pairValuesFromCookies(cookies []*http.Cookie) []*PairValue {
return pairs return pairs
} }
func pairsToStrings(pairs []*PairValue) ([]string) { func pairsToStrings(pairs []*PairValue) []string {
// Converts a list of pairs into a list of strings containing all keys and values // Converts a list of pairs into a list of strings containing all keys and values
// k1: v1, k2: v2 -> ["k1", "v1", "k2", "v2"] // k1: v1, k2: v2 -> ["k1", "v1", "k2", "v2"]
strs := make([]string, 0) strs := make([]string, 0)
@ -710,9 +710,9 @@ func FieldStrToGo(field string) (SearchField, error) {
return FieldBothCookie, nil return FieldBothCookie, nil
case "tag": case "tag":
return FieldTag, nil return FieldTag, nil
case "after": case "after", "af":
return FieldAfter, nil return FieldAfter, nil
case "before": case "before", "b4":
return FieldBefore, nil return FieldBefore, nil
case "timerange": case "timerange":
return FieldTimeRange, nil return FieldTimeRange, nil

@ -8,7 +8,9 @@ import (
func checkSearch(t *testing.T, req *ProxyRequest, expected bool, args ...interface{}) { func checkSearch(t *testing.T, req *ProxyRequest, expected bool, args ...interface{}) {
checker, err := NewRequestChecker(args...) checker, err := NewRequestChecker(args...)
if err != nil { t.Error(err.Error()) } if err != nil {
t.Error(err.Error())
}
result := checker(req) result := checker(req)
if result != expected { if result != expected {
_, f, ln, _ := runtime.Caller(1) _, f, ln, _ := runtime.Caller(1)
@ -18,9 +20,13 @@ func checkSearch(t *testing.T, req *ProxyRequest, expected bool, args ...interfa
func TestAllSearch(t *testing.T) { func TestAllSearch(t *testing.T) {
checker, err := NewRequestChecker(FieldAll, StrContains, "foo") checker, err := NewRequestChecker(FieldAll, StrContains, "foo")
if err != nil { t.Error(err.Error()) } if err != nil {
t.Error(err.Error())
}
req := testReq() req := testReq()
if !checker(req) { t.Error("Failed to match FieldAll, StrContains") } if !checker(req) {
t.Error("Failed to match FieldAll, StrContains")
}
} }
func TestBodySearch(t *testing.T) { func TestBodySearch(t *testing.T) {

@ -190,4 +190,3 @@ func SignHost(ca tls.Certificate, hosts []string) (cert tls.Certificate, err err
PrivateKey: certpriv, PrivateKey: certpriv,
}, nil }, nil
} }

@ -11,17 +11,19 @@ import (
"sync" "sync"
"time" "time"
_ "github.com/mattn/go-sqlite3"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
_ "github.com/mattn/go-sqlite3"
) )
var REQUEST_SELECT string = "SELECT id, full_request, response_id, unmangled_id, port, is_ssl, host, start_datetime, end_datetime FROM requests" var REQUEST_SELECT string = "SELECT id, full_request, response_id, unmangled_id, port, is_ssl, host, start_datetime, end_datetime FROM requests"
var RESPONSE_SELECT string = "SELECT id, full_response, unmangled_id FROM responses" var RESPONSE_SELECT string = "SELECT id, full_response, unmangled_id FROM responses"
var WS_SELECT string = "SELECT id, parent_request, unmangled_id, is_binary, direction, time_sent, contents FROM websocket_messages" var WS_SELECT string = "SELECT id, parent_request, unmangled_id, is_binary, direction, time_sent, contents FROM websocket_messages"
var inmemIdCounter = IdCounter()
type SQLiteStorage struct { type SQLiteStorage struct {
dbConn *sql.DB dbConn *sql.DB
mtx sync.Mutex mtx sync.Mutex
logger *log.Logger logger *log.Logger
} }
@ -49,7 +51,8 @@ func OpenSQLiteStorage(fname string, logger *log.Logger) (*SQLiteStorage, error)
} }
func InMemoryStorage(logger *log.Logger) (*SQLiteStorage, error) { func InMemoryStorage(logger *log.Logger) (*SQLiteStorage, error) {
return OpenSQLiteStorage("file::memory:?mode=memory&cache=shared", logger) var toOpen = fmt.Sprintf("file:inmem%d:memory:?mode=memory&cache=shared", inmemIdCounter())
return OpenSQLiteStorage(toOpen, logger)
} }
func (rs *SQLiteStorage) Close() { func (rs *SQLiteStorage) Close() {
@ -77,7 +80,7 @@ func reqFromRow(
return nil, fmt.Errorf("id cannot be null") return nil, fmt.Errorf("id cannot be null")
} }
reqDbId := strconv.FormatInt(db_id.Int64, 10) reqDbId := strconv.FormatInt(db_id.Int64, 10)
if db_host.Valid { if db_host.Valid {
host = db_host.String host = db_host.String
} else { } else {
@ -272,7 +275,7 @@ func wsFromRow(tx *sql.Tx, ms *SQLiteStorage, id sql.NullInt64, parent_request s
return wsm, nil return wsm, nil
} }
func addTagsToStorage(tx *sql.Tx, req *ProxyRequest) (error) { func addTagsToStorage(tx *sql.Tx, req *ProxyRequest) error {
// Save the tags // Save the tags
for _, tag := range req.Tags() { for _, tag := range req.Tags() {
var db_tagid sql.NullInt64 var db_tagid sql.NullInt64
@ -340,7 +343,7 @@ func deleteTags(tx *sql.Tx, dbid string) error {
func cleanTags(tx *sql.Tx) error { func cleanTags(tx *sql.Tx) error {
// Delete tags with no associated requests // Delete tags with no associated requests
// not efficient if we have tons of tags, but whatever // not efficient if we have tons of tags, but whatever
stmt, err := tx.Prepare(` stmt, err := tx.Prepare(`
DELETE FROM tags WHERE id NOT IN (SELECT tagid FROM tagged); DELETE FROM tags WHERE id NOT IN (SELECT tagid FROM tagged);
@ -378,7 +381,6 @@ func (ms *SQLiteStorage) saveNewRequest(tx *sql.Tx, req *ProxyRequest) error {
var rspid *string var rspid *string
var unmangledId *string var unmangledId *string
if req.ServerResponse != nil { if req.ServerResponse != nil {
if req.ServerResponse.DbId == "" { if req.ServerResponse.DbId == "" {
return errors.New("response has not been saved yet, cannot save request") return errors.New("response has not been saved yet, cannot save request")
@ -398,7 +400,7 @@ func (ms *SQLiteStorage) saveNewRequest(tx *sql.Tx, req *ProxyRequest) error {
} else { } else {
unmangledId = nil unmangledId = nil
} }
stmt, err := tx.Prepare(` stmt, err := tx.Prepare(`
INSERT INTO requests ( INSERT INTO requests (
full_request, full_request,
@ -478,7 +480,7 @@ func (ms *SQLiteStorage) updateRequest(tx *sql.Tx, req *ProxyRequest) error {
} else { } else {
unmangledId = nil unmangledId = nil
} }
stmt, err := tx.Prepare(` stmt, err := tx.Prepare(`
UPDATE requests SET UPDATE requests SET
full_request=?, full_request=?,
@ -557,20 +559,20 @@ func (ms *SQLiteStorage) loadRequest(tx *sql.Tx, reqid string) (*ProxyRequest, e
var db_end_datetime sql.NullInt64 var db_end_datetime sql.NullInt64
// err = tx.QueryRow(` // err = tx.QueryRow(`
// SELECT // SELECT
// id, full_request, response_id, unmangled_id, port, is_ssl, host, start_datetime, end_datetime // id, full_request, response_id, unmangled_id, port, is_ssl, host, start_datetime, end_datetime
// FROM requests WHERE id=?`, dbId).Scan( // FROM requests WHERE id=?`, dbId).Scan(
err = tx.QueryRow(REQUEST_SELECT + " WHERE id=?", dbId).Scan( err = tx.QueryRow(REQUEST_SELECT+" WHERE id=?", dbId).Scan(
&db_id, &db_id,
&db_full_request, &db_full_request,
&db_response_id, &db_response_id,
&db_unmangled_id, &db_unmangled_id,
&db_port, &db_port,
&db_is_ssl, &db_is_ssl,
&db_host, &db_host,
&db_start_datetime, &db_start_datetime,
&db_end_datetime, &db_end_datetime,
) )
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, fmt.Errorf("Request with id %d does not exist", dbId) return nil, fmt.Errorf("Request with id %d does not exist", dbId)
} else if err != nil { } else if err != nil {
@ -609,7 +611,7 @@ func (ms *SQLiteStorage) loadUnmangledRequest(tx *sql.Tx, reqid string) (*ProxyR
} }
var db_unmangled_id sql.NullInt64 var db_unmangled_id sql.NullInt64
err = tx.QueryRow("SELECT unmangled_id FROM requests WHERE id=?", dbId).Scan(&db_unmangled_id) err = tx.QueryRow("SELECT unmangled_id FROM requests WHERE id=?", dbId).Scan(&db_unmangled_id)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, fmt.Errorf("request has no unmangled version") return nil, fmt.Errorf("request has no unmangled version")
@ -624,7 +626,7 @@ func (ms *SQLiteStorage) loadUnmangledRequest(tx *sql.Tx, reqid string) (*ProxyR
return ms.loadRequest(tx, strconv.FormatInt(db_unmangled_id.Int64, 10)) return ms.loadRequest(tx, strconv.FormatInt(db_unmangled_id.Int64, 10))
} }
func (ms *SQLiteStorage) DeleteRequest(reqid string) (error) { func (ms *SQLiteStorage) DeleteRequest(reqid string) error {
ms.mtx.Lock() ms.mtx.Lock()
defer ms.mtx.Unlock() defer ms.mtx.Unlock()
tx, err := ms.dbConn.Begin() tx, err := ms.dbConn.Begin()
@ -640,7 +642,7 @@ func (ms *SQLiteStorage) DeleteRequest(reqid string) (error) {
return nil return nil
} }
func (ms *SQLiteStorage) deleteRequest(tx *sql.Tx, reqid string) (error) { func (ms *SQLiteStorage) deleteRequest(tx *sql.Tx, reqid string) error {
if reqid == "" { if reqid == "" {
return nil return nil
} }
@ -837,16 +839,16 @@ func (ms *SQLiteStorage) loadResponse(tx *sql.Tx, rspid string) (*ProxyResponse,
if err != nil { if err != nil {
return nil, fmt.Errorf("Invalid response id: %s", rspid) return nil, fmt.Errorf("Invalid response id: %s", rspid)
} }
var db_id sql.NullInt64 var db_id sql.NullInt64
var db_full_response []byte var db_full_response []byte
var db_unmangled_id sql.NullInt64 var db_unmangled_id sql.NullInt64
err = tx.QueryRow(RESPONSE_SELECT + " WHERE id=?", dbId).Scan( err = tx.QueryRow(RESPONSE_SELECT+" WHERE id=?", dbId).Scan(
&db_id, &db_id,
&db_full_response, &db_full_response,
&db_unmangled_id, &db_unmangled_id,
) )
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, fmt.Errorf("Response with id %d does not exist", dbId) return nil, fmt.Errorf("Response with id %d does not exist", dbId)
} else if err != nil { } else if err != nil {
@ -883,7 +885,7 @@ func (ms *SQLiteStorage) loadUnmangledResponse(tx *sql.Tx, rspid string) (*Proxy
} }
var db_unmangled_id sql.NullInt64 var db_unmangled_id sql.NullInt64
err = tx.QueryRow("SELECT unmangled_id FROM responses WHERE id=?", dbId).Scan(&db_unmangled_id) err = tx.QueryRow("SELECT unmangled_id FROM responses WHERE id=?", dbId).Scan(&db_unmangled_id)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, fmt.Errorf("response has no unmangled version") return nil, fmt.Errorf("response has no unmangled version")
@ -1030,7 +1032,7 @@ func (ms *SQLiteStorage) saveNewWSMessage(tx *sql.Tx, req *ProxyRequest, wsm *Pr
insertedId, _ = res.LastInsertId() insertedId, _ = res.LastInsertId()
wsm.DbId = strconv.FormatInt(insertedId, 10) wsm.DbId = strconv.FormatInt(insertedId, 10)
return nil return nil
} }
func (ms *SQLiteStorage) UpdateWSMessage(req *ProxyRequest, wsm *ProxyWSMessage) error { func (ms *SQLiteStorage) UpdateWSMessage(req *ProxyRequest, wsm *ProxyWSMessage) error {
@ -1141,16 +1143,15 @@ func (ms *SQLiteStorage) loadWSMessage(tx *sql.Tx, wsmid string) (*ProxyWSMessag
var db_time_sent sql.NullInt64 var db_time_sent sql.NullInt64
var db_contents []byte var db_contents []byte
err = tx.QueryRow(WS_SELECT+" WHERE id=?", dbId).Scan(
err = tx.QueryRow(WS_SELECT + " WHERE id=?", dbId).Scan( &db_id,
&db_id, &db_parent_request,
&db_parent_request, &db_unmangled_id,
&db_unmangled_id, &db_is_binary,
&db_is_binary, &db_direction,
&db_direction, &db_time_sent,
&db_time_sent, &db_contents,
&db_contents, )
)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, fmt.Errorf("Message with id %d does not exist", dbId) return nil, fmt.Errorf("Message with id %d does not exist", dbId)
} else if err != nil { } else if err != nil {
@ -1197,7 +1198,7 @@ func (ms *SQLiteStorage) loadUnmangledWSMessage(tx *sql.Tx, wsmid string) (*Prox
} }
var db_unmangled_id sql.NullInt64 var db_unmangled_id sql.NullInt64
err = tx.QueryRow("SELECT unmangled_id FROM requests WHERE id=?", dbId).Scan(&db_unmangled_id) err = tx.QueryRow("SELECT unmangled_id FROM requests WHERE id=?", dbId).Scan(&db_unmangled_id)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, fmt.Errorf("message has no unmangled version") return nil, fmt.Errorf("message has no unmangled version")
@ -1403,7 +1404,7 @@ func (ms *SQLiteStorage) search(tx *sql.Tx, limit int64, args ...interface{}) ([
} }
} }
} }
// Can't optimize, just make a checker and do a naive implementation // Can't optimize, just make a checker and do a naive implementation
checker, err := NewRequestChecker(args...) checker, err := NewRequestChecker(args...)
if err != nil { if err != nil {
@ -1432,7 +1433,7 @@ func (ms *SQLiteStorage) checkRequests(tx *sql.Tx, limit int64, checker RequestC
return ms.reqSearchHelper(tx, limit, checker, "") return ms.reqSearchHelper(tx, limit, checker, "")
} }
func (ms *SQLiteStorage) SaveQuery(name string, query MessageQuery) (error) { func (ms *SQLiteStorage) SaveQuery(name string, query MessageQuery) error {
ms.mtx.Lock() ms.mtx.Lock()
defer ms.mtx.Unlock() defer ms.mtx.Unlock()
tx, err := ms.dbConn.Begin() tx, err := ms.dbConn.Begin()
@ -1448,7 +1449,7 @@ func (ms *SQLiteStorage) SaveQuery(name string, query MessageQuery) (error) {
return nil return nil
} }
func (ms *SQLiteStorage) saveQuery(tx *sql.Tx, name string, query MessageQuery) (error) { func (ms *SQLiteStorage) saveQuery(tx *sql.Tx, name string, query MessageQuery) error {
strQuery, err := GoQueryToStrQuery(query) strQuery, err := GoQueryToStrQuery(query)
if err != nil { if err != nil {
return fmt.Errorf("error creating string version of query: %s", err.Error()) return fmt.Errorf("error creating string version of query: %s", err.Error())
@ -1458,7 +1459,7 @@ func (ms *SQLiteStorage) saveQuery(tx *sql.Tx, name string, query MessageQuery)
if err != nil { if err != nil {
return fmt.Errorf("error marshaling query to json: %s", err.Error()) return fmt.Errorf("error marshaling query to json: %s", err.Error())
} }
if err := ms.deleteQuery(tx, name); err != nil { if err := ms.deleteQuery(tx, name); err != nil {
return err return err
} }
@ -1523,7 +1524,7 @@ func (ms *SQLiteStorage) loadQuery(tx *sql.Tx, name string) (MessageQuery, error
return retQuery, nil return retQuery, nil
} }
func (ms *SQLiteStorage) DeleteQuery(name string) (error) { func (ms *SQLiteStorage) DeleteQuery(name string) error {
ms.mtx.Lock() ms.mtx.Lock()
defer ms.mtx.Unlock() defer ms.mtx.Unlock()
tx, err := ms.dbConn.Begin() tx, err := ms.dbConn.Begin()
@ -1539,7 +1540,7 @@ func (ms *SQLiteStorage) DeleteQuery(name string) (error) {
return nil return nil
} }
func (ms *SQLiteStorage) deleteQuery(tx *sql.Tx, name string) (error) { func (ms *SQLiteStorage) deleteQuery(tx *sql.Tx, name string) error {
stmt, err := tx.Prepare(`DELETE FROM saved_contexts WHERE context_name=?;`) stmt, err := tx.Prepare(`DELETE FROM saved_contexts WHERE context_name=?;`)
if err != nil { if err != nil {
return fmt.Errorf("error preparing statement to insert request into database: %s", err.Error()) return fmt.Errorf("error preparing statement to insert request into database: %s", err.Error())
@ -1602,4 +1603,3 @@ func (ms *SQLiteStorage) allSavedQueries(tx *sql.Tx) ([]*SavedQuery, error) {
} }
return savedQueries, nil return savedQueries, nil
} }

@ -1,10 +1,9 @@
package main package main
import ( import (
"testing"
"runtime" "runtime"
"testing"
"time" "time"
"fmt"
) )
func testStorage() *SQLiteStorage { func testStorage() *SQLiteStorage {
@ -20,7 +19,7 @@ func checkTags(t *testing.T, result, expected []string) {
return return
} }
for i, a := range(result) { for i, a := range result {
b := expected[i] b := expected[i]
if a != b { if a != b {
t.Errorf("Failed tag test at %s:%d. Expected %s, got %s", f, ln, expected, result) t.Errorf("Failed tag test at %s:%d. Expected %s, got %s", f, ln, expected, result)
@ -56,7 +55,6 @@ func TestTagging(t *testing.T) {
checkTags(t, req3.Tags(), []string{"bar"}) checkTags(t, req3.Tags(), []string{"bar"})
} }
func TestTime(t *testing.T) { func TestTime(t *testing.T) {
req := testReq() req := testReq()
req.StartDatetime = time.Unix(0, 1234567) req.StartDatetime = time.Unix(0, 1234567)

@ -1,8 +1,8 @@
package main package main
import ( import (
"fmt"
"errors" "errors"
"fmt"
) )
type MessageStorage interface { type MessageStorage interface {
@ -11,7 +11,7 @@ type MessageStorage interface {
// Close the storage // Close the storage
Close() Close()
// Update an existing request in the storage. Requires that it has already been saved // Update an existing request in the storage. Requires that it has already been saved
UpdateRequest(req *ProxyRequest) error UpdateRequest(req *ProxyRequest) error
// Save a new instance of the request in the storage regardless of if it has already been saved // Save a new instance of the request in the storage regardless of if it has already been saved
@ -20,7 +20,7 @@ type MessageStorage interface {
LoadRequest(reqid string) (*ProxyRequest, error) LoadRequest(reqid string) (*ProxyRequest, error)
LoadUnmangledRequest(reqid string) (*ProxyRequest, error) LoadUnmangledRequest(reqid string) (*ProxyRequest, error)
// Delete a request // Delete a request
DeleteRequest(reqid string) (error) DeleteRequest(reqid string) error
// Update an existing response in the storage. Requires that it has already been saved // Update an existing response in the storage. Requires that it has already been saved
UpdateResponse(rsp *ProxyResponse) error UpdateResponse(rsp *ProxyResponse) error
@ -30,7 +30,7 @@ type MessageStorage interface {
LoadResponse(rspid string) (*ProxyResponse, error) LoadResponse(rspid string) (*ProxyResponse, error)
LoadUnmangledResponse(rspid string) (*ProxyResponse, error) LoadUnmangledResponse(rspid string) (*ProxyResponse, error)
// Delete a response // Delete a response
DeleteResponse(rspid string) (error) DeleteResponse(rspid string) error
// Update an existing websocket message in the storage. Requires that it has already been saved // Update an existing websocket message in the storage. Requires that it has already been saved
UpdateWSMessage(req *ProxyRequest, wsm *ProxyWSMessage) error UpdateWSMessage(req *ProxyRequest, wsm *ProxyWSMessage) error
@ -40,7 +40,7 @@ type MessageStorage interface {
LoadWSMessage(wsmid string) (*ProxyWSMessage, error) LoadWSMessage(wsmid string) (*ProxyWSMessage, error)
LoadUnmangledWSMessage(wsmid string) (*ProxyWSMessage, error) LoadUnmangledWSMessage(wsmid string) (*ProxyWSMessage, error)
// Delete a WSMessage // Delete a WSMessage
DeleteWSMessage(wsmid string) (error) DeleteWSMessage(wsmid string) error
// Get list of all the request keys // Get list of all the request keys
RequestKeys() ([]string, error) RequestKeys() ([]string, error)
@ -57,9 +57,9 @@ type MessageStorage interface {
// Query functions // Query functions
AllSavedQueries() ([]*SavedQuery, error) AllSavedQueries() ([]*SavedQuery, error)
SaveQuery(name string, query MessageQuery) (error) SaveQuery(name string, query MessageQuery) error
LoadQuery(name string) (MessageQuery, error) LoadQuery(name string) (MessageQuery, error)
DeleteQuery(name string) (error) DeleteQuery(name string) error
} }
const QueryNotSupported = ConstErr("custom query not supported") const QueryNotSupported = ConstErr("custom query not supported")
@ -67,7 +67,7 @@ const QueryNotSupported = ConstErr("custom query not supported")
type ReqSort []*ProxyRequest type ReqSort []*ProxyRequest
type SavedQuery struct { type SavedQuery struct {
Name string Name string
Query MessageQuery Query MessageQuery
} }
@ -118,7 +118,7 @@ func SaveNewRequest(ms MessageStorage, req *ProxyRequest) error {
} }
if err := ms.SaveNewRequest(req); err != nil { if err := ms.SaveNewRequest(req); err != nil {
return fmt.Errorf("error saving new request: %s", err.Error()) return fmt.Errorf("error saving new request: %s", err.Error())
} }
for _, wsm := range req.WSMessages { for _, wsm := range req.WSMessages {
@ -224,4 +224,3 @@ func UpdateWSMessage(ms MessageStorage, req *ProxyRequest, wsm *ProxyWSMessage)
return ms.UpdateWSMessage(req, wsm) return ms.UpdateWSMessage(req, wsm)
} }
} }

@ -1,11 +1,11 @@
package main package main
import ( import (
"testing"
"runtime" "runtime"
"testing"
) )
func testReq() (*ProxyRequest) { func testReq() *ProxyRequest {
testReq, _ := ProxyRequestFromBytes( testReq, _ := ProxyRequestFromBytes(
[]byte("POST /?foo=bar HTTP/1.1\r\nFoo: Bar\r\nCookie: cookie=choco\r\nContent-Length: 7\r\n\r\nfoo=baz"), []byte("POST /?foo=bar HTTP/1.1\r\nFoo: Bar\r\nCookie: cookie=choco\r\nContent-Length: 7\r\n\r\nfoo=baz"),
"foobaz", "foobaz",

@ -1,16 +1,16 @@
package main package main
import ( import (
"sync"
"log"
"io/ioutil" "io/ioutil"
"log"
"sync"
) )
type ConstErr string type ConstErr string
func (e ConstErr) Error() string { return string(e) } func (e ConstErr) Error() string { return string(e) }
func DuplicateBytes(bs []byte) ([]byte) { func DuplicateBytes(bs []byte) []byte {
retBs := make([]byte, len(bs)) retBs := make([]byte, len(bs))
copy(retBs, bs) copy(retBs, bs)
return retBs return retBs

@ -0,0 +1,169 @@
package main
import (
"encoding/pem"
"html/template"
"net/http"
"strings"
)
// Page template
var MASTER_SRC string = `
<html>
<head>
<title>{{block "title" .}}Puppy Proxy{{end}}</title>
{{block "head" .}}{{end}}
</head>
<body>
{{block "body" .}}{{end}}
</body>
</html>
`
var MASTER_TPL *template.Template
// Page sources
var HOME_SRC string = `
{{define "title"}}Puppy Home{{end}}
{{define "body"}}
<p>Welcome to Puppy<p>
<ul>
<li><a href="/certs">Download CA certificate</a></li>
</ul>
{{end}}
`
var HOME_TPL *template.Template
var CERTS_SRC string = `
{{define "title"}}CA Certificate{{end}}
{{define "body"}}
<p>Downlad this CA cert and add it to your browser to intercept HTTPS messages<p>
<p><a href="/certs/download">Download</p>
{{end}}
`
var CERTS_TPL *template.Template
var RSPVIEW_SRC string = `
{{define "title"}}Response Viewer{{end}}
{{define "head"}}
<script>
function ViewResponse() {
rspid = document.getElementById("rspid").value
window.location.href = "/rsp/" + rspid
}
</script>
{{end}}
{{define "body"}}
<p>Enter a response ID below to view it in the browser<p>
<input type="text" id="rspid"></input><input type="button" onclick="ViewResponse()" value="Go!"></input>
{{end}}
`
var RSPVIEW_TPL *template.Template
func init() {
var err error
MASTER_TPL, err = template.New("master").Parse(MASTER_SRC)
if err != nil {
panic(err)
}
HOME_TPL, err = template.Must(MASTER_TPL.Clone()).Parse(HOME_SRC)
if err != nil {
panic(err)
}
CERTS_TPL, err = template.Must(MASTER_TPL.Clone()).Parse(CERTS_SRC)
if err != nil {
panic(err)
}
RSPVIEW_TPL, err = template.Must(MASTER_TPL.Clone()).Parse(RSPVIEW_SRC)
if err != nil {
panic(err)
}
}
func responseHeaders(w http.ResponseWriter) {
w.Header().Set("Connection", "close")
w.Header().Set("Cache-control", "no-cache")
w.Header().Set("Pragma", "no-cache")
w.Header().Set("Cache-control", "no-store")
w.Header().Set("X-Frame-Options", "DENY")
}
func WebUIHandler(w http.ResponseWriter, r *http.Request, iproxy *InterceptingProxy) {
responseHeaders(w)
parts := strings.Split(r.URL.Path, "/")
switch parts[1] {
case "":
WebUIRootHandler(w, r, iproxy)
case "certs":
WebUICertsHandler(w, r, iproxy, parts[2:])
case "rsp":
WebUIRspHandler(w, r, iproxy, parts[2:])
}
}
func WebUIRootHandler(w http.ResponseWriter, r *http.Request, iproxy *InterceptingProxy) {
err := HOME_TPL.Execute(w, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func WebUICertsHandler(w http.ResponseWriter, r *http.Request, iproxy *InterceptingProxy, path []string) {
if len(path) > 0 && path[0] == "download" {
cert := iproxy.GetCACertificate()
if cert == nil {
w.Write([]byte("no active certs to download"))
return
}
pemData := pem.EncodeToMemory(
&pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Certificate[0],
},
)
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment; filename=\"cert.pem\"")
w.Write(pemData)
return
}
err := CERTS_TPL.Execute(w, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func viewResponseHeaders(w http.ResponseWriter) {
w.Header().Del("Cookie")
}
func WebUIRspHandler(w http.ResponseWriter, r *http.Request, iproxy *InterceptingProxy, path []string) {
if len(path) > 0 {
reqid := path[0]
ms := iproxy.GetProxyStorage()
req, err := ms.LoadRequest(reqid)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
rsp := req.ServerResponse
for k, v := range rsp.Header {
for _, vv := range v {
w.Header().Add(k, vv)
}
}
viewResponseHeaders(w)
w.WriteHeader(rsp.StatusCode)
w.Write(rsp.BodyBytes())
return
}
err := RSPVIEW_TPL.Execute(w, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
Loading…
Cancel
Save