diff --git a/lib/plugins/capture/capture_test.go b/lib/plugins/capture/capture_test.go index 97c31f8..52186b8 100644 --- a/lib/plugins/capture/capture_test.go +++ b/lib/plugins/capture/capture_test.go @@ -62,10 +62,10 @@ func newTestResponseWriter() *testResponseWriter { } func TestListenHTTP(t *testing.T) { - px = proxy.NewProxy() + px = proxy.NewProxy(listenHTTPAddr, "") go func(t *testing.T) { - if err := px.Start(listenHTTPAddr); err != nil { + if err := px.Start(); err != nil { t.Fatal(err) } }(t) diff --git a/lib/plugins/logger/logger_test.go b/lib/plugins/logger/logger_test.go index 3c4d7f2..0623057 100644 --- a/lib/plugins/logger/logger_test.go +++ b/lib/plugins/logger/logger_test.go @@ -61,10 +61,10 @@ func newTestResponseWriter() *testResponseWriter { } func TestListenHTTP(t *testing.T) { - px = proxy.NewProxy() + px = proxy.NewProxy(listenHTTPAddr, "") go func(t *testing.T) { - if err := px.Start(listenHTTPAddr); err != nil { + if err := px.Start(); err != nil { t.Fatal(err) } }(t) diff --git a/lib/proxy/proxy.go b/lib/proxy/proxy.go index a85f6d5..d2be387 100644 --- a/lib/proxy/proxy.go +++ b/lib/proxy/proxy.go @@ -98,6 +98,10 @@ type Proxy struct { interceptors []Interceptor // Logger functions. loggers []Logger + // The proxy's listen address + listenAddr string + // Destination server, if not empty, the proxy acts as a reverse proxy + dstAddr string } // ProxiedRequest struct provides properties for executing a *http.Request and @@ -108,9 +112,14 @@ type ProxiedRequest struct { Response *http.Response } -// NewProxy creates and returns a Proxy reference. -func NewProxy() *Proxy { - return &Proxy{} +// NewProxy creates and returns a Proxy reference. the proxy will listen on the +// given address. if dstAddr is not empty, the proxy will be a reverse proxy, +// and forward request data to dstAddr +func NewProxy(listenAddr, dstServer string) *Proxy { + return &Proxy{ + listenAddr: listenAddr, + dstAddr: dstServer, + } } // Reset clears the list of interfaces. @@ -183,8 +192,13 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { out.URL.Scheme = "https" } + if p.dstAddr != "" { + out.Host = p.dstAddr + } + // Making sure the Host header is present. out.URL.Host = out.Host + log.Println("Host: ", out.Host); out.Header.Add("Host", out.Host) out.Proto = "HTTP/1.1" @@ -280,14 +294,14 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Start creates an HTTP proxy server that listens on the given address. -func (p *Proxy) Start(addr string) error { +func (p *Proxy) Start() error { p.srv = http.Server{ - Addr: addr, + Addr: p.listenAddr, Handler: p, } p.rt = &http.Transport{} - log.Printf("Listening for incoming HTTP client requests on %s.\n", addr) + log.Printf("Listening for incoming HTTP client requests on %s.\n", p.listenAddr) if err := p.srv.ListenAndServe(); err != nil { return err } @@ -309,22 +323,22 @@ func certificateLookup(clientHello *tls.ClientHelloInfo) (*tls.Certificate, erro } // StartTLS creates an HTTPs proxy server that listens on the given address. -func (p *Proxy) StartTLS(addr string) error { +func (p *Proxy) StartTLS() error { p.srv = http.Server{ - Addr: addr, + Addr: p.listenAddr, Handler: p, TLSConfig: &tls.Config{ GetCertificate: certificateLookup, - InsecureSkipVerify: false, + InsecureSkipVerify: true, }, } p.rt = &http.Transport{ TLSClientConfig: &tls.Config{ - InsecureSkipVerify: false, + InsecureSkipVerify: true, }, } - log.Printf("Listening for incoming HTTPs client requests on %s.\n", addr) + log.Printf("Listening for incoming HTTPs client requests on %s.\n", p.listenAddr) gencert.SetRootCACert(os.Getenv(EnvSSLCert)) gencert.SetRootCAKey(os.Getenv(EnvSSLKey)) @@ -336,25 +350,25 @@ func (p *Proxy) StartTLS(addr string) error { } // StartUnix creates an HTTP proxy server that listens on the proxy unix socket and forwards to proxied unix socket. -func (p *Proxy) StartUnix(proxy string, proxied string) error { +func (p *Proxy) StartUnix() error { p.srv = http.Server{ Addr: "http+unix://proxy", Handler: p, } u := &httpunix.Transport{} - u.RegisterLocation("proxied", proxied) + u.RegisterLocation("proxied", p.dstAddr) p.rt = u p.AddDirector(UnixDirector{"http+unix://proxied"}) - log.Printf("Listening for incoming HTTP client requests on %s\n", proxy) + log.Printf("Listening for incoming HTTP client requests on %s\n", p.listenAddr) - os.Remove(proxy) - l, err := net.Listen("unix", proxy) + os.Remove(p.listenAddr) + l, err := net.Listen("unix", p.listenAddr) if err != nil { return err } defer l.Close() - defer os.Remove(proxy) + defer os.Remove(p.listenAddr) return p.srv.Serve(l) } diff --git a/lib/proxy/proxy_test.go b/lib/proxy/proxy_test.go index 40f0d7e..905bdd6 100644 --- a/lib/proxy/proxy_test.go +++ b/lib/proxy/proxy_test.go @@ -125,10 +125,10 @@ func newTestResponseWriter() *testResponseWriter { } func TestListenHTTP(t *testing.T) { - proxy = NewProxy() + proxy = NewProxy(listenHTTPAddr, "") go func(t *testing.T) { - if err := proxy.Start(listenHTTPAddr); err != nil { + if err := proxy.Start(); err != nil { t.Fatal(err) } }(t) @@ -137,10 +137,10 @@ func TestListenHTTP(t *testing.T) { } func TestListenHTTPs(t *testing.T) { - sslProxy = NewProxy() + sslProxy = NewProxy(listenHTTPsAddr, "") go func(t *testing.T) { - if err := sslProxy.StartTLS(listenHTTPsAddr); err != nil { + if err := sslProxy.StartTLS(); err != nil { t.Fatal(err) } }(t) @@ -182,10 +182,10 @@ func TestUnixServer(t *testing.T) { } func TestListenUnix(t *testing.T) { - unixProxy = NewProxy() + unixProxy = NewProxy(listenUnixPath, serverUnixPath) go func(t *testing.T) { - if err := unixProxy.StartUnix(listenUnixPath, serverUnixPath); err != nil { + if err := unixProxy.StartUnix(); err != nil { t.Fatal(err) } }(t) diff --git a/main.go b/main.go index 744f1d2..92828e4 100644 --- a/main.go +++ b/main.go @@ -32,6 +32,7 @@ import ( "github.com/malfunkt/hyperfox/lib/plugins/logger" "github.com/malfunkt/hyperfox/lib/proxy" "upper.io/db.v3" + "strings" ) const version = "1.9.8" @@ -39,7 +40,7 @@ const version = "1.9.8" const ( defaultAddress = `0.0.0.0` defaultPort = uint(1080) - defaultSSLPort = uint(10443) + defaultSSLPort = uint(0) proxyUnixSocket = `/tmp/hyperfox` ) @@ -51,6 +52,12 @@ var ( flagSSLCertFile = flag.String("c", "", "Path to root CA certificate.") flagSSLKeyFile = flag.String("k", "", "Path to root CA key.") flagUnixSocket = flag.String("S", "", "Path to socket.") + flagHTTPProxy = flag.String("httpProxy", "", + "start http reverse proxy, format example: 10.0.0.2:80-192.168.0.2:80, " + + "part before dash is listen address, part after dash is destination address.") + flagHTTPSProxy = flag.String("httpsProxy", "", + "start https reverse proxy, format example: 10.0.0.2:443-192.168.0.2:443, " + + "part before dash is listen address, part after dash is destination address.") ) var ( @@ -60,7 +67,7 @@ var ( func main() { // Banner. - log.Printf("Hyperfox v%s // https://hyperfox.org\n", version) + log.Printf("Hyperfox v%s // https://hyperfox.org", version) log.Printf("By José Carlos Nieto.\n\n") // Parsing command line flags. @@ -80,13 +87,7 @@ func main() { } // Is SSL enabled? - var sslEnabled bool - if *flagSSLPort > 0 && *flagSSLCertFile != "" { - sslEnabled = true - } - - // User requested SSL mode. - if sslEnabled { + if *flagSSLPort > 0 || *flagHTTPSProxy != "" { if *flagSSLCertFile == "" { flag.Usage() log.Fatal("Missing root CA certificate") @@ -101,16 +102,9 @@ func main() { os.Setenv(proxy.EnvSSLKey, *flagSSLKeyFile) } - // Creating proxy. - p := proxy.NewProxy() - - // Attaching logger. - p.AddLogger(logger.Stdout{}) - // Attaching capture tool. res := make(chan capture.Response, 256) - - p.AddBodyWriteCloser(capture.New(res)) + capt := capture.New(res) // Saving captured data with a goroutine. go func() { @@ -139,18 +133,49 @@ func main() { wg.Add(1) go func() { defer wg.Done() - if err := p.Start(fmt.Sprintf("%s:%d", *flagAddress, *flagPort)); err != nil { - log.Fatalf("Failed to bind on the given interface (HTTP): ", err) + addr := fmt.Sprintf("%s:%d", *flagAddress, *flagPort) + if err := startHTTPProxy(addr, "", capt); err != nil { + log.Fatalf("Failed to route mode http proxy, bind address: %s, error: %s", addr, err) + } + }() + } + + // start http reverse proxy + if *flagHTTPProxy != "" { + addrs := strings.Split(*flagHTTPProxy, "-") + if len(addrs) != 2 { + log.Fatalf("'%s' is not valid http proxy address", flagHTTPProxy) + } + wg.Add(1) + go func() { + defer wg.Done() + if err := startHTTPProxy(addrs[0], addrs[1], capt); err != nil { + log.Fatalf("Failed to http reverse proxy, address: %s, error: %s", *flagHTTPProxy, err) + } + }() + } + + if *flagSSLPort > 0 { + wg.Add(1) + go func() { + defer wg.Done() + addr := fmt.Sprintf("%s:%d", *flagAddress, *flagSSLPort) + if err := startHTTPSProxy(addr, "", capt); err != nil { + log.Fatalf("Failed to route mode https proxy, bind address: %s, error: %s", addr, err) } }() } - if sslEnabled { + if *flagHTTPSProxy != "" { + addrs := strings.Split(*flagHTTPSProxy, "-") + if len(addrs) != 2 { + log.Fatalf("'%s' is not valid https proxy address", *flagHTTPSProxy) + } wg.Add(1) go func() { defer wg.Done() - if err := p.StartTLS(fmt.Sprintf("%s:%d", *flagAddress, *flagSSLPort)); err != nil { - log.Fatalf("Failed to bind on the given interface (HTTPS): ", err) + if err := startHTTPSProxy(addrs[0], addrs[1], capt); err != nil { + log.Fatalf("Failed to https reverse proxy, address: %s, error: %s", *flagHTTPSProxy, err) } }() } @@ -159,7 +184,7 @@ func main() { wg.Add(1) go func() { defer wg.Done() - if err := p.StartUnix(proxyUnixSocket, *flagUnixSocket); err != nil { + if err := startUnixSocketProxy(proxyUnixSocket, *flagUnixSocket, capt); err != nil { log.Fatalf("Failed to bind on %s: %s", proxyUnixSocket, err) } }() @@ -167,3 +192,27 @@ func main() { wg.Wait() } + +func startHTTPProxy(listenAddr, dstAddr string, capt *capture.Capture) error { + p := proxy.NewProxy(listenAddr, dstAddr) + // Attaching logger. + p.AddLogger(logger.Stdout{}) + p.AddBodyWriteCloser(capt) + return p.Start() +} + +func startHTTPSProxy(listenAddr, dstAddr string, capt *capture.Capture) error { + p := proxy.NewProxy(listenAddr, dstAddr) + // Attaching logger. + p.AddLogger(logger.Stdout{}) + p.AddBodyWriteCloser(capt) + return p.StartTLS() +} + +func startUnixSocketProxy(listenAddr, dstAddr string, capt *capture.Capture) error { + p := proxy.NewProxy(listenAddr, dstAddr) + // Attaching logger. + p.AddLogger(logger.Stdout{}) + p.AddBodyWriteCloser(capt) + return p.StartUnix() +} \ No newline at end of file