diff --git a/go.mod b/go.mod index 1feff8d..41101ec 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,3 @@ module github.com/dstotijn/gurp go 1.13 - -require github.com/google/martian v2.1.0+incompatible diff --git a/go.sum b/go.sum deleted file mode 100644 index bbcb7d0..0000000 --- a/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= diff --git a/main.go b/main.go index 2001c40..c886107 100644 --- a/main.go +++ b/main.go @@ -2,53 +2,21 @@ package main import ( "crypto/tls" - "crypto/x509" "log" - "net" - "os" - "os/signal" - "time" - - "github.com/google/martian" - "github.com/google/martian/mitm" + "net/http" ) func main() { - p := martian.NewProxy() - defer p.Close() + proxy := NewProxy() - tlsc, err := tls.LoadX509KeyPair("/Users/dstotijn/.ssh/gurp_cert.pem", "/Users/dstotijn/.ssh/gurp_key.pem") - if err != nil { - log.Fatal(err) - } - priv := tlsc.PrivateKey - - x509c, err := x509.ParseCertificate(tlsc.Certificate[0]) - if err != nil { - log.Fatal(err) + s := &http.Server{ + Addr: ":8080", + Handler: proxy, + TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){}, // Disable HTTP/2 } - mc, err := mitm.NewConfig(x509c, priv) - if err != nil { - log.Fatal(err) + err := s.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + log.Fatalf("HTTP server closed: %v", err) } - mc.SetValidity(time.Hour) - mc.SetOrganization("Gurp, Inc.") - - p.SetMITM(mc) - - l, err := net.Listen("tcp", ":8080") - if err != nil { - log.Fatal(err) - } - - go func() { - if err := p.Serve(l); err != nil { - log.Println(err) - } - }() - - sigc := make(chan os.Signal, 1) - signal.Notify(sigc, os.Interrupt, os.Kill) - <-sigc } diff --git a/modd.conf b/modd.conf new file mode 100644 index 0000000..dbe567b --- /dev/null +++ b/modd.conf @@ -0,0 +1,3 @@ +**/*.go { + daemon +sigterm: go run . +} \ No newline at end of file diff --git a/proxy.go b/proxy.go new file mode 100644 index 0000000..6536947 --- /dev/null +++ b/proxy.go @@ -0,0 +1,81 @@ +package main + +import ( + "io" + "log" + "net" + "net/http" + "net/http/httputil" +) + +// Proxy is used to forward HTTP requests. +type Proxy struct { + rp httputil.ReverseProxy +} + +// NewProxy returns a new Proxy. +func NewProxy() *Proxy { + return &Proxy{ + rp: httputil.ReverseProxy{ + Director: func(r *http.Request) { + log.Printf("Director handled URL: %v", r.URL) + }, + }, + } +} + +func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { + log.Printf("ServeHTTP: Received request (host: %v, url: %v", r.Host, r.URL) + + if r.Method == http.MethodConnect { + p.handleConnect(w, r) + return + } + + p.rp.ServeHTTP(w, r) + log.Printf("ServeHTTP: Finished (host: %v, url: %v", r.Host, r.URL) +} + +func (p *Proxy) handleConnect(w http.ResponseWriter, r *http.Request) { + hj, ok := w.(http.Hijacker) + if !ok { + log.Printf("handleConnect: ResponseWriter is not a http.Hijacker (type: %T)", w) + writeError(w, r, http.StatusServiceUnavailable) + return + } + + // destConn is the TCP connection to the destination web server of the + // proxied HTTP request. + destConn, err := net.Dial("tcp", r.Host) + if err != nil { + log.Printf("handleConnect: Connect to destination host failed: %v", err) + writeError(w, r, http.StatusBadGateway) + return + } + defer destConn.Close() + + w.WriteHeader(http.StatusOK) + + // clientConn is the TCP connection to the client. + clientConn, _, err := hj.Hijack() + if err != nil { + log.Printf("handleConnect: Hijack failed: %v", err) + writeError(w, r, http.StatusServiceUnavailable) + return + } + defer clientConn.Close() + + errc := make(chan error, 1) + go tunnelData(destConn, clientConn, errc) + go tunnelData(clientConn, destConn, errc) + <-errc +} + +func tunnelData(dst, src io.ReadWriter, errc chan<- error) { + _, err := io.Copy(dst, src) + errc <- err +} + +func writeError(w http.ResponseWriter, r *http.Request, code int) { + http.Error(w, http.StatusText(code), code) +}