From 52aa568c4810636152595f781e8ba4a35edb7e15 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Mon, 15 Feb 2021 20:17:23 +0100 Subject: [PATCH 01/45] adding http basic auth --- simplehttpserver.go | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index 2e7ad19..86cd06f 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -12,6 +12,9 @@ import ( type options struct { ListenAddress string Folder string + Username string + Password string + Realm string Verbose bool } @@ -21,6 +24,9 @@ func main() { flag.StringVar(&opts.ListenAddress, "listen", "0.0.0.0:8000", "Address:Port") flag.StringVar(&opts.Folder, "path", ".", "Folder") flag.BoolVar(&opts.Verbose, "v", false, "Verbose") + flag.StringVar(&opts.Username, "username", "", "Basic auth username") + flag.StringVar(&opts.Password, "password", "", "Basic auth password") + flag.StringVar(&opts.Realm, "realm", "Please enter username and password", "Realm") flag.Parse() if flag.NArg() > 0 && opts.Folder == "." { @@ -28,7 +34,12 @@ func main() { } log.Printf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) - fmt.Println(http.ListenAndServe(opts.ListenAddress, loglayer(http.FileServer(http.Dir(opts.Folder))))) + layers := loglayer(http.FileServer(http.Dir(opts.Folder))) + if opts.Username != "" || opts.Password != "" { + layers = loglayer(basicauthlayer(http.FileServer(http.Dir(opts.Folder)))) + } + + fmt.Println(http.ListenAndServe(opts.ListenAddress, layers)) } func loglayer(handler http.Handler) http.Handler { @@ -47,6 +58,19 @@ func loglayer(handler http.Handler) http.Handler { }) } +func basicauthlayer(handler http.Handler) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user, pass, ok := r.BasicAuth() + if !ok || user != opts.Username || pass != opts.Password { + w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=\"%s\"", opts.Realm)) + w.WriteHeader(401) + w.Write([]byte("Unauthorized.\n")) + return + } + handler.ServeHTTP(w, r) + }) +} + type loggingResponseWriter struct { http.ResponseWriter statusCode int From 8ad8b6e6efc9dcdb2ac541741531abfdf252f329 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Mon, 15 Feb 2021 20:56:48 +0100 Subject: [PATCH 02/45] adding file upload support --- simplehttpserver.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/simplehttpserver.go b/simplehttpserver.go index 2e7ad19..1bbb135 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -4,15 +4,18 @@ import ( "bytes" "flag" "fmt" + "io/ioutil" "log" "net/http" "net/http/httputil" + "path" ) type options struct { ListenAddress string Folder string Verbose bool + Upload bool } var opts options @@ -20,7 +23,9 @@ var opts options func main() { flag.StringVar(&opts.ListenAddress, "listen", "0.0.0.0:8000", "Address:Port") flag.StringVar(&opts.Folder, "path", ".", "Folder") + flag.BoolVar(&opts.Upload, "upload", false, "Enable upload via PUT") flag.BoolVar(&opts.Verbose, "v", false, "Verbose") + flag.Parse() if flag.NArg() > 0 && opts.Folder == "." { @@ -28,6 +33,9 @@ func main() { } log.Printf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) + if opts.Upload { + log.Println("Upload enabled") + } fmt.Println(http.ListenAndServe(opts.ListenAddress, loglayer(http.FileServer(http.Dir(opts.Folder))))) } @@ -37,6 +45,18 @@ func loglayer(handler http.Handler) http.Handler { lrw := newLoggingResponseWriter(w) handler.ServeHTTP(lrw, r) + // Handles file write if enabled + if opts.Upload && r.Method == http.MethodPut { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Println(err) + } + err = handleUpload(path.Base(r.URL.Path), data) + if err != nil { + log.Println(err) + } + } + if opts.Verbose { headers := new(bytes.Buffer) lrw.Header().Write(headers) //nolint @@ -70,3 +90,7 @@ func (lrw *loggingResponseWriter) WriteHeader(code int) { lrw.statusCode = code lrw.ResponseWriter.WriteHeader(code) } + +func handleUpload(file string, data []byte) error { + return ioutil.WriteFile(file, data, 0655) +} From 6b4eb1c947d95a706fcb2ec58efe32ca790a65ad Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Mon, 15 Feb 2021 21:11:58 +0100 Subject: [PATCH 03/45] adding https support --- simplehttpserver.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index 2e7ad19..1aa7c54 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -12,6 +12,9 @@ import ( type options struct { ListenAddress string Folder string + Certificate string + Key string + HTTPS bool Verbose bool } @@ -20,6 +23,9 @@ var opts options func main() { flag.StringVar(&opts.ListenAddress, "listen", "0.0.0.0:8000", "Address:Port") flag.StringVar(&opts.Folder, "path", ".", "Folder") + flag.BoolVar(&opts.HTTPS, "https", false, "HTTPS") + flag.StringVar(&opts.Certificate, "cert", "", "Certificate") + flag.StringVar(&opts.Key, "key", "", "Key") flag.BoolVar(&opts.Verbose, "v", false, "Verbose") flag.Parse() @@ -28,7 +34,14 @@ func main() { } log.Printf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) - fmt.Println(http.ListenAndServe(opts.ListenAddress, loglayer(http.FileServer(http.Dir(opts.Folder))))) + if opts.HTTPS { + if opts.Certificate == "" || opts.Key == "" { + log.Fatal("Certificate or Key file not specified") + } + fmt.Println(http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, loglayer(http.FileServer(http.Dir(opts.Folder))))) + } else { + fmt.Println(http.ListenAndServe(opts.ListenAddress, loglayer(http.FileServer(http.Dir(opts.Folder))))) + } } func loglayer(handler http.Handler) http.Handler { From 8890cbf2942ecc8cdc73233e4440cc679faefe96 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Mon, 15 Feb 2021 21:12:12 +0100 Subject: [PATCH 04/45] cert generation helper --- gen_cert.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 gen_cert.sh diff --git a/gen_cert.sh b/gen_cert.sh new file mode 100644 index 0000000..0cba96a --- /dev/null +++ b/gen_cert.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +openssl genrsa -out server.key 2048 +openssl ecparam -genkey -name secp384r1 -out server.key +openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 \ No newline at end of file From 0fc0dea2669949c069f5c5678849f3a0fb686f6e Mon Sep 17 00:00:00 2001 From: sandeep <8293321+bauthard@users.noreply.github.com> Date: Tue, 16 Feb 2021 01:53:09 +0530 Subject: [PATCH 05/45] Update build.yaml --- .github/workflows/build.yaml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a157f84..008bca6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,27 +6,18 @@ on: pull_request: jobs: - lint: - name: golangci-lint + golangci-lint: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Run golangci-lint - uses: golangci/golangci-lint-action@v2.2.0 + uses: golangci/golangci-lint-action@v2.4.0 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. version: v1.31 args: --timeout 5m - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - # args: --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true build: name: Build runs-on: ubuntu-latest From ae48cc600f4eb883a6fad3de2f3bd98edaba857f Mon Sep 17 00:00:00 2001 From: sandeep <8293321+bauthard@users.noreply.github.com> Date: Tue, 16 Feb 2021 01:53:09 +0530 Subject: [PATCH 06/45] Update build.yaml --- .github/workflows/build.yaml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a157f84..008bca6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,27 +6,18 @@ on: pull_request: jobs: - lint: - name: golangci-lint + golangci-lint: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Run golangci-lint - uses: golangci/golangci-lint-action@v2.2.0 + uses: golangci/golangci-lint-action@v2.4.0 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. version: v1.31 args: --timeout 5m - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - # args: --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true build: name: Build runs-on: ubuntu-latest From 8c1cc031b82f6459765779a17bbe6a47ac3393fe Mon Sep 17 00:00:00 2001 From: sandeep <8293321+bauthard@users.noreply.github.com> Date: Tue, 16 Feb 2021 01:53:09 +0530 Subject: [PATCH 07/45] Update build.yaml --- .github/workflows/build.yaml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a157f84..008bca6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,27 +6,18 @@ on: pull_request: jobs: - lint: - name: golangci-lint + golangci-lint: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Run golangci-lint - uses: golangci/golangci-lint-action@v2.2.0 + uses: golangci/golangci-lint-action@v2.4.0 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. version: v1.31 args: --timeout 5m - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - # args: --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true build: name: Build runs-on: ubuntu-latest From b71f31edcf32618c0963f8f955cfd0901d538706 Mon Sep 17 00:00:00 2001 From: sandeep <8293321+bauthard@users.noreply.github.com> Date: Tue, 16 Feb 2021 01:53:09 +0530 Subject: [PATCH 08/45] Update build.yaml --- .github/workflows/build.yaml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a157f84..008bca6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,27 +6,18 @@ on: pull_request: jobs: - lint: - name: golangci-lint + golangci-lint: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Run golangci-lint - uses: golangci/golangci-lint-action@v2.2.0 + uses: golangci/golangci-lint-action@v2.4.0 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. version: v1.31 args: --timeout 5m - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - # args: --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true build: name: Build runs-on: ubuntu-latest From ed7e2ee2d2e987c8e173ed38914908913c4c0abe Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Mon, 15 Feb 2021 21:54:25 +0100 Subject: [PATCH 09/45] linting fix --- simplehttpserver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index 86cd06f..b7ff509 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -63,8 +63,8 @@ func basicauthlayer(handler http.Handler) http.HandlerFunc { user, pass, ok := r.BasicAuth() if !ok || user != opts.Username || pass != opts.Password { w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=\"%s\"", opts.Realm)) - w.WriteHeader(401) - w.Write([]byte("Unauthorized.\n")) + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte("Unauthorized.\n")) //nolint return } handler.ServeHTTP(w, r) From 752b822207e2648c22ade4ccf2a4ebf02d26dd7f Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Wed, 17 Feb 2021 15:47:10 +0100 Subject: [PATCH 10/45] adding dep bot --- .github/dependabot.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..4d5617f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,34 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "chore" + include: "scope" + + # Maintain dependencies for go modules + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "chore" + include: "scope" + + # Maintain dependencies for docker + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "chore" + include: "scope" From 526bfd99472cf9dbcda081024b2f4ad5aa7c5ea2 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 19 Feb 2021 20:59:30 +0100 Subject: [PATCH 11/45] refactoring basic auth option --- simplehttpserver.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index e181945..d0314ad 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -9,13 +9,15 @@ import ( "net/http" "net/http/httputil" "path" + "strings" ) type options struct { ListenAddress string Folder string - Username string - Password string + BasicAuth string + username string + password string Realm string Certificate string Key string @@ -34,8 +36,7 @@ func main() { flag.StringVar(&opts.Certificate, "cert", "", "Certificate") flag.StringVar(&opts.Key, "key", "", "Key") flag.BoolVar(&opts.Verbose, "v", false, "Verbose") - flag.StringVar(&opts.Username, "username", "", "Basic auth username") - flag.StringVar(&opts.Password, "password", "", "Basic auth password") + flag.StringVar(&opts.BasicAuth, "basic-auth", "", "Basic auth (username:password)") flag.StringVar(&opts.Realm, "realm", "Please enter username and password", "Realm") flag.Parse() @@ -46,7 +47,14 @@ func main() { log.Printf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) layers := loglayer(http.FileServer(http.Dir(opts.Folder))) - if opts.Username != "" || opts.Password != "" { + if opts.BasicAuth != "" { + baTokens := strings.SplitN(opts.BasicAuth, ":", 1) + if len(baTokens) > 0 { + opts.username = baTokens[0] + } + if len(baTokens) > 1 { + opts.password = baTokens[1] + } layers = loglayer(basicauthlayer(http.FileServer(http.Dir(opts.Folder)))) } @@ -94,7 +102,7 @@ func loglayer(handler http.Handler) http.Handler { func basicauthlayer(handler http.Handler) http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user, pass, ok := r.BasicAuth() - if !ok || user != opts.Username || pass != opts.Password { + if !ok || user != opts.username || pass != opts.password { w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=\"%s\"", opts.Realm)) w.WriteHeader(http.StatusUnauthorized) w.Write([]byte("Unauthorized.\n")) //nolint From 1593493b6a65f6ca659dcfa69fc6235fa203f07e Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 01:35:25 +0100 Subject: [PATCH 12/45] fixing split amount --- simplehttpserver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index d0314ad..809e52c 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -48,7 +48,7 @@ func main() { log.Printf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) layers := loglayer(http.FileServer(http.Dir(opts.Folder))) if opts.BasicAuth != "" { - baTokens := strings.SplitN(opts.BasicAuth, ":", 1) + baTokens := strings.SplitN(opts.BasicAuth, ":", 2) if len(baTokens) > 0 { opts.username = baTokens[0] } From 5a1f3a3ca43b46f80348756b8f293ceb30eaeebb Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 01:39:21 +0100 Subject: [PATCH 13/45] replacing log with gologger --- go.mod | 2 ++ go.sum | 35 +++++++++++++++++++++++++++++++++++ simplehttpserver.go | 16 +++++++++------- 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 go.sum diff --git a/go.mod b/go.mod index 1876095..120667d 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/projectdiscovery/simplehttpserver go 1.15 + +require github.com/projectdiscovery/gologger v1.1.4 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..82f8703 --- /dev/null +++ b/go.sum @@ -0,0 +1,35 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= +github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/projectdiscovery/gologger v1.1.4 h1:qWxGUq7ukHWT849uGPkagPKF3yBPYAsTtMKunQ8O2VI= +github.com/projectdiscovery/gologger v1.1.4/go.mod h1:Bhb6Bdx2PV1nMaFLoXNBmHIU85iROS9y1tBuv7T5pMY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/simplehttpserver.go b/simplehttpserver.go index e181945..98475c6 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -9,6 +9,8 @@ import ( "net/http" "net/http/httputil" "path" + + "github.com/projectdiscovery/gologger" ) type options struct { @@ -44,22 +46,22 @@ func main() { opts.Folder = flag.Args()[0] } - log.Printf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) + gologger.Print().Msgf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) layers := loglayer(http.FileServer(http.Dir(opts.Folder))) if opts.Username != "" || opts.Password != "" { layers = loglayer(basicauthlayer(http.FileServer(http.Dir(opts.Folder)))) } if opts.Upload { - log.Println("Upload enabled") + gologger.Print().Msgf("Upload enabled") } if opts.HTTPS { if opts.Certificate == "" || opts.Key == "" { - log.Fatal("Certificate or Key file not specified") + gologger.Fatal().Msgf("Certificate or Key file not specified") } - fmt.Println(http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers)) + gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers)) } else { - fmt.Println(http.ListenAndServe(opts.ListenAddress, layers)) + gologger.Print().Msgf("%s\n", http.ListenAndServe(opts.ListenAddress, layers)) } } @@ -84,9 +86,9 @@ func loglayer(handler http.Handler) http.Handler { if opts.Verbose { headers := new(bytes.Buffer) lrw.Header().Write(headers) //nolint - log.Printf("\nRemote Address: %s\n%s\n%s %d %s\n%s\n%s\n", r.RemoteAddr, string(fullRequest), r.Proto, lrw.statusCode, http.StatusText(lrw.statusCode), headers.String(), string(lrw.Data)) + gologger.Print().Msgf("\nRemote Address: %s\n%s\n%s %d %s\n%s\n%s\n", r.RemoteAddr, string(fullRequest), r.Proto, lrw.statusCode, http.StatusText(lrw.statusCode), headers.String(), string(lrw.Data)) } else { - log.Printf("%s \"%s %s %s\" %d %d", r.RemoteAddr, r.Method, r.URL, r.Proto, lrw.statusCode, len(lrw.Data)) + gologger.Print().Msgf("%s \"%s %s %s\" %d %d", r.RemoteAddr, r.Method, r.URL, r.Proto, lrw.statusCode, len(lrw.Data)) } }) } From 75383752133ebf463f179c45bf97223e49f7ea07 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 01:52:43 +0100 Subject: [PATCH 14/45] adding auto port increment --- simplehttpserver.go | 62 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index 98475c6..1dbc260 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -2,13 +2,19 @@ package main import ( "bytes" + "errors" "flag" "fmt" "io/ioutil" "log" + "net" "net/http" "net/http/httputil" + "os" "path" + "runtime" + "strconv" + "syscall" "github.com/projectdiscovery/gologger" ) @@ -55,13 +61,29 @@ func main() { if opts.Upload { gologger.Print().Msgf("Upload enabled") } +retry_listen: + var err error if opts.HTTPS { if opts.Certificate == "" || opts.Key == "" { gologger.Fatal().Msgf("Certificate or Key file not specified") } - gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers)) + err = http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers) + } else { - gologger.Print().Msgf("%s\n", http.ListenAndServe(opts.ListenAddress, layers)) + err = http.ListenAndServe(opts.ListenAddress, layers) + gologger.Print().Msgf("%s\n") + } + if err != nil { + if isErrorAddressAlreadyInUse(err) { + gologger.Warning().Msgf("Can't listen on %s: %s - retrying with another port\n", opts.ListenAddress, err) + newListenAddress, err := incPort(opts.ListenAddress) + if err != nil { + gologger.Fatal().Msgf("%s\n", err) + } + opts.ListenAddress = newListenAddress + goto retry_listen + } + gologger.Warning().Msgf("%s\n", err) } } @@ -133,3 +155,39 @@ func (lrw *loggingResponseWriter) WriteHeader(code int) { func handleUpload(file string, data []byte) error { return ioutil.WriteFile(file, data, 0655) } + +func isErrorAddressAlreadyInUse(err error) bool { + var eOsSyscall *os.SyscallError + if !errors.As(err, &eOsSyscall) { + return false + } + var errErrno syscall.Errno // doesn't need a "*" (ptr) because it's already a ptr (uintptr) + if !errors.As(eOsSyscall, &errErrno) { + return false + } + if errErrno == syscall.EADDRINUSE { + return true + } + const WSAEADDRINUSE = 10048 + if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE { + return true + } + return false +} + +func incPort(address string) (string, error) { + addrOrig, portOrig, err := net.SplitHostPort(address) + if err != nil { + return address, err + } + + // increment port + portNumber, err := strconv.Atoi(portOrig) + if err != nil { + return address, err + } + portNumber++ + newPort := strconv.FormatInt(int64(portNumber), 10) + + return net.JoinHostPort(addrOrig, newPort), nil +} From 69d59ffa6ddd82886dc92c461f4b9e7686fc897a Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 01:55:37 +0100 Subject: [PATCH 15/45] fixing lint errors --- simplehttpserver.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index 1dbc260..b366ce9 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -59,19 +59,17 @@ func main() { } if opts.Upload { - gologger.Print().Msgf("Upload enabled") + gologger.Print().Msg("Upload enabled") } retry_listen: var err error if opts.HTTPS { if opts.Certificate == "" || opts.Key == "" { - gologger.Fatal().Msgf("Certificate or Key file not specified") + gologger.Fatal().Msg("Certificate or Key file not specified") } err = http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers) - } else { err = http.ListenAndServe(opts.ListenAddress, layers) - gologger.Print().Msgf("%s\n") } if err != nil { if isErrorAddressAlreadyInUse(err) { From bfe28042add32b69c9a6687e351e8468b9caac3f Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 02:51:41 +0100 Subject: [PATCH 16/45] adding self certificate generation --- pkg/sslcert/options.go | 21 ++++++ pkg/sslcert/sslcert.go | 151 +++++++++++++++++++++++++++++++++++++++ pkg/sslcert/tlsconfig.go | 19 +++++ simplehttpserver.go | 19 ++++- 4 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 pkg/sslcert/options.go create mode 100644 pkg/sslcert/sslcert.go create mode 100644 pkg/sslcert/tlsconfig.go diff --git a/pkg/sslcert/options.go b/pkg/sslcert/options.go new file mode 100644 index 0000000..54cbc0d --- /dev/null +++ b/pkg/sslcert/options.go @@ -0,0 +1,21 @@ +package sslcert + +import "time" + +type Options struct { + Host string // Comma-separated hostnames and IPs to generate a certificate for") + Organization string + ValidFrom string // Creation date formatted as Jan 1 15:04:05 2011 + ValidFor time.Duration // 365*24*time.Hour Duration that certificate is valid for + IsCA bool // whether this cert should be its own Certificate Authority + RSABits int // 2048 Size of RSA key to generate. Ignored if --ecdsa-curve is set + EcdsaCurve string // ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521 + Ed25519Key bool // Generate an Ed25519 key +} + +var DefaultOptions = Options{ + ValidFor: time.Duration(365 * 24 * time.Hour), + IsCA: false, + RSABits: 2048, + Organization: "Acme Co", +} diff --git a/pkg/sslcert/sslcert.go b/pkg/sslcert/sslcert.go new file mode 100644 index 0000000..6469b78 --- /dev/null +++ b/pkg/sslcert/sslcert.go @@ -0,0 +1,151 @@ +// Package sslcert contains a reworked version of https://golang.org/src/crypto/tls/generate_cert.go +package sslcert + +import ( + "bufio" + "bytes" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "errors" + "fmt" + "math/big" + "net" + "strings" + "time" +) + +func pubKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + case ed25519.PrivateKey: + return k.Public().(ed25519.PublicKey) + default: + return nil + } +} + +func Generate(options Options) (privateKey, publicKey []byte, err error) { + if options.Host == "" { + return nil, nil, errors.New("Empty host value") + } + + var priv interface{} + switch options.EcdsaCurve { + case "": + if options.Ed25519Key { + _, priv, err = ed25519.GenerateKey(rand.Reader) + } else { + priv, err = rsa.GenerateKey(rand.Reader, options.RSABits) + } + case "P224": + priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + case "P256": + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case "P384": + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + case "P521": + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + default: + err = fmt.Errorf("Unrecognized elliptic curve: %q", options.EcdsaCurve) + return + } + if err != nil { + err = fmt.Errorf("Failed to generate private key: %v", err) + return + } + + // ECDSA, ED25519 and RSA subject keys should have the DigitalSignature + // KeyUsage bits set in the x509.Certificate template + keyUsage := x509.KeyUsageDigitalSignature + // Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In + // the context of TLS this KeyUsage is particular to RSA key exchange and + // authentication. + if _, isRSA := priv.(*rsa.PrivateKey); isRSA { + keyUsage |= x509.KeyUsageKeyEncipherment + } + + var notBefore time.Time + if len(options.ValidFrom) == 0 { + notBefore = time.Now() + } else { + notBefore, err = time.Parse("Jan 2 15:04:05 2006", options.ValidFrom) + if err != nil { + err = fmt.Errorf("Failed to parse creation date: %v", err) + return + } + } + + notAfter := notBefore.Add(options.ValidFor) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + err = fmt.Errorf("Failed to generate serial number: %v", err) + return + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{options.Organization}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: keyUsage, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(options.Host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + if options.IsCA { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey(priv), priv) + if err != nil { + err = fmt.Errorf("Failed to create certificate: %v", err) + return + } + + var pubKeyBuf bytes.Buffer + pubKeyBufb := bufio.NewWriter(&pubKeyBuf) + err = pem.Encode(pubKeyBufb, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + if err != nil { + err = fmt.Errorf("Failed to write data to cert.pem: %v", err) + return + } + + privBytes, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + err = fmt.Errorf("Unable to marshal private key: %v", err) + return + } + var privKeyBuf bytes.Buffer + privKeyBufb := bufio.NewWriter(&privKeyBuf) + err = pem.Encode(privKeyBufb, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}) + if err != nil { + err = fmt.Errorf("Failed to write data to key.pem: %v", err) + return + } + + return pubKeyBuf.Bytes(), privKeyBuf.Bytes(), nil +} diff --git a/pkg/sslcert/tlsconfig.go b/pkg/sslcert/tlsconfig.go new file mode 100644 index 0000000..e2bbe92 --- /dev/null +++ b/pkg/sslcert/tlsconfig.go @@ -0,0 +1,19 @@ +package sslcert + +import ( + "crypto/tls" +) + +func NewTLSConfig(options Options) (*tls.Config, error) { + pubKey, privKey, err := Generate(options) + if err != nil { + return nil, err + } + + cert, err := tls.X509KeyPair(pubKey, privKey) + if err != nil { + return nil, err + } + + return &tls.Config{Certificates: []tls.Certificate{cert}}, nil +} diff --git a/simplehttpserver.go b/simplehttpserver.go index 98475c6..803d02c 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -11,6 +11,7 @@ import ( "path" "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/simplehttpserver/pkg/sslcert" ) type options struct { @@ -21,6 +22,7 @@ type options struct { Realm string Certificate string Key string + Domain string HTTPS bool Verbose bool Upload bool @@ -35,6 +37,7 @@ func main() { flag.BoolVar(&opts.HTTPS, "https", false, "HTTPS") flag.StringVar(&opts.Certificate, "cert", "", "Certificate") flag.StringVar(&opts.Key, "key", "", "Key") + flag.StringVar(&opts.Domain, "domain", "", "Domain") flag.BoolVar(&opts.Verbose, "v", false, "Verbose") flag.StringVar(&opts.Username, "username", "", "Basic auth username") flag.StringVar(&opts.Password, "password", "", "Basic auth password") @@ -57,9 +60,21 @@ func main() { } if opts.HTTPS { if opts.Certificate == "" || opts.Key == "" { - gologger.Fatal().Msgf("Certificate or Key file not specified") + tlsOptions := sslcert.DefaultOptions + tlsOptions.Host = opts.Domain + tlsConfig, err := sslcert.NewTLSConfig(tlsOptions) + if err != nil { + gologger.Fatal().Msgf("%s\n", err) + } + httpServer := &http.Server{ + Addr: opts.ListenAddress, + TLSConfig: tlsConfig, + } + httpServer.Handler = layers + gologger.Print().Msgf("%s\n", httpServer.ListenAndServeTLS("", "")) + } else { + gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers)) } - gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers)) } else { gologger.Print().Msgf("%s\n", http.ListenAndServe(opts.ListenAddress, layers)) } From 86e2a7d6d16d98897b7c44ac943a2b1a2ebae8fb Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 17:48:56 +0100 Subject: [PATCH 17/45] improved output --- simplehttpserver.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index e560113..62cb4e6 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -14,8 +14,8 @@ import ( "path" "runtime" "strconv" - "syscall" "strings" + "syscall" "github.com/projectdiscovery/gologger" ) @@ -53,7 +53,6 @@ func main() { opts.Folder = flag.Args()[0] } - gologger.Print().Msgf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) layers := loglayer(http.FileServer(http.Dir(opts.Folder))) if opts.BasicAuth != "" { baTokens := strings.SplitN(opts.BasicAuth, ":", 2) @@ -65,11 +64,11 @@ func main() { } layers = loglayer(basicauthlayer(http.FileServer(http.Dir(opts.Folder)))) } - if opts.Upload { - gologger.Print().Msg("Upload enabled") + gologger.Print().Msg("Starting service with Upload enabled") } retry_listen: + gologger.Print().Msgf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) var err error if opts.HTTPS { if opts.Certificate == "" || opts.Key == "" { @@ -81,7 +80,7 @@ retry_listen: } if err != nil { if isErrorAddressAlreadyInUse(err) { - gologger.Warning().Msgf("Can't listen on %s: %s - retrying with another port\n", opts.ListenAddress, err) + gologger.Print().Msgf("Can't listen on %s: %s - retrying with another port\n", opts.ListenAddress, err) newListenAddress, err := incPort(opts.ListenAddress) if err != nil { gologger.Fatal().Msgf("%s\n", err) @@ -89,7 +88,7 @@ retry_listen: opts.ListenAddress = newListenAddress goto retry_listen } - gologger.Warning().Msgf("%s\n", err) + gologger.Print().Msgf("%s\n", err) } } From bd654bf7de05652c7cc25912482156fb607db277 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 17:58:09 +0100 Subject: [PATCH 18/45] adding missing buffer flush --- pkg/sslcert/sslcert.go | 2 ++ simplehttpserver.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/sslcert/sslcert.go b/pkg/sslcert/sslcert.go index 6469b78..862ee21 100644 --- a/pkg/sslcert/sslcert.go +++ b/pkg/sslcert/sslcert.go @@ -133,6 +133,7 @@ func Generate(options Options) (privateKey, publicKey []byte, err error) { err = fmt.Errorf("Failed to write data to cert.pem: %v", err) return } + pubKeyBufb.Flush() privBytes, err := x509.MarshalPKCS8PrivateKey(priv) if err != nil { @@ -146,6 +147,7 @@ func Generate(options Options) (privateKey, publicKey []byte, err error) { err = fmt.Errorf("Failed to write data to key.pem: %v", err) return } + privKeyBufb.Flush() return pubKeyBuf.Bytes(), privKeyBuf.Bytes(), nil } diff --git a/simplehttpserver.go b/simplehttpserver.go index 803d02c..f8e5875 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -37,7 +37,7 @@ func main() { flag.BoolVar(&opts.HTTPS, "https", false, "HTTPS") flag.StringVar(&opts.Certificate, "cert", "", "Certificate") flag.StringVar(&opts.Key, "key", "", "Key") - flag.StringVar(&opts.Domain, "domain", "", "Domain") + flag.StringVar(&opts.Domain, "domain", "local.host", "Domain") flag.BoolVar(&opts.Verbose, "v", false, "Verbose") flag.StringVar(&opts.Username, "username", "", "Basic auth username") flag.StringVar(&opts.Password, "password", "", "Basic auth password") From bbee79f0fca7e3577978ad06e95c1704690db3c6 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 20:11:28 +0100 Subject: [PATCH 19/45] adding initial implementation of template based tcp server --- go.mod | 5 +- go.sum | 2 + pkg/tcpserver/responseengine.go | 15 ++++ pkg/tcpserver/rule.go | 22 ++++++ pkg/tcpserver/tcpserver.go | 124 ++++++++++++++++++++++++++++++++ simplehttpserver.go | 20 ++++++ 6 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 pkg/tcpserver/responseengine.go create mode 100644 pkg/tcpserver/rule.go create mode 100644 pkg/tcpserver/tcpserver.go diff --git a/go.mod b/go.mod index 120667d..2bc0442 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module github.com/projectdiscovery/simplehttpserver go 1.15 -require github.com/projectdiscovery/gologger v1.1.4 +require ( + github.com/projectdiscovery/gologger v1.1.4 + gopkg.in/yaml.v2 v2.4.0 +) diff --git a/go.sum b/go.sum index 82f8703..bd1e991 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/tcpserver/responseengine.go b/pkg/tcpserver/responseengine.go new file mode 100644 index 0000000..c3286f8 --- /dev/null +++ b/pkg/tcpserver/responseengine.go @@ -0,0 +1,15 @@ +package tcpserver + +import ( + "errors" +) + +func (t *TCPServer) BuildResponse(data []byte) ([]byte, error) { + // Process all the rules + for _, rule := range t.options.rules { + if rule.matchRegex.Match(data) { + return []byte(rule.Response), nil + } + } + return nil, errors.New("No matched rule") +} diff --git a/pkg/tcpserver/rule.go b/pkg/tcpserver/rule.go new file mode 100644 index 0000000..ba31839 --- /dev/null +++ b/pkg/tcpserver/rule.go @@ -0,0 +1,22 @@ +package tcpserver + +import "regexp" + +type RulesConfiguration struct { + Rules []Rule `yaml:"rules"` +} + +type Rule struct { + Match string `yaml:"match,omitempty"` + matchRegex *regexp.Regexp + Response string `yaml:"response,omitempty"` +} + +func NewRule(match string, response string) (*Rule, error) { + regxp, err := regexp.Compile(match) + if err != nil { + return nil, err + } + + return &Rule{Match: match, matchRegex: regxp, Response: response}, nil +} diff --git a/pkg/tcpserver/tcpserver.go b/pkg/tcpserver/tcpserver.go new file mode 100644 index 0000000..607afe1 --- /dev/null +++ b/pkg/tcpserver/tcpserver.go @@ -0,0 +1,124 @@ +package tcpserver + +import ( + "crypto/tls" + "io/ioutil" + "net" + "time" + + "gopkg.in/yaml.v2" +) + +type Options struct { + Listen string + TLS bool + Certificate string + Key string + Domain string + rules []Rule +} + +type TCPServer struct { + options Options + listener net.Listener +} + +func New(options Options) (*TCPServer, error) { + return &TCPServer{options: options}, nil +} + +func (t *TCPServer) AddRule(rule Rule) error { + t.options.rules = append(t.options.rules, rule) + return nil +} + +func (t *TCPServer) ListenAndServe() error { + listener, err := net.Listen("tcp4", t.options.Listen) + if err != nil { + return err + } + t.listener = listener + return t.run() +} + +func (t *TCPServer) handleConnection(conn net.Conn) error { + defer conn.Close() + + buf := make([]byte, 4096) + for { + conn.SetReadDeadline(time.Now().Add(time.Duration(5 * time.Second))) + _, err := conn.Read(buf) + if err != nil { + return err + } + + resp, err := t.BuildResponse(buf) + if err != nil { + return err + } + + conn.Write(resp) + } +} + +func (t *TCPServer) ListenAndServeTLS() error { + var tlsConfig *tls.Config + if t.options.Certificate != "" && t.options.Key != "" { + cert, err := tls.LoadX509KeyPair(t.options.Certificate, t.options.Key) + if err != nil { + return err + } + tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}} + } else { + tlsOptions := sslcert.DefaultOptions + tlsOptions.Host = opts.Domain + cfg, err := sslcert.NewTLSConfig(tlsOptions) + if err != nil { + return err + } + tlsConfig = cfg + } + + listener, err := tls.Listen("tcp", t.options.Listen, tlsConfig) + if err != nil { + return err + } + t.listener = listener + return t.run() +} + +func (t *TCPServer) run() error { + for { + c, err := t.listener.Accept() + if err != nil { + return err + } + go t.handleConnection(c) + } +} + +func (t *TCPServer) Close() error { + return t.listener.Close() +} + +func (t *TCPServer) LoadTemplate(templatePath string) error { + var config RulesConfiguration + yamlFile, err := ioutil.ReadFile(templatePath) + if err != nil { + return err + } + err = yaml.Unmarshal(yamlFile, &config) + if err != nil { + return err + } + + for _, ruleTemplate := range config.Rules { + rule, err := NewRule(ruleTemplate.Match, ruleTemplate.Response) + if err != nil { + return err + } + t.options.rules = append(t.options.rules, *rule) + } + + return nil +} diff --git a/simplehttpserver.go b/simplehttpserver.go index 6e5f22d..1335ce1 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/simplehttpserver/pkg/tcpserver" ) type options struct { @@ -26,12 +27,18 @@ type options struct { HTTPS bool Verbose bool Upload bool + TCP bool + RulesFile string + TLS bool } var opts options func main() { flag.StringVar(&opts.ListenAddress, "listen", "0.0.0.0:8000", "Address:Port") + flag.BoolVar(&opts.TCP, "tcp", false, "TCP Server") + flag.BoolVar(&opts.TLS, "tls", false, "Enable TCP TLS") + flag.StringVar(&opts.RulesFile, "rules", "", "Rules yaml file") flag.StringVar(&opts.Folder, "path", ".", "Folder") flag.BoolVar(&opts.Upload, "upload", false, "Enable upload via PUT") flag.BoolVar(&opts.HTTPS, "https", false, "HTTPS") @@ -47,6 +54,19 @@ func main() { opts.Folder = flag.Args()[0] } + if opts.TCP { + serverTCP, err := tcpserver.New(tcpserver.Options{Listen: opts.ListenAddress, TLS: opts.TLS, Domain: "local.host"}) + if err != nil { + gologger.Fatal().Msgf("%s\n", err) + } + err = serverTCP.LoadTemplate(opts.RulesFile) + if err != nil { + gologger.Fatal().Msgf("%s\n", err) + } + + gologger.Print().Msgf("%s\n", serverTCP.ListenAndServe()) + } + gologger.Print().Msgf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) layers := loglayer(http.FileServer(http.Dir(opts.Folder))) if opts.BasicAuth != "" { From c8614c543900bda208d63a5103e0a40b1be5fc33 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 02:51:41 +0100 Subject: [PATCH 20/45] fixing conflict --- pkg/sslcert/options.go | 21 ++++++ pkg/sslcert/sslcert.go | 151 +++++++++++++++++++++++++++++++++++++++ pkg/sslcert/tlsconfig.go | 19 +++++ simplehttpserver.go | 19 ++++- 4 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 pkg/sslcert/options.go create mode 100644 pkg/sslcert/sslcert.go create mode 100644 pkg/sslcert/tlsconfig.go diff --git a/pkg/sslcert/options.go b/pkg/sslcert/options.go new file mode 100644 index 0000000..54cbc0d --- /dev/null +++ b/pkg/sslcert/options.go @@ -0,0 +1,21 @@ +package sslcert + +import "time" + +type Options struct { + Host string // Comma-separated hostnames and IPs to generate a certificate for") + Organization string + ValidFrom string // Creation date formatted as Jan 1 15:04:05 2011 + ValidFor time.Duration // 365*24*time.Hour Duration that certificate is valid for + IsCA bool // whether this cert should be its own Certificate Authority + RSABits int // 2048 Size of RSA key to generate. Ignored if --ecdsa-curve is set + EcdsaCurve string // ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521 + Ed25519Key bool // Generate an Ed25519 key +} + +var DefaultOptions = Options{ + ValidFor: time.Duration(365 * 24 * time.Hour), + IsCA: false, + RSABits: 2048, + Organization: "Acme Co", +} diff --git a/pkg/sslcert/sslcert.go b/pkg/sslcert/sslcert.go new file mode 100644 index 0000000..6469b78 --- /dev/null +++ b/pkg/sslcert/sslcert.go @@ -0,0 +1,151 @@ +// Package sslcert contains a reworked version of https://golang.org/src/crypto/tls/generate_cert.go +package sslcert + +import ( + "bufio" + "bytes" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "errors" + "fmt" + "math/big" + "net" + "strings" + "time" +) + +func pubKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + case ed25519.PrivateKey: + return k.Public().(ed25519.PublicKey) + default: + return nil + } +} + +func Generate(options Options) (privateKey, publicKey []byte, err error) { + if options.Host == "" { + return nil, nil, errors.New("Empty host value") + } + + var priv interface{} + switch options.EcdsaCurve { + case "": + if options.Ed25519Key { + _, priv, err = ed25519.GenerateKey(rand.Reader) + } else { + priv, err = rsa.GenerateKey(rand.Reader, options.RSABits) + } + case "P224": + priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + case "P256": + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case "P384": + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + case "P521": + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + default: + err = fmt.Errorf("Unrecognized elliptic curve: %q", options.EcdsaCurve) + return + } + if err != nil { + err = fmt.Errorf("Failed to generate private key: %v", err) + return + } + + // ECDSA, ED25519 and RSA subject keys should have the DigitalSignature + // KeyUsage bits set in the x509.Certificate template + keyUsage := x509.KeyUsageDigitalSignature + // Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In + // the context of TLS this KeyUsage is particular to RSA key exchange and + // authentication. + if _, isRSA := priv.(*rsa.PrivateKey); isRSA { + keyUsage |= x509.KeyUsageKeyEncipherment + } + + var notBefore time.Time + if len(options.ValidFrom) == 0 { + notBefore = time.Now() + } else { + notBefore, err = time.Parse("Jan 2 15:04:05 2006", options.ValidFrom) + if err != nil { + err = fmt.Errorf("Failed to parse creation date: %v", err) + return + } + } + + notAfter := notBefore.Add(options.ValidFor) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + err = fmt.Errorf("Failed to generate serial number: %v", err) + return + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{options.Organization}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: keyUsage, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(options.Host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + if options.IsCA { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey(priv), priv) + if err != nil { + err = fmt.Errorf("Failed to create certificate: %v", err) + return + } + + var pubKeyBuf bytes.Buffer + pubKeyBufb := bufio.NewWriter(&pubKeyBuf) + err = pem.Encode(pubKeyBufb, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + if err != nil { + err = fmt.Errorf("Failed to write data to cert.pem: %v", err) + return + } + + privBytes, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + err = fmt.Errorf("Unable to marshal private key: %v", err) + return + } + var privKeyBuf bytes.Buffer + privKeyBufb := bufio.NewWriter(&privKeyBuf) + err = pem.Encode(privKeyBufb, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}) + if err != nil { + err = fmt.Errorf("Failed to write data to key.pem: %v", err) + return + } + + return pubKeyBuf.Bytes(), privKeyBuf.Bytes(), nil +} diff --git a/pkg/sslcert/tlsconfig.go b/pkg/sslcert/tlsconfig.go new file mode 100644 index 0000000..e2bbe92 --- /dev/null +++ b/pkg/sslcert/tlsconfig.go @@ -0,0 +1,19 @@ +package sslcert + +import ( + "crypto/tls" +) + +func NewTLSConfig(options Options) (*tls.Config, error) { + pubKey, privKey, err := Generate(options) + if err != nil { + return nil, err + } + + cert, err := tls.X509KeyPair(pubKey, privKey) + if err != nil { + return nil, err + } + + return &tls.Config{Certificates: []tls.Certificate{cert}}, nil +} diff --git a/simplehttpserver.go b/simplehttpserver.go index 1335ce1..db3195c 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/simplehttpserver/pkg/sslcert" "github.com/projectdiscovery/simplehttpserver/pkg/tcpserver" ) @@ -24,6 +25,7 @@ type options struct { Realm string Certificate string Key string + Domain string HTTPS bool Verbose bool Upload bool @@ -44,6 +46,7 @@ func main() { flag.BoolVar(&opts.HTTPS, "https", false, "HTTPS") flag.StringVar(&opts.Certificate, "cert", "", "Certificate") flag.StringVar(&opts.Key, "key", "", "Key") + flag.StringVar(&opts.Domain, "domain", "", "Domain") flag.BoolVar(&opts.Verbose, "v", false, "Verbose") flag.StringVar(&opts.BasicAuth, "basic-auth", "", "Basic auth (username:password)") flag.StringVar(&opts.Realm, "realm", "Please enter username and password", "Realm") @@ -85,9 +88,21 @@ func main() { } if opts.HTTPS { if opts.Certificate == "" || opts.Key == "" { - gologger.Fatal().Msgf("Certificate or Key file not specified") + tlsOptions := sslcert.DefaultOptions + tlsOptions.Host = opts.Domain + tlsConfig, err := sslcert.NewTLSConfig(tlsOptions) + if err != nil { + gologger.Fatal().Msgf("%s\n", err) + } + httpServer := &http.Server{ + Addr: opts.ListenAddress, + TLSConfig: tlsConfig, + } + httpServer.Handler = layers + gologger.Print().Msgf("%s\n", httpServer.ListenAndServeTLS("", "")) + } else { + gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers)) } - gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers)) } else { gologger.Print().Msgf("%s\n", http.ListenAndServe(opts.ListenAddress, layers)) } From ca04767a3b974785646d4612270b598e3daa6404 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 17:58:09 +0100 Subject: [PATCH 21/45] adding missing buffer flush --- pkg/sslcert/sslcert.go | 2 ++ simplehttpserver.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/sslcert/sslcert.go b/pkg/sslcert/sslcert.go index 6469b78..862ee21 100644 --- a/pkg/sslcert/sslcert.go +++ b/pkg/sslcert/sslcert.go @@ -133,6 +133,7 @@ func Generate(options Options) (privateKey, publicKey []byte, err error) { err = fmt.Errorf("Failed to write data to cert.pem: %v", err) return } + pubKeyBufb.Flush() privBytes, err := x509.MarshalPKCS8PrivateKey(priv) if err != nil { @@ -146,6 +147,7 @@ func Generate(options Options) (privateKey, publicKey []byte, err error) { err = fmt.Errorf("Failed to write data to key.pem: %v", err) return } + privKeyBufb.Flush() return pubKeyBuf.Bytes(), privKeyBuf.Bytes(), nil } diff --git a/simplehttpserver.go b/simplehttpserver.go index db3195c..fff559f 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -46,7 +46,7 @@ func main() { flag.BoolVar(&opts.HTTPS, "https", false, "HTTPS") flag.StringVar(&opts.Certificate, "cert", "", "Certificate") flag.StringVar(&opts.Key, "key", "", "Key") - flag.StringVar(&opts.Domain, "domain", "", "Domain") + flag.StringVar(&opts.Domain, "domain", "local.host", "Domain") flag.BoolVar(&opts.Verbose, "v", false, "Verbose") flag.StringVar(&opts.BasicAuth, "basic-auth", "", "Basic auth (username:password)") flag.StringVar(&opts.Realm, "realm", "Please enter username and password", "Realm") From 7bfbf59c6bd11b08fde92673d37975d271be4234 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 20:22:29 +0100 Subject: [PATCH 22/45] fixing name --- pkg/tcpserver/tcpserver.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/tcpserver/tcpserver.go b/pkg/tcpserver/tcpserver.go index 607afe1..fc08f03 100644 --- a/pkg/tcpserver/tcpserver.go +++ b/pkg/tcpserver/tcpserver.go @@ -6,6 +6,7 @@ import ( "net" "time" + "github.com/projectdiscovery/simplehttpserver/pkg/sslcert" "gopkg.in/yaml.v2" ) @@ -71,7 +72,7 @@ func (t *TCPServer) ListenAndServeTLS() error { tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}} } else { tlsOptions := sslcert.DefaultOptions - tlsOptions.Host = opts.Domain + tlsOptions.Host = t.options.Domain cfg, err := sslcert.NewTLSConfig(tlsOptions) if err != nil { return err From d728e2fb4dfa8cd35af9220d7a2edb4a478a00b5 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 23 Feb 2021 20:24:06 +0100 Subject: [PATCH 23/45] replacing log => gologger --- simplehttpserver.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index fff559f..47d516f 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -5,7 +5,6 @@ import ( "flag" "fmt" "io/ioutil" - "log" "net/http" "net/http/httputil" "path" @@ -118,11 +117,11 @@ func loglayer(handler http.Handler) http.Handler { if opts.Upload && r.Method == http.MethodPut { data, err := ioutil.ReadAll(r.Body) if err != nil { - log.Println(err) + gologger.Print().Msgf("%s\n", err) } err = handleUpload(path.Base(r.URL.Path), data) if err != nil { - log.Println(err) + gologger.Print().Msgf("%s\n", err) } } From ca921084993fcf7cd7b8b2e606276a2c29e3f87b Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Wed, 24 Feb 2021 23:09:19 +0100 Subject: [PATCH 24/45] removing log package --- simplehttpserver.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index 62cb4e6..50a8af1 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -6,7 +6,6 @@ import ( "flag" "fmt" "io/ioutil" - "log" "net" "net/http" "net/http/httputil" @@ -102,11 +101,11 @@ func loglayer(handler http.Handler) http.Handler { if opts.Upload && r.Method == http.MethodPut { data, err := ioutil.ReadAll(r.Body) if err != nil { - log.Println(err) + gologger.Print().Msgf("%s\n", err) } err = handleUpload(path.Base(r.URL.Path), data) if err != nil { - log.Println(err) + gologger.Print().Msgf("%s\n", err) } } From 9be5b1adc6427e6f6d9ec737d915cdfe6a90be40 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Wed, 24 Feb 2021 23:18:32 +0100 Subject: [PATCH 25/45] removing unused log --- simplehttpserver.go | 1 - 1 file changed, 1 deletion(-) diff --git a/simplehttpserver.go b/simplehttpserver.go index 1ad66e8..f03f258 100644 --- a/simplehttpserver.go +++ b/simplehttpserver.go @@ -6,7 +6,6 @@ import ( "flag" "fmt" "io/ioutil" - "log" "net" "net/http" "net/http/httputil" From c339cd798c40d8ec5ead8ec17b96d7bbac57df49 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 26 Feb 2021 08:42:34 +0100 Subject: [PATCH 26/45] code refactor --- .github/workflows/build.yaml | 4 +- .goreleaser.yml | 2 +- Dockerfile | 4 +- gen_cert.sh | 5 - internal/runner/banner.go | 24 ++++ internal/runner/doc.go | 2 + internal/runner/options.go | 93 ++++++++++++++ internal/runner/runner.go | 70 ++++++++++ pkg/httpserver/authlayer.go | 19 +++ pkg/httpserver/httpserver.go | 120 +++++++++++++++++ pkg/httpserver/loglayer.go | 69 ++++++++++ pkg/httpserver/uploadlayer.go | 7 + pkg/tcpserver/tcpserver.go | 7 + simplehttpserver.go | 233 ---------------------------------- 14 files changed, 416 insertions(+), 243 deletions(-) delete mode 100644 gen_cert.sh create mode 100644 internal/runner/banner.go create mode 100644 internal/runner/doc.go create mode 100644 internal/runner/options.go create mode 100644 internal/runner/runner.go create mode 100644 pkg/httpserver/authlayer.go create mode 100644 pkg/httpserver/httpserver.go create mode 100644 pkg/httpserver/loglayer.go create mode 100644 pkg/httpserver/uploadlayer.go delete mode 100644 simplehttpserver.go diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 008bca6..11d1658 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,8 +32,8 @@ jobs: - name: Test run: go test . - working-directory: . + working-directory: cmd/simplehttpserver/ - name: Build run: go build . - working-directory: . + working-directory: cmd/simplehttpserver/ diff --git a/.goreleaser.yml b/.goreleaser.yml index 50bb74f..f6cb1e2 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,6 +1,6 @@ builds: - binary: simplehttpserver - main: simplehttpserver.go + main: cmd/simplehttpserver/simplehttpserver.go goos: - linux - windows diff --git a/Dockerfile b/Dockerfile index a01590c..4603b20 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM golang:1.14-alpine AS builder +FROM golang:1.16-alpine AS builder RUN apk add --no-cache git -RUN GO111MODULE=auto go get -u -v github.com/projectdiscovery/simplehttpserver +RUN GO111MODULE=auto go get -u -v github.com/projectdiscovery/simplehttpserver/cmd/simplehttpserver FROM alpine:latest COPY --from=builder /go/bin/simplehttpserver /usr/local/bin/ diff --git a/gen_cert.sh b/gen_cert.sh deleted file mode 100644 index 0cba96a..0000000 --- a/gen_cert.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -openssl genrsa -out server.key 2048 -openssl ecparam -genkey -name secp384r1 -out server.key -openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 \ No newline at end of file diff --git a/internal/runner/banner.go b/internal/runner/banner.go new file mode 100644 index 0000000..c8972db --- /dev/null +++ b/internal/runner/banner.go @@ -0,0 +1,24 @@ +package runner + +import "github.com/projectdiscovery/gologger" + +const banner = ` + _ _ _ _ _ + ___(_)_ __ ___ _ __ | | ___| |__ | |_| |_ _ __ ___ ___ _ ____ _____ _ __ +/ __| | '_ ' _ \| '_ \| |/ _ \ '_ \| __| __| '_ \/ __|/ _ \ '__\ \ / / _ \ '__| +\__ \ | | | | | | |_) | | __/ | | | |_| |_| |_) \__ \ __/ | \ V / __/ | +|___/_|_| |_| |_| .__/|_|\___|_| |_|\__|\__| .__/|___/\___|_| \_/ \___|_| + |_| |_| +` + +// Version is the current version +const Version = `0.0.1` + +// showBanner is used to show the banner to the user +func showBanner() { + gologger.Print().Msgf("%s\n", banner) + gologger.Print().Msgf("\t\tprojectdiscovery.io\n\n") + + gologger.Print().Msgf("Use with caution. You are responsible for your actions\n") + gologger.Print().Msgf("Developers assume no liability and are not responsible for any misuse or damage.\n") +} diff --git a/internal/runner/doc.go b/internal/runner/doc.go new file mode 100644 index 0000000..6d6e364 --- /dev/null +++ b/internal/runner/doc.go @@ -0,0 +1,2 @@ +// Package runner contains the internal logic +package runner diff --git a/internal/runner/options.go b/internal/runner/options.go new file mode 100644 index 0000000..3170cd3 --- /dev/null +++ b/internal/runner/options.go @@ -0,0 +1,93 @@ +package runner + +import ( + "flag" + "os" + "strings" + + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/gologger/levels" +) + +type Options struct { + ListenAddress string + Folder string + BasicAuth string + username string + password string + Realm string + TLSCertificate string + TLSKey string + TLSDomain string + HTTPS bool + Verbose bool + EnableUpload bool + EnableTCP bool + RulesFile string + TCPWithTLS bool + Version bool + Silent bool +} + +// ParseOptions parses the command line options for application +func ParseOptions() *Options { + options := &Options{} + flag.StringVar(&options.ListenAddress, "listen", "0.0.0.0:8000", "Address:Port") + flag.BoolVar(&options.EnableTCP, "tcp", false, "TCP Server") + flag.BoolVar(&options.TCPWithTLS, "tls", false, "Enable TCP TLS") + flag.StringVar(&options.RulesFile, "rules", "", "Rules yaml file") + flag.StringVar(&options.Folder, "path", ".", "Folder") + flag.BoolVar(&options.EnableUpload, "upload", false, "Enable upload via PUT") + flag.BoolVar(&options.HTTPS, "https", false, "HTTPS") + flag.StringVar(&options.TLSCertificate, "cert", "", "HTTPS Certificate") + flag.StringVar(&options.TLSKey, "key", "", "HTTPS Certificate Key") + flag.StringVar(&options.TLSDomain, "domain", "local.host", "Domain") + flag.BoolVar(&options.Verbose, "verbose", false, "Verbose") + flag.StringVar(&options.BasicAuth, "basic-auth", "", "Basic auth (username:password)") + flag.StringVar(&options.Realm, "realm", "Please enter username and password", "Realm") + flag.BoolVar(&options.Version, "version", false, "Show version of the software") + flag.BoolVar(&options.Silent, "silent", false, "Show only results in the output") + + flag.Parse() + + // Read the inputs and configure the logging + options.configureOutput() + + showBanner() + + if options.Version { + gologger.Info().Msgf("Current Version: %s\n", Version) + os.Exit(0) + } + + options.validateOptions() + + return options +} + +func (options *Options) validateOptions() { + if flag.NArg() > 0 && options.Folder == "." { + options.Folder = flag.Args()[0] + } + + if options.BasicAuth != "" { + baTokens := strings.SplitN(options.BasicAuth, ":", 2) + if len(baTokens) > 0 { + options.username = baTokens[0] + } + if len(baTokens) > 1 { + options.password = baTokens[1] + } + } +} + +// configureOutput configures the output on the screen +func (options *Options) configureOutput() { + // If the user desires verbose output, show verbose output + if options.Verbose { + gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose) + } + if options.Silent { + gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent) + } +} diff --git a/internal/runner/runner.go b/internal/runner/runner.go new file mode 100644 index 0000000..a5d5230 --- /dev/null +++ b/internal/runner/runner.go @@ -0,0 +1,70 @@ +package runner + +import ( + "github.com/projectdiscovery/simplehttpserver/pkg/httpserver" + "github.com/projectdiscovery/simplehttpserver/pkg/tcpserver" +) + +// Runner is a client for running the enumeration process. +type Runner struct { + options *Options + serverTCP *tcpserver.TCPServer + httpServer *httpserver.HTTPServer +} + +func New(options *Options) (*Runner, error) { + r := Runner{options: options} + if r.options.EnableTCP { + serverTCP, err := tcpserver.New(tcpserver.Options{ + Listen: r.options.ListenAddress, + TLS: r.options.TCPWithTLS, + Domain: "local.host", + Verbose: r.options.Verbose, + }) + if err != nil { + return nil, err + } + err = serverTCP.LoadTemplate(r.options.RulesFile) + if err != nil { + return nil, err + } + r.serverTCP = serverTCP + return &r, nil + } + + httpServer, err := httpserver.New(&httpserver.Options{ + Folder: r.options.Folder, + EnableUpload: r.options.EnableUpload, + ListenAddress: r.options.ListenAddress, + TLS: r.options.HTTPS, + Certificate: r.options.TLSCertificate, + CertificateKey: r.options.TLSKey, + CertificateDomain: r.options.TLSDomain, + BasicAuthUsername: r.options.username, + BasicAuthPassword: r.options.password, + BasicAuthReal: r.options.Realm, + Verbose: r.options.Verbose, + }) + if err != nil { + return nil, err + } + r.httpServer = httpServer + + return &r, nil +} + +func (r *Runner) Run() error { + if r.options.EnableTCP { + return r.serverTCP.ListenAndServe() + } + + if r.options.HTTPS { + return r.httpServer.ListenAndServeTLS() + } + + return r.httpServer.ListenAndServe() +} + +func (r *Runner) Close() error { + return nil +} diff --git a/pkg/httpserver/authlayer.go b/pkg/httpserver/authlayer.go new file mode 100644 index 0000000..f2eff4b --- /dev/null +++ b/pkg/httpserver/authlayer.go @@ -0,0 +1,19 @@ +package httpserver + +import ( + "fmt" + "net/http" +) + +func (t *HTTPServer) basicauthlayer(handler http.Handler) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user, pass, ok := r.BasicAuth() + if !ok || user != t.options.BasicAuthUsername || pass != t.options.BasicAuthPassword { + w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=\"%s\"", t.options.BasicAuthReal)) + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte("Unauthorized.\n")) //nolint + return + } + handler.ServeHTTP(w, r) + }) +} diff --git a/pkg/httpserver/httpserver.go b/pkg/httpserver/httpserver.go new file mode 100644 index 0000000..6e817d3 --- /dev/null +++ b/pkg/httpserver/httpserver.go @@ -0,0 +1,120 @@ +package httpserver + +import ( + "errors" + "net" + "net/http" + "os" + "runtime" + "strconv" + "syscall" + + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/simplehttpserver/pkg/sslcert" +) + +type Options struct { + Folder string + EnableUpload bool + ListenAddress string + TLS bool + Certificate string + CertificateKey string + CertificateDomain string + BasicAuthUsername string + BasicAuthPassword string + BasicAuthReal string + Verbose bool +} + +type HTTPServer struct { + options *Options + layers http.Handler + listener net.Listener +} + +func New(options *Options) (*HTTPServer, error) { + var h HTTPServer + EnableUpload = options.EnableUpload + EnableVerbose = options.Verbose + layers := h.loglayer(http.FileServer(http.Dir(options.Folder))) + if options.BasicAuthUsername != "" || options.BasicAuthPassword != "" { + layers = h.loglayer(h.basicauthlayer(http.FileServer(http.Dir(options.Folder)))) + } + + return &HTTPServer{options: options, layers: layers}, nil +} + +func (t *HTTPServer) ListenAndServe() error { + var err error +retry_listen: + gologger.Print().Msgf("Serving %s on http://%s/...", t.options.Folder, t.options.ListenAddress) + err = http.ListenAndServe(t.options.ListenAddress, t.layers) + if err != nil { + if isErrorAddressAlreadyInUse(err) { + gologger.Print().Msgf("Can't listen on %s: %s - retrying with another port\n", t.options.ListenAddress, err) + newListenAddress, err := incPort(t.options.ListenAddress) + if err != nil { + return err + } + t.options.ListenAddress = newListenAddress + goto retry_listen + } + } + return nil +} + +func (t *HTTPServer) ListenAndServeTLS() error { + gologger.Print().Msgf("Serving %s on https://%s/...", t.options.Folder, t.options.ListenAddress) + if t.options.Certificate == "" || t.options.CertificateKey == "" { + tlsOptions := sslcert.DefaultOptions + tlsOptions.Host = t.options.CertificateDomain + tlsConfig, err := sslcert.NewTLSConfig(tlsOptions) + if err != nil { + return err + } + httpServer := &http.Server{ + Addr: t.options.ListenAddress, + TLSConfig: tlsConfig, + } + httpServer.Handler = t.layers + return httpServer.ListenAndServeTLS("", "") + } + return http.ListenAndServeTLS(t.options.ListenAddress, t.options.Certificate, t.options.CertificateKey, t.layers) +} + +func isErrorAddressAlreadyInUse(err error) bool { + var eOsSyscall *os.SyscallError + if !errors.As(err, &eOsSyscall) { + return false + } + var errErrno syscall.Errno // doesn't need a "*" (ptr) because it's already a ptr (uintptr) + if !errors.As(eOsSyscall, &errErrno) { + return false + } + if errErrno == syscall.EADDRINUSE { + return true + } + const WSAEADDRINUSE = 10048 + if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE { + return true + } + return false +} + +func incPort(address string) (string, error) { + addrOrig, portOrig, err := net.SplitHostPort(address) + if err != nil { + return address, err + } + + // increment port + portNumber, err := strconv.Atoi(portOrig) + if err != nil { + return address, err + } + portNumber++ + newPort := strconv.FormatInt(int64(portNumber), 10) + + return net.JoinHostPort(addrOrig, newPort), nil +} diff --git a/pkg/httpserver/loglayer.go b/pkg/httpserver/loglayer.go new file mode 100644 index 0000000..2d84d9a --- /dev/null +++ b/pkg/httpserver/loglayer.go @@ -0,0 +1,69 @@ +package httpserver + +import ( + "bytes" + "io/ioutil" + "net/http" + "net/http/httputil" + "path" + + "github.com/projectdiscovery/gologger" +) + +// Convenience globals +var ( + EnableUpload bool + EnableVerbose bool +) + +func (t *HTTPServer) loglayer(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fullRequest, _ := httputil.DumpRequest(r, true) + lrw := newLoggingResponseWriter(w) + handler.ServeHTTP(lrw, r) + + // Handles file write if enabled + if EnableUpload && r.Method == http.MethodPut { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + gologger.Print().Msgf("%s\n", err) + } + err = handleUpload(path.Base(r.URL.Path), data) + if err != nil { + gologger.Print().Msgf("%s\n", err) + } + } + + if EnableVerbose { + headers := new(bytes.Buffer) + lrw.Header().Write(headers) //nolint + gologger.Print().Msgf("\nRemote Address: %s\n%s\n%s %d %s\n%s\n%s\n", r.RemoteAddr, string(fullRequest), r.Proto, lrw.statusCode, http.StatusText(lrw.statusCode), headers.String(), string(lrw.Data)) + } else { + gologger.Print().Msgf("%s \"%s %s %s\" %d %d", r.RemoteAddr, r.Method, r.URL, r.Proto, lrw.statusCode, len(lrw.Data)) + } + }) +} + +type loggingResponseWriter struct { + http.ResponseWriter + statusCode int + Data []byte +} + +func newLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter { + return &loggingResponseWriter{w, http.StatusOK, []byte{}} +} + +func (lrw *loggingResponseWriter) Write(data []byte) (int, error) { + lrw.Data = append(lrw.Data, data...) + return lrw.ResponseWriter.Write(data) +} + +func (lrw *loggingResponseWriter) Header() http.Header { + return lrw.ResponseWriter.Header() +} + +func (lrw *loggingResponseWriter) WriteHeader(code int) { + lrw.statusCode = code + lrw.ResponseWriter.WriteHeader(code) +} diff --git a/pkg/httpserver/uploadlayer.go b/pkg/httpserver/uploadlayer.go new file mode 100644 index 0000000..2663fba --- /dev/null +++ b/pkg/httpserver/uploadlayer.go @@ -0,0 +1,7 @@ +package httpserver + +import "io/ioutil" + +func handleUpload(file string, data []byte) error { + return ioutil.WriteFile(file, data, 0655) +} diff --git a/pkg/tcpserver/tcpserver.go b/pkg/tcpserver/tcpserver.go index fc08f03..15be615 100644 --- a/pkg/tcpserver/tcpserver.go +++ b/pkg/tcpserver/tcpserver.go @@ -6,6 +6,7 @@ import ( "net" "time" + "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/simplehttpserver/pkg/sslcert" "gopkg.in/yaml.v2" ) @@ -17,6 +18,7 @@ type Options struct { Key string Domain string rules []Rule + Verbose bool } type TCPServer struct { @@ -34,6 +36,7 @@ func (t *TCPServer) AddRule(rule Rule) error { } func (t *TCPServer) ListenAndServe() error { + gologger.Print().Msgf("Serving %s on tcp://%s", t.options.Listen) listener, err := net.Listen("tcp4", t.options.Listen) if err != nil { return err @@ -53,12 +56,16 @@ func (t *TCPServer) handleConnection(conn net.Conn) error { return err } + gologger.Print().Msgf("%s\n", buf) + resp, err := t.BuildResponse(buf) if err != nil { return err } conn.Write(resp) + + gologger.Print().Msgf("%s\n", resp) } } diff --git a/simplehttpserver.go b/simplehttpserver.go deleted file mode 100644 index f03f258..0000000 --- a/simplehttpserver.go +++ /dev/null @@ -1,233 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "flag" - "fmt" - "io/ioutil" - "net" - "net/http" - "net/http/httputil" - "os" - "path" - "runtime" - "strconv" - "strings" - "syscall" - - "github.com/projectdiscovery/gologger" - "github.com/projectdiscovery/simplehttpserver/pkg/sslcert" - "github.com/projectdiscovery/simplehttpserver/pkg/tcpserver" -) - -type options struct { - ListenAddress string - Folder string - BasicAuth string - username string - password string - Realm string - Certificate string - Key string - Domain string - HTTPS bool - Verbose bool - Upload bool - TCP bool - RulesFile string - TLS bool -} - -var opts options - -func main() { - flag.StringVar(&opts.ListenAddress, "listen", "0.0.0.0:8000", "Address:Port") - flag.BoolVar(&opts.TCP, "tcp", false, "TCP Server") - flag.BoolVar(&opts.TLS, "tls", false, "Enable TCP TLS") - flag.StringVar(&opts.RulesFile, "rules", "", "Rules yaml file") - flag.StringVar(&opts.Folder, "path", ".", "Folder") - flag.BoolVar(&opts.Upload, "upload", false, "Enable upload via PUT") - flag.BoolVar(&opts.HTTPS, "https", false, "HTTPS") - flag.StringVar(&opts.Certificate, "cert", "", "Certificate") - flag.StringVar(&opts.Key, "key", "", "Key") - flag.StringVar(&opts.Domain, "domain", "local.host", "Domain") - flag.BoolVar(&opts.Verbose, "v", false, "Verbose") - flag.StringVar(&opts.BasicAuth, "basic-auth", "", "Basic auth (username:password)") - flag.StringVar(&opts.Realm, "realm", "Please enter username and password", "Realm") - - flag.Parse() - - if flag.NArg() > 0 && opts.Folder == "." { - opts.Folder = flag.Args()[0] - } - - if opts.TCP { - serverTCP, err := tcpserver.New(tcpserver.Options{Listen: opts.ListenAddress, TLS: opts.TLS, Domain: "local.host"}) - if err != nil { - gologger.Fatal().Msgf("%s\n", err) - } - err = serverTCP.LoadTemplate(opts.RulesFile) - if err != nil { - gologger.Fatal().Msgf("%s\n", err) - } - - gologger.Print().Msgf("%s\n", serverTCP.ListenAndServe()) - } - - gologger.Print().Msgf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) - layers := loglayer(http.FileServer(http.Dir(opts.Folder))) - if opts.BasicAuth != "" { - baTokens := strings.SplitN(opts.BasicAuth, ":", 2) - if len(baTokens) > 0 { - opts.username = baTokens[0] - } - if len(baTokens) > 1 { - opts.password = baTokens[1] - } - layers = loglayer(basicauthlayer(http.FileServer(http.Dir(opts.Folder)))) - } - if opts.Upload { - gologger.Print().Msg("Starting service with Upload enabled") - } -retry_listen: - gologger.Print().Msgf("Serving %s on http://%s/...", opts.Folder, opts.ListenAddress) - var err error - if opts.HTTPS { - if opts.Certificate == "" || opts.Key == "" { - tlsOptions := sslcert.DefaultOptions - tlsOptions.Host = opts.Domain - tlsConfig, err := sslcert.NewTLSConfig(tlsOptions) - if err != nil { - gologger.Fatal().Msgf("%s\n", err) - } - httpServer := &http.Server{ - Addr: opts.ListenAddress, - TLSConfig: tlsConfig, - } - httpServer.Handler = layers - gologger.Print().Msgf("%s\n", httpServer.ListenAndServeTLS("", "")) - } else { - gologger.Print().Msgf("%s\n", http.ListenAndServeTLS(opts.ListenAddress, opts.Certificate, opts.Key, layers)) - } - } else { - err = http.ListenAndServe(opts.ListenAddress, layers) - } - if err != nil { - if isErrorAddressAlreadyInUse(err) { - gologger.Print().Msgf("Can't listen on %s: %s - retrying with another port\n", opts.ListenAddress, err) - newListenAddress, err := incPort(opts.ListenAddress) - if err != nil { - gologger.Fatal().Msgf("%s\n", err) - } - opts.ListenAddress = newListenAddress - goto retry_listen - } - gologger.Print().Msgf("%s\n", err) - } -} - -func loglayer(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fullRequest, _ := httputil.DumpRequest(r, true) - lrw := newLoggingResponseWriter(w) - handler.ServeHTTP(lrw, r) - - // Handles file write if enabled - if opts.Upload && r.Method == http.MethodPut { - data, err := ioutil.ReadAll(r.Body) - if err != nil { - gologger.Print().Msgf("%s\n", err) - } - err = handleUpload(path.Base(r.URL.Path), data) - if err != nil { - gologger.Print().Msgf("%s\n", err) - } - } - - if opts.Verbose { - headers := new(bytes.Buffer) - lrw.Header().Write(headers) //nolint - gologger.Print().Msgf("\nRemote Address: %s\n%s\n%s %d %s\n%s\n%s\n", r.RemoteAddr, string(fullRequest), r.Proto, lrw.statusCode, http.StatusText(lrw.statusCode), headers.String(), string(lrw.Data)) - } else { - gologger.Print().Msgf("%s \"%s %s %s\" %d %d", r.RemoteAddr, r.Method, r.URL, r.Proto, lrw.statusCode, len(lrw.Data)) - } - }) -} - -func basicauthlayer(handler http.Handler) http.HandlerFunc { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - user, pass, ok := r.BasicAuth() - if !ok || user != opts.username || pass != opts.password { - w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=\"%s\"", opts.Realm)) - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte("Unauthorized.\n")) //nolint - return - } - handler.ServeHTTP(w, r) - }) -} - -type loggingResponseWriter struct { - http.ResponseWriter - statusCode int - Data []byte -} - -func newLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter { - return &loggingResponseWriter{w, http.StatusOK, []byte{}} -} - -func (lrw *loggingResponseWriter) Write(data []byte) (int, error) { - lrw.Data = append(lrw.Data, data...) - return lrw.ResponseWriter.Write(data) -} - -func (lrw *loggingResponseWriter) Header() http.Header { - return lrw.ResponseWriter.Header() -} - -func (lrw *loggingResponseWriter) WriteHeader(code int) { - lrw.statusCode = code - lrw.ResponseWriter.WriteHeader(code) -} - -func handleUpload(file string, data []byte) error { - return ioutil.WriteFile(file, data, 0655) -} - -func isErrorAddressAlreadyInUse(err error) bool { - var eOsSyscall *os.SyscallError - if !errors.As(err, &eOsSyscall) { - return false - } - var errErrno syscall.Errno // doesn't need a "*" (ptr) because it's already a ptr (uintptr) - if !errors.As(eOsSyscall, &errErrno) { - return false - } - if errErrno == syscall.EADDRINUSE { - return true - } - const WSAEADDRINUSE = 10048 - if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE { - return true - } - return false -} - -func incPort(address string) (string, error) { - addrOrig, portOrig, err := net.SplitHostPort(address) - if err != nil { - return address, err - } - - // increment port - portNumber, err := strconv.Atoi(portOrig) - if err != nil { - return address, err - } - portNumber++ - newPort := strconv.FormatInt(int64(portNumber), 10) - - return net.JoinHostPort(addrOrig, newPort), nil -} From ced71943a55da047f29d9b990bdd0a6bab459a11 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 26 Feb 2021 21:11:20 +0100 Subject: [PATCH 27/45] bind logic refactor --- go.mod | 1 + go.sum | 2 ++ internal/runner/runner.go | 21 +++++++++++++ pkg/binder/binder.go | 32 +++++++++++++++++++ pkg/httpserver/httpserver.go | 60 ++---------------------------------- 5 files changed, 59 insertions(+), 57 deletions(-) create mode 100644 pkg/binder/binder.go diff --git a/go.mod b/go.mod index 2bc0442..f8bb144 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/projectdiscovery/simplehttpserver go 1.15 require ( + github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/projectdiscovery/gologger v1.1.4 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index bd1e991..6757769 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= +github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/projectdiscovery/gologger v1.1.4 h1:qWxGUq7ukHWT849uGPkagPKF3yBPYAsTtMKunQ8O2VI= diff --git a/internal/runner/runner.go b/internal/runner/runner.go index a5d5230..94b8c37 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -1,6 +1,8 @@ package runner import ( + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/simplehttpserver/pkg/binder" "github.com/projectdiscovery/simplehttpserver/pkg/httpserver" "github.com/projectdiscovery/simplehttpserver/pkg/tcpserver" ) @@ -14,6 +16,16 @@ type Runner struct { func New(options *Options) (*Runner, error) { r := Runner{options: options} + // Check if the process can listen on the specified ip:port + if !binder.CanListenOn(r.options.ListenAddress) { + newListenAddress, err := binder.GetRandomListenAddress(r.options.ListenAddress) + if err != nil { + return nil, err + } + gologger.Print().Msgf("Can't listen on %s: %s - Using\n", r.options.ListenAddress, err, newListenAddress) + r.options.ListenAddress = newListenAddress + } + if r.options.EnableTCP { serverTCP, err := tcpserver.New(tcpserver.Options{ Listen: r.options.ListenAddress, @@ -55,16 +67,25 @@ func New(options *Options) (*Runner, error) { func (r *Runner) Run() error { if r.options.EnableTCP { + gologger.Print().Msgf("Serving TCP rule based server on tcp://%s", r.options.ListenAddress) return r.serverTCP.ListenAndServe() } if r.options.HTTPS { + gologger.Print().Msgf("Serving %s on https://%s/...", r.options.Folder, r.options.ListenAddress) return r.httpServer.ListenAndServeTLS() } + gologger.Print().Msgf("Serving %s on http://%s/...", r.options.Folder, r.options.ListenAddress) return r.httpServer.ListenAndServe() } func (r *Runner) Close() error { + if r.serverTCP != nil { + r.serverTCP.Close() + } + if r.httpServer != nil { + r.httpServer.Close() + } return nil } diff --git a/pkg/binder/binder.go b/pkg/binder/binder.go new file mode 100644 index 0000000..2a248a1 --- /dev/null +++ b/pkg/binder/binder.go @@ -0,0 +1,32 @@ +package binder + +import ( + "fmt" + "net" + + "github.com/phayes/freeport" +) + +func CanListenOn(address string) bool { + listener, err := net.Listen("tcp4", address) + if err != nil { + return false + } + defer listener.Close() + return true +} + +func GetRandomListenAddress(currentAddress string) (string, error) { + addrOrig, _, err := net.SplitHostPort(currentAddress) + if err != nil { + return "", err + } + + newPort, err := freeport.GetFreePort() + if err != nil { + return "", err + } + + return net.JoinHostPort(addrOrig, fmt.Sprintf("%d", newPort)), nil + +} diff --git a/pkg/httpserver/httpserver.go b/pkg/httpserver/httpserver.go index 6e817d3..c37355e 100644 --- a/pkg/httpserver/httpserver.go +++ b/pkg/httpserver/httpserver.go @@ -1,15 +1,9 @@ package httpserver import ( - "errors" "net" "net/http" - "os" - "runtime" - "strconv" - "syscall" - "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/simplehttpserver/pkg/sslcert" ) @@ -46,26 +40,10 @@ func New(options *Options) (*HTTPServer, error) { } func (t *HTTPServer) ListenAndServe() error { - var err error -retry_listen: - gologger.Print().Msgf("Serving %s on http://%s/...", t.options.Folder, t.options.ListenAddress) - err = http.ListenAndServe(t.options.ListenAddress, t.layers) - if err != nil { - if isErrorAddressAlreadyInUse(err) { - gologger.Print().Msgf("Can't listen on %s: %s - retrying with another port\n", t.options.ListenAddress, err) - newListenAddress, err := incPort(t.options.ListenAddress) - if err != nil { - return err - } - t.options.ListenAddress = newListenAddress - goto retry_listen - } - } - return nil + return http.ListenAndServe(t.options.ListenAddress, t.layers) } func (t *HTTPServer) ListenAndServeTLS() error { - gologger.Print().Msgf("Serving %s on https://%s/...", t.options.Folder, t.options.ListenAddress) if t.options.Certificate == "" || t.options.CertificateKey == "" { tlsOptions := sslcert.DefaultOptions tlsOptions.Host = t.options.CertificateDomain @@ -83,38 +61,6 @@ func (t *HTTPServer) ListenAndServeTLS() error { return http.ListenAndServeTLS(t.options.ListenAddress, t.options.Certificate, t.options.CertificateKey, t.layers) } -func isErrorAddressAlreadyInUse(err error) bool { - var eOsSyscall *os.SyscallError - if !errors.As(err, &eOsSyscall) { - return false - } - var errErrno syscall.Errno // doesn't need a "*" (ptr) because it's already a ptr (uintptr) - if !errors.As(eOsSyscall, &errErrno) { - return false - } - if errErrno == syscall.EADDRINUSE { - return true - } - const WSAEADDRINUSE = 10048 - if runtime.GOOS == "windows" && errErrno == WSAEADDRINUSE { - return true - } - return false -} - -func incPort(address string) (string, error) { - addrOrig, portOrig, err := net.SplitHostPort(address) - if err != nil { - return address, err - } - - // increment port - portNumber, err := strconv.Atoi(portOrig) - if err != nil { - return address, err - } - portNumber++ - newPort := strconv.FormatInt(int64(portNumber), 10) - - return net.JoinHostPort(addrOrig, newPort), nil +func (t *HTTPServer) Close() error { + return nil } From 254d46196632274f3cc7b6b3a8252d0b7b27d59b Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 26 Feb 2021 21:14:27 +0100 Subject: [PATCH 28/45] misc --- internal/runner/runner.go | 2 +- pkg/tcpserver/tcpserver.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 94b8c37..6603d50 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -22,7 +22,7 @@ func New(options *Options) (*Runner, error) { if err != nil { return nil, err } - gologger.Print().Msgf("Can't listen on %s: %s - Using\n", r.options.ListenAddress, err, newListenAddress) + gologger.Print().Msgf("Can't listen on %s: %s - Using %s\n", r.options.ListenAddress, err, newListenAddress) r.options.ListenAddress = newListenAddress } diff --git a/pkg/tcpserver/tcpserver.go b/pkg/tcpserver/tcpserver.go index 15be615..7d46b3a 100644 --- a/pkg/tcpserver/tcpserver.go +++ b/pkg/tcpserver/tcpserver.go @@ -36,7 +36,6 @@ func (t *TCPServer) AddRule(rule Rule) error { } func (t *TCPServer) ListenAndServe() error { - gologger.Print().Msgf("Serving %s on tcp://%s", t.options.Listen) listener, err := net.Listen("tcp4", t.options.Listen) if err != nil { return err From 80f5074a7d0769b32c31af5c78a77821516990c2 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 26 Feb 2021 22:45:27 +0100 Subject: [PATCH 29/45] adding missing files --- .gitignore | 1 - cmd/simplehttpserver/simplehttpserver.go | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 cmd/simplehttpserver/simplehttpserver.go diff --git a/.gitignore b/.gitignore index dabd7a7..b883f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ *.exe -simplehttpserver diff --git a/cmd/simplehttpserver/simplehttpserver.go b/cmd/simplehttpserver/simplehttpserver.go new file mode 100644 index 0000000..0dbbe6c --- /dev/null +++ b/cmd/simplehttpserver/simplehttpserver.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/projectdiscovery/gologger" + "github.com/projectdiscovery/simplehttpserver/internal/runner" +) + +func main() { + // Parse the command line flags and read config files + options := runner.ParseOptions() + runner, err := runner.New(options) + if err != nil { + gologger.Fatal().Msgf("Could not create runner: %s\n", err) + } + + runner.Run() + runner.Close() +} From fb9d1df1004215a31b89b5ad47911ca43475613e Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 16 Apr 2021 16:06:42 +0200 Subject: [PATCH 30/45] adding common tools to docker --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 4603b20..0919a3c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ RUN apk add --no-cache git RUN GO111MODULE=auto go get -u -v github.com/projectdiscovery/simplehttpserver/cmd/simplehttpserver FROM alpine:latest +RUN apk add --no-cache bind-tools ca-certificates COPY --from=builder /go/bin/simplehttpserver /usr/local/bin/ ENTRYPOINT ["simplehttpserver"] From 49ceaa7165da0d8fe1bb991b1094d0d95b8f17aa Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 16 Apr 2021 16:07:06 +0200 Subject: [PATCH 31/45] removing sslcert + deps bump --- go.mod | 1 + go.sum | 2 + pkg/httpserver/httpserver.go | 8 +- pkg/sslcert/options.go | 21 ----- pkg/sslcert/sslcert.go | 153 ----------------------------------- pkg/sslcert/tlsconfig.go | 19 ----- pkg/tcpserver/tcpserver.go | 2 +- 7 files changed, 7 insertions(+), 199 deletions(-) delete mode 100644 pkg/sslcert/options.go delete mode 100644 pkg/sslcert/sslcert.go delete mode 100644 pkg/sslcert/tlsconfig.go diff --git a/go.mod b/go.mod index f8bb144..1d88f80 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,6 @@ go 1.15 require ( github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/projectdiscovery/gologger v1.1.4 + github.com/projectdiscovery/sslcert v0.0.0-20210416140253-8f56bec1bb5e gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 6757769..aafe4a2 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/projectdiscovery/gologger v1.1.4 h1:qWxGUq7ukHWT849uGPkagPKF3yBPYAsTtMKunQ8O2VI= github.com/projectdiscovery/gologger v1.1.4/go.mod h1:Bhb6Bdx2PV1nMaFLoXNBmHIU85iROS9y1tBuv7T5pMY= +github.com/projectdiscovery/sslcert v0.0.0-20210416140253-8f56bec1bb5e h1:IZa08TUGbU7I0HUb9QQt/8wuu2fPZqfnMXwWhtMxei8= +github.com/projectdiscovery/sslcert v0.0.0-20210416140253-8f56bec1bb5e/go.mod h1:jSp8W5zIkNPxAqVdcoFlfv0K5cqogTe65fMinR0Fvuk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= diff --git a/pkg/httpserver/httpserver.go b/pkg/httpserver/httpserver.go index c37355e..958a6e7 100644 --- a/pkg/httpserver/httpserver.go +++ b/pkg/httpserver/httpserver.go @@ -1,10 +1,9 @@ package httpserver import ( - "net" "net/http" - "github.com/projectdiscovery/simplehttpserver/pkg/sslcert" + "github.com/projectdiscovery/sslcert" ) type Options struct { @@ -22,9 +21,8 @@ type Options struct { } type HTTPServer struct { - options *Options - layers http.Handler - listener net.Listener + options *Options + layers http.Handler } func New(options *Options) (*HTTPServer, error) { diff --git a/pkg/sslcert/options.go b/pkg/sslcert/options.go deleted file mode 100644 index 54cbc0d..0000000 --- a/pkg/sslcert/options.go +++ /dev/null @@ -1,21 +0,0 @@ -package sslcert - -import "time" - -type Options struct { - Host string // Comma-separated hostnames and IPs to generate a certificate for") - Organization string - ValidFrom string // Creation date formatted as Jan 1 15:04:05 2011 - ValidFor time.Duration // 365*24*time.Hour Duration that certificate is valid for - IsCA bool // whether this cert should be its own Certificate Authority - RSABits int // 2048 Size of RSA key to generate. Ignored if --ecdsa-curve is set - EcdsaCurve string // ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521 - Ed25519Key bool // Generate an Ed25519 key -} - -var DefaultOptions = Options{ - ValidFor: time.Duration(365 * 24 * time.Hour), - IsCA: false, - RSABits: 2048, - Organization: "Acme Co", -} diff --git a/pkg/sslcert/sslcert.go b/pkg/sslcert/sslcert.go deleted file mode 100644 index 862ee21..0000000 --- a/pkg/sslcert/sslcert.go +++ /dev/null @@ -1,153 +0,0 @@ -// Package sslcert contains a reworked version of https://golang.org/src/crypto/tls/generate_cert.go -package sslcert - -import ( - "bufio" - "bytes" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "errors" - "fmt" - "math/big" - "net" - "strings" - "time" -) - -func pubKey(priv interface{}) interface{} { - switch k := priv.(type) { - case *rsa.PrivateKey: - return &k.PublicKey - case *ecdsa.PrivateKey: - return &k.PublicKey - case ed25519.PrivateKey: - return k.Public().(ed25519.PublicKey) - default: - return nil - } -} - -func Generate(options Options) (privateKey, publicKey []byte, err error) { - if options.Host == "" { - return nil, nil, errors.New("Empty host value") - } - - var priv interface{} - switch options.EcdsaCurve { - case "": - if options.Ed25519Key { - _, priv, err = ed25519.GenerateKey(rand.Reader) - } else { - priv, err = rsa.GenerateKey(rand.Reader, options.RSABits) - } - case "P224": - priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) - case "P256": - priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - case "P384": - priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - case "P521": - priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) - default: - err = fmt.Errorf("Unrecognized elliptic curve: %q", options.EcdsaCurve) - return - } - if err != nil { - err = fmt.Errorf("Failed to generate private key: %v", err) - return - } - - // ECDSA, ED25519 and RSA subject keys should have the DigitalSignature - // KeyUsage bits set in the x509.Certificate template - keyUsage := x509.KeyUsageDigitalSignature - // Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In - // the context of TLS this KeyUsage is particular to RSA key exchange and - // authentication. - if _, isRSA := priv.(*rsa.PrivateKey); isRSA { - keyUsage |= x509.KeyUsageKeyEncipherment - } - - var notBefore time.Time - if len(options.ValidFrom) == 0 { - notBefore = time.Now() - } else { - notBefore, err = time.Parse("Jan 2 15:04:05 2006", options.ValidFrom) - if err != nil { - err = fmt.Errorf("Failed to parse creation date: %v", err) - return - } - } - - notAfter := notBefore.Add(options.ValidFor) - - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - err = fmt.Errorf("Failed to generate serial number: %v", err) - return - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{options.Organization}, - }, - NotBefore: notBefore, - NotAfter: notAfter, - - KeyUsage: keyUsage, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - hosts := strings.Split(options.Host, ",") - for _, h := range hosts { - if ip := net.ParseIP(h); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) - } else { - template.DNSNames = append(template.DNSNames, h) - } - } - - if options.IsCA { - template.IsCA = true - template.KeyUsage |= x509.KeyUsageCertSign - } - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey(priv), priv) - if err != nil { - err = fmt.Errorf("Failed to create certificate: %v", err) - return - } - - var pubKeyBuf bytes.Buffer - pubKeyBufb := bufio.NewWriter(&pubKeyBuf) - err = pem.Encode(pubKeyBufb, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - if err != nil { - err = fmt.Errorf("Failed to write data to cert.pem: %v", err) - return - } - pubKeyBufb.Flush() - - privBytes, err := x509.MarshalPKCS8PrivateKey(priv) - if err != nil { - err = fmt.Errorf("Unable to marshal private key: %v", err) - return - } - var privKeyBuf bytes.Buffer - privKeyBufb := bufio.NewWriter(&privKeyBuf) - err = pem.Encode(privKeyBufb, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}) - if err != nil { - err = fmt.Errorf("Failed to write data to key.pem: %v", err) - return - } - privKeyBufb.Flush() - - return pubKeyBuf.Bytes(), privKeyBuf.Bytes(), nil -} diff --git a/pkg/sslcert/tlsconfig.go b/pkg/sslcert/tlsconfig.go deleted file mode 100644 index e2bbe92..0000000 --- a/pkg/sslcert/tlsconfig.go +++ /dev/null @@ -1,19 +0,0 @@ -package sslcert - -import ( - "crypto/tls" -) - -func NewTLSConfig(options Options) (*tls.Config, error) { - pubKey, privKey, err := Generate(options) - if err != nil { - return nil, err - } - - cert, err := tls.X509KeyPair(pubKey, privKey) - if err != nil { - return nil, err - } - - return &tls.Config{Certificates: []tls.Certificate{cert}}, nil -} diff --git a/pkg/tcpserver/tcpserver.go b/pkg/tcpserver/tcpserver.go index 7d46b3a..f78f876 100644 --- a/pkg/tcpserver/tcpserver.go +++ b/pkg/tcpserver/tcpserver.go @@ -7,7 +7,7 @@ import ( "time" "github.com/projectdiscovery/gologger" - "github.com/projectdiscovery/simplehttpserver/pkg/sslcert" + "github.com/projectdiscovery/sslcert" "gopkg.in/yaml.v2" ) From d068ebe21e9a746612bc357a8cda8fa29d7720c6 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 16 Apr 2021 16:40:25 +0200 Subject: [PATCH 32/45] using go 1.14 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 1d88f80..f4c4a9d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/projectdiscovery/simplehttpserver -go 1.15 +go 1.14 require ( github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 From e55d3feac94109c6eded54b0a6a98edd7933921e Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 16 Apr 2021 16:41:26 +0200 Subject: [PATCH 33/45] updated readme --- README.md | 119 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f88164f..00daf9c 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,14 @@ [![Docker Images](https://img.shields.io/docker/pulls/projectdiscovery/simplehttpserver.svg)](https://hub.docker.com/r/projectdiscovery/simplehttpserver) [![Chat on Discord](https://img.shields.io/discord/695645237418131507.svg?logo=discord)](https://discord.gg/KECAGdH) -simplehttpserver is a go enhanced version of the well known python simplehttpserver. +simplehttpserver is a go enhanced version of the well known python simplehttpserver with in addition a fully customizable TCP server, both supporting TLS. # Resources - [Features](#features) - [Usage](#usage) - [Installation Instructions](#installation-instructions) -- [Running simplehttpserver](#running-simplehttpserver-in-the-current-folder ) +- [Running simplehttpserver](#running-simplehttpserver-in-the-current-folder) - [Thanks](#thanks) # Features @@ -28,9 +28,11 @@ simplehttpserver is a go enhanced version of the well known python simplehttpser
- - File server in arbitrary directory - - Full request/response dump - - Configurable ip address and listening port +- HTTPS support +- File server in arbitrary directory +- Full request/response dump +- Configurable ip address and listening port +- Configurable HTTP/TCP server with customizable response via YAML template # Installation Instructions @@ -70,11 +72,23 @@ simplehttpserver -h This will display help for the tool. Here are all the switches it supports. -| Flag | Description | Example | -| ------ | ---------------------------------------------------- | --------------------------------------- | -| listen | Configure listening ip:port (default 127.0.0.1:8000) | simplehttpserver -listen 127.0.0.1:8000 | -| path | Fileserver folder (default current directory) | simplehttpserver -path /var/docs | -| v | Verbose (dump request/response, default false) | simplehttpserver -v | +| Flag | Description | Example | +| ----------- | -------------------------------------------------------------------- | ------------------------------------------------- | +| listen | Configure listening ip:port (default 127.0.0.1:8000) | simplehttpserver -listen 127.0.0.1:8000 | +| path | Fileserver folder (default current directory) | simplehttpserver -path /var/docs | +| verbose | Verbose (dump request/response, default false) | simplehttpserver -v | +| tcp | TCP server (default 127.0.0.1:8000) | simplehttpserver -tcp 127.0.0.1:8000 | +| tls | Enable TLS for TCP server | simplehttpserver -tls | +| rules | File containing yaml rules | simplehttpserver -rules rule.yaml | | +| upload | Enable file upload in case of http server | simplehttpserver -upload | +| https | Enable HTTPS in case of http server | simplehttpserver -https | +| cert | HTTPS/TLS certificate (self generated if not specified) | simplehttpserver -cert cert.pem | +| key | HTTPS/TLS certificate private key (self generated if not specified) | simplehttpserver -key cert.key | +| domain | Domain name to use for the self-generated certificate | simplehttpserver -domain projectdiscovery.io | +| basic-auth | Basic auth (username:password) | simplehttpserver -basic-auth user:password | +| realm | Basic auth message | simplehttpserver -realm "insert the credentials" | +| version | Show version | simplehttpserver -version | +| silent | Show only results | simplehttpserver -silent | ### Running simplehttpserver in the current folder @@ -87,6 +101,91 @@ This will run the tool exposing the current directory on port 8000 2021/01/11 21:41:15 [::1]:50181 "GET /favicon.ico HTTP/1.1" 404 19 ``` +### Running simplehttpserver in the current folder with HTTPS + +This will run the tool exposing the current directory on port 8000 over HTTPS with user provided certificate: + +```sh +▶ simplehttpserver -https -cert cert.pen -key cert.key +2021/01/11 21:40:48 Serving . on http://0.0.0.0:8000/... +2021/01/11 21:41:15 [::1]:50181 "GET / HTTP/1.1" 200 383 +2021/01/11 21:41:15 [::1]:50181 "GET /favicon.ico HTTP/1.1" 404 19 +``` + +Instead, to run with self-signed certificate and specific domain name: +```sh +▶ simplehttpserver -https -domain localhost +2021/01/11 21:40:48 Serving . on http://0.0.0.0:8000/... +2021/01/11 21:41:15 [::1]:50181 "GET / HTTP/1.1" 200 383 +2021/01/11 21:41:15 [::1]:50181 "GET /favicon.ico HTTP/1.1" 404 19 +``` + +### Running simplehttpserver with basic auth and file upload + +This will run the tool and will request the user to enter username and password before authorizing file uploads + +```sh +▶ simplehttpserver -basic-auth root:root -upload +2021/01/11 21:40:48 Serving . on http://0.0.0.0:8000/... +``` + +To upload files use the following curl request with basic auth header: +```sh +▶ curl -v --user 'root:root' --upload-file file.txt http://localhost:8000/file.txt +``` + +### Running TCP server with custom responses + +This will run the tool as TLS TCP server and enable custom responses based on YAML templates: + +```sh +▶ simplehttpserver -rule rules.yaml -tcp -tls -domain localhost +``` + +The rules are written as follows: +```yaml +rules: + - match: regex + response: response data +``` + +For example to handle two different paths simulating an HTTP server or SMTP commands: +```yaml +rules: + # HTTP Requests + - match: GET /path1 + response: | + HTTP/1.0 200 OK + Server: httpd/2.0 + x-frame-options: SAMEORIGIN + x-xss-protection: 1; mode=block + Date: Fri, 16 Apr 2021 14:30:32 GMT + Content-Type: text/html + Connection: close + + + + - match: GET /path2 + response: | + HTTP/1.0 404 OK + Server: httpd/2.0 + + Not found + # SMTP Commands + - match: "EHLO example.com" + response: | + 250-localhost Nice to meet you, [127.0.0.1] + 250-PIPELINING + 250-8BITMIME + 250-SMTPUTF8 + 250-AUTH LOGIN PLAIN + 250 STARTTLS + - match: "MAIL FROM: " + response: 250 Accepted + - match: "RCPT TO: " + response: 250 Accepted +``` + # Thanks simplehttpserver is made with 🖤 by the [projectdiscovery](https://projectdiscovery.io) team. Community contributions have made the project what it is. See the **[Thanks.md](https://github.com/projectdiscovery/simplehttpserver/blob/master/THANKS.md)** file for more details. From ef3b178ae32b8499bc25fb38933e31d8ed168394 Mon Sep 17 00:00:00 2001 From: sandeep <8293321+ehsandeep@users.noreply.github.com> Date: Fri, 16 Apr 2021 22:08:06 +0530 Subject: [PATCH 34/45] Update build.yaml --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 11d1658..008bca6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,8 +32,8 @@ jobs: - name: Test run: go test . - working-directory: cmd/simplehttpserver/ + working-directory: . - name: Build run: go build . - working-directory: cmd/simplehttpserver/ + working-directory: . From bce92ad125f31668a58d4bcdff509a061f04d1d0 Mon Sep 17 00:00:00 2001 From: sandeep <8293321+ehsandeep@users.noreply.github.com> Date: Fri, 16 Apr 2021 22:15:35 +0530 Subject: [PATCH 35/45] reverting working-directory --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 008bca6..4179b91 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,8 +32,8 @@ jobs: - name: Test run: go test . - working-directory: . + working-directory: cmd/simplehttpserver - name: Build run: go build . - working-directory: . + working-directory: cmd/simplehttpserver/ From 566a82a76a360d0dce6cac9c479041d86a302612 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 16 Apr 2021 19:09:45 +0200 Subject: [PATCH 36/45] adding absolute path --- internal/runner/options.go | 9 +++++++++ internal/runner/runner.go | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/internal/runner/options.go b/internal/runner/options.go index 3170cd3..9104c4a 100644 --- a/internal/runner/options.go +++ b/internal/runner/options.go @@ -3,6 +3,7 @@ package runner import ( "flag" "os" + "path/filepath" "strings" "github.com/projectdiscovery/gologger" @@ -91,3 +92,11 @@ func (options *Options) configureOutput() { gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent) } } + +func (o Options) FolderAbsPath() string { + abspath, err := filepath.Abs(o.Folder) + if err != nil { + return o.Folder + } + return abspath +} diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 6603d50..820dc51 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -72,11 +72,11 @@ func (r *Runner) Run() error { } if r.options.HTTPS { - gologger.Print().Msgf("Serving %s on https://%s/...", r.options.Folder, r.options.ListenAddress) + gologger.Print().Msgf("Serving %s on https://%s/", r.options.FolderAbsPath(), r.options.ListenAddress) return r.httpServer.ListenAndServeTLS() } - gologger.Print().Msgf("Serving %s on http://%s/...", r.options.Folder, r.options.ListenAddress) + gologger.Print().Msgf("Serving %s on http://%s/", r.options.FolderAbsPath(), r.options.ListenAddress) return r.httpServer.ListenAndServe() } From 1fe30e15f17c64dc45e59b840512b1c8c78b38d8 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 16 Apr 2021 19:19:24 +0200 Subject: [PATCH 37/45] adding comments --- cmd/simplehttpserver/simplehttpserver.go | 6 +++--- internal/runner/options.go | 1 + internal/runner/runner.go | 3 +++ pkg/binder/binder.go | 2 ++ pkg/httpserver/httpserver.go | 6 ++++++ pkg/httpserver/loglayer.go | 3 +++ pkg/tcpserver/responseengine.go | 3 ++- pkg/tcpserver/rule.go | 5 ++++- pkg/tcpserver/tcpserver.go | 7 +++++++ 9 files changed, 31 insertions(+), 5 deletions(-) diff --git a/cmd/simplehttpserver/simplehttpserver.go b/cmd/simplehttpserver/simplehttpserver.go index 0dbbe6c..36ae61d 100644 --- a/cmd/simplehttpserver/simplehttpserver.go +++ b/cmd/simplehttpserver/simplehttpserver.go @@ -8,11 +8,11 @@ import ( func main() { // Parse the command line flags and read config files options := runner.ParseOptions() - runner, err := runner.New(options) + r, err := runner.New(options) if err != nil { gologger.Fatal().Msgf("Could not create runner: %s\n", err) } - runner.Run() - runner.Close() + r.Run() + defer r.Close() } diff --git a/internal/runner/options.go b/internal/runner/options.go index 9104c4a..6c8e1f9 100644 --- a/internal/runner/options.go +++ b/internal/runner/options.go @@ -93,6 +93,7 @@ func (options *Options) configureOutput() { } } +// FolderAbsPath of the fileserver folder func (o Options) FolderAbsPath() string { abspath, err := filepath.Abs(o.Folder) if err != nil { diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 820dc51..653cbed 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -14,6 +14,7 @@ type Runner struct { httpServer *httpserver.HTTPServer } +// New instance of runner func New(options *Options) (*Runner, error) { r := Runner{options: options} // Check if the process can listen on the specified ip:port @@ -65,6 +66,7 @@ func New(options *Options) (*Runner, error) { return &r, nil } +// Run logic func (r *Runner) Run() error { if r.options.EnableTCP { gologger.Print().Msgf("Serving TCP rule based server on tcp://%s", r.options.ListenAddress) @@ -80,6 +82,7 @@ func (r *Runner) Run() error { return r.httpServer.ListenAndServe() } +// Close the listening services func (r *Runner) Close() error { if r.serverTCP != nil { r.serverTCP.Close() diff --git a/pkg/binder/binder.go b/pkg/binder/binder.go index 2a248a1..a530cf1 100644 --- a/pkg/binder/binder.go +++ b/pkg/binder/binder.go @@ -7,6 +7,7 @@ import ( "github.com/phayes/freeport" ) +// CanListenOn the specified address func CanListenOn(address string) bool { listener, err := net.Listen("tcp4", address) if err != nil { @@ -16,6 +17,7 @@ func CanListenOn(address string) bool { return true } +// GetRandomListenAddress from the specified one func GetRandomListenAddress(currentAddress string) (string, error) { addrOrig, _, err := net.SplitHostPort(currentAddress) if err != nil { diff --git a/pkg/httpserver/httpserver.go b/pkg/httpserver/httpserver.go index 958a6e7..1747017 100644 --- a/pkg/httpserver/httpserver.go +++ b/pkg/httpserver/httpserver.go @@ -6,6 +6,7 @@ import ( "github.com/projectdiscovery/sslcert" ) +// Options of the http server type Options struct { Folder string EnableUpload bool @@ -20,11 +21,13 @@ type Options struct { Verbose bool } +// HTTPServer instance type HTTPServer struct { options *Options layers http.Handler } +// New http server instance with options func New(options *Options) (*HTTPServer, error) { var h HTTPServer EnableUpload = options.EnableUpload @@ -37,10 +40,12 @@ func New(options *Options) (*HTTPServer, error) { return &HTTPServer{options: options, layers: layers}, nil } +// ListenAndServe requests over http func (t *HTTPServer) ListenAndServe() error { return http.ListenAndServe(t.options.ListenAddress, t.layers) } +// ListenAndServeTLS requests over https func (t *HTTPServer) ListenAndServeTLS() error { if t.options.Certificate == "" || t.options.CertificateKey == "" { tlsOptions := sslcert.DefaultOptions @@ -59,6 +64,7 @@ func (t *HTTPServer) ListenAndServeTLS() error { return http.ListenAndServeTLS(t.options.ListenAddress, t.options.Certificate, t.options.CertificateKey, t.layers) } +// Close the service func (t *HTTPServer) Close() error { return nil } diff --git a/pkg/httpserver/loglayer.go b/pkg/httpserver/loglayer.go index 2d84d9a..1e64b8f 100644 --- a/pkg/httpserver/loglayer.go +++ b/pkg/httpserver/loglayer.go @@ -54,15 +54,18 @@ func newLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter { return &loggingResponseWriter{w, http.StatusOK, []byte{}} } +// Write the data func (lrw *loggingResponseWriter) Write(data []byte) (int, error) { lrw.Data = append(lrw.Data, data...) return lrw.ResponseWriter.Write(data) } +// Header of the response func (lrw *loggingResponseWriter) Header() http.Header { return lrw.ResponseWriter.Header() } +// WriteHeader status code func (lrw *loggingResponseWriter) WriteHeader(code int) { lrw.statusCode = code lrw.ResponseWriter.WriteHeader(code) diff --git a/pkg/tcpserver/responseengine.go b/pkg/tcpserver/responseengine.go index c3286f8..ec15da0 100644 --- a/pkg/tcpserver/responseengine.go +++ b/pkg/tcpserver/responseengine.go @@ -4,6 +4,7 @@ import ( "errors" ) +// BuildResponse according to rules func (t *TCPServer) BuildResponse(data []byte) ([]byte, error) { // Process all the rules for _, rule := range t.options.rules { @@ -11,5 +12,5 @@ func (t *TCPServer) BuildResponse(data []byte) ([]byte, error) { return []byte(rule.Response), nil } } - return nil, errors.New("No matched rule") + return nil, errors.New("no matched rule") } diff --git a/pkg/tcpserver/rule.go b/pkg/tcpserver/rule.go index ba31839..903331b 100644 --- a/pkg/tcpserver/rule.go +++ b/pkg/tcpserver/rule.go @@ -2,17 +2,20 @@ package tcpserver import "regexp" +// RulesConfiguration from yaml type RulesConfiguration struct { Rules []Rule `yaml:"rules"` } +// Rule to apply to various requests type Rule struct { Match string `yaml:"match,omitempty"` matchRegex *regexp.Regexp Response string `yaml:"response,omitempty"` } -func NewRule(match string, response string) (*Rule, error) { +// NewRule from model +func NewRule(match, response string) (*Rule, error) { regxp, err := regexp.Compile(match) if err != nil { return nil, err diff --git a/pkg/tcpserver/tcpserver.go b/pkg/tcpserver/tcpserver.go index f78f876..7bff39e 100644 --- a/pkg/tcpserver/tcpserver.go +++ b/pkg/tcpserver/tcpserver.go @@ -11,6 +11,7 @@ import ( "gopkg.in/yaml.v2" ) +// Options of the tcp server type Options struct { Listen string TLS bool @@ -21,20 +22,24 @@ type Options struct { Verbose bool } +// TCPServer instance type TCPServer struct { options Options listener net.Listener } +// New tcp server instance with specified options func New(options Options) (*TCPServer, error) { return &TCPServer{options: options}, nil } +// AddRule to the server func (t *TCPServer) AddRule(rule Rule) error { t.options.rules = append(t.options.rules, rule) return nil } +// ListenAndServe requests func (t *TCPServer) ListenAndServe() error { listener, err := net.Listen("tcp4", t.options.Listen) if err != nil { @@ -68,6 +73,7 @@ func (t *TCPServer) handleConnection(conn net.Conn) error { } } +// ListenAndServe requests over tls func (t *TCPServer) ListenAndServeTLS() error { var tlsConfig *tls.Config if t.options.Certificate != "" && t.options.Key != "" { @@ -108,6 +114,7 @@ func (t *TCPServer) Close() error { return t.listener.Close() } +// LoadTemplate from yaml func (t *TCPServer) LoadTemplate(templatePath string) error { var config RulesConfiguration yamlFile, err := ioutil.ReadFile(templatePath) From b0911c1316fb5c187fe71b1f5372af0372b65852 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 16 Apr 2021 19:30:13 +0200 Subject: [PATCH 38/45] adding missing error checks --- cmd/simplehttpserver/simplehttpserver.go | 4 +++- internal/runner/options.go | 3 ++- internal/runner/runner.go | 8 ++++++-- pkg/binder/binder.go | 5 ++++- pkg/tcpserver/tcpserver.go | 11 ++++++++--- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/cmd/simplehttpserver/simplehttpserver.go b/cmd/simplehttpserver/simplehttpserver.go index 36ae61d..9d37de8 100644 --- a/cmd/simplehttpserver/simplehttpserver.go +++ b/cmd/simplehttpserver/simplehttpserver.go @@ -13,6 +13,8 @@ func main() { gologger.Fatal().Msgf("Could not create runner: %s\n", err) } - r.Run() + if err := r.Run(); err != nil { + gologger.Info().Msgf("%s\n", err) + } defer r.Close() } diff --git a/internal/runner/options.go b/internal/runner/options.go index 6c8e1f9..0b5e587 100644 --- a/internal/runner/options.go +++ b/internal/runner/options.go @@ -10,6 +10,7 @@ import ( "github.com/projectdiscovery/gologger/levels" ) +// Options of the tool type Options struct { ListenAddress string Folder string @@ -94,7 +95,7 @@ func (options *Options) configureOutput() { } // FolderAbsPath of the fileserver folder -func (o Options) FolderAbsPath() string { +func (o *Options) FolderAbsPath() string { abspath, err := filepath.Abs(o.Folder) if err != nil { return o.Folder diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 653cbed..4bda588 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -85,10 +85,14 @@ func (r *Runner) Run() error { // Close the listening services func (r *Runner) Close() error { if r.serverTCP != nil { - r.serverTCP.Close() + if err := r.serverTCP.Close(); err != nil { + return err + } } if r.httpServer != nil { - r.httpServer.Close() + if err := r.httpServer.Close(); err != nil { + return err + } } return nil } diff --git a/pkg/binder/binder.go b/pkg/binder/binder.go index a530cf1..88a051b 100644 --- a/pkg/binder/binder.go +++ b/pkg/binder/binder.go @@ -5,6 +5,7 @@ import ( "net" "github.com/phayes/freeport" + "github.com/projectdiscovery/gologger" ) // CanListenOn the specified address @@ -13,7 +14,9 @@ func CanListenOn(address string) bool { if err != nil { return false } - defer listener.Close() + if err := listener.Close(); err != nil { + gologger.Info().Msgf("%s\n", err) + } return true } diff --git a/pkg/tcpserver/tcpserver.go b/pkg/tcpserver/tcpserver.go index 7bff39e..dfcb18b 100644 --- a/pkg/tcpserver/tcpserver.go +++ b/pkg/tcpserver/tcpserver.go @@ -54,7 +54,9 @@ func (t *TCPServer) handleConnection(conn net.Conn) error { buf := make([]byte, 4096) for { - conn.SetReadDeadline(time.Now().Add(time.Duration(5 * time.Second))) + if err := conn.SetReadDeadline(time.Now().Add(time.Duration(5 * time.Second))); err != nil { + gologger.Info().Msgf("%s\n", err) + } _, err := conn.Read(buf) if err != nil { return err @@ -67,7 +69,9 @@ func (t *TCPServer) handleConnection(conn net.Conn) error { return err } - conn.Write(resp) + if _, err := conn.Write(resp); err != nil { + gologger.Info().Msgf("%s\n", err) + } gologger.Print().Msgf("%s\n", resp) } @@ -106,10 +110,11 @@ func (t *TCPServer) run() error { if err != nil { return err } - go t.handleConnection(c) + go t.handleConnection(c) //nolint } } +// Close the service func (t *TCPServer) Close() error { return t.listener.Close() } From 309e6bc1170114b3bb80ddb5e403cce4bd77dc0b Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 16 Apr 2021 19:33:04 +0200 Subject: [PATCH 39/45] adding missing go docs --- pkg/binder/doc.go | 2 ++ pkg/httpserver/doc.go | 2 ++ pkg/tcpserver/doc.go | 2 ++ 3 files changed, 6 insertions(+) create mode 100644 pkg/binder/doc.go create mode 100644 pkg/httpserver/doc.go create mode 100644 pkg/tcpserver/doc.go diff --git a/pkg/binder/doc.go b/pkg/binder/doc.go new file mode 100644 index 0000000..709dc10 --- /dev/null +++ b/pkg/binder/doc.go @@ -0,0 +1,2 @@ +// Package binder contains binding helpers +package binder diff --git a/pkg/httpserver/doc.go b/pkg/httpserver/doc.go new file mode 100644 index 0000000..5344c9f --- /dev/null +++ b/pkg/httpserver/doc.go @@ -0,0 +1,2 @@ +// Package httpserver contains the http server logic +package httpserver diff --git a/pkg/tcpserver/doc.go b/pkg/tcpserver/doc.go new file mode 100644 index 0000000..4ab6d69 --- /dev/null +++ b/pkg/tcpserver/doc.go @@ -0,0 +1,2 @@ +// Package tcpserver contains the tcp server logic +package tcpserver From 89bbfaac877c637c32f56dc98d2d3a6ece77c9b9 Mon Sep 17 00:00:00 2001 From: mzack Date: Fri, 16 Apr 2021 20:10:40 +0200 Subject: [PATCH 40/45] fixing misc lint errors --- .golangci.yml | 6 +----- cmd/simplehttpserver/simplehttpserver.go | 2 +- internal/runner/options.go | 6 +++--- internal/runner/runner.go | 2 +- pkg/binder/binder.go | 1 - pkg/tcpserver/tcpserver.go | 12 +++++++----- 6 files changed, 13 insertions(+), 16 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index d5e9089..31b66f3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -41,8 +41,6 @@ linters-settings: - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf # lll: # line-length: 140 - maligned: - suggest-new: true misspell: locale: US nolintlint: @@ -73,14 +71,12 @@ linters: - gosimple - govet - ineffassign - - interfacer - - maligned - misspell - nakedret - noctx - nolintlint - rowserrcheck - - scopelint + - exportloopref - staticcheck - structcheck - stylecheck diff --git a/cmd/simplehttpserver/simplehttpserver.go b/cmd/simplehttpserver/simplehttpserver.go index 9d37de8..ed82b40 100644 --- a/cmd/simplehttpserver/simplehttpserver.go +++ b/cmd/simplehttpserver/simplehttpserver.go @@ -16,5 +16,5 @@ func main() { if err := r.Run(); err != nil { gologger.Info().Msgf("%s\n", err) } - defer r.Close() + defer r.Close() //nolint } diff --git a/internal/runner/options.go b/internal/runner/options.go index 0b5e587..bf69db3 100644 --- a/internal/runner/options.go +++ b/internal/runner/options.go @@ -95,10 +95,10 @@ func (options *Options) configureOutput() { } // FolderAbsPath of the fileserver folder -func (o *Options) FolderAbsPath() string { - abspath, err := filepath.Abs(o.Folder) +func (options *Options) FolderAbsPath() string { + abspath, err := filepath.Abs(options.Folder) if err != nil { - return o.Folder + return options.Folder } return abspath } diff --git a/internal/runner/runner.go b/internal/runner/runner.go index 4bda588..7d69e25 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -28,7 +28,7 @@ func New(options *Options) (*Runner, error) { } if r.options.EnableTCP { - serverTCP, err := tcpserver.New(tcpserver.Options{ + serverTCP, err := tcpserver.New(&tcpserver.Options{ Listen: r.options.ListenAddress, TLS: r.options.TCPWithTLS, Domain: "local.host", diff --git a/pkg/binder/binder.go b/pkg/binder/binder.go index 88a051b..549e9d9 100644 --- a/pkg/binder/binder.go +++ b/pkg/binder/binder.go @@ -33,5 +33,4 @@ func GetRandomListenAddress(currentAddress string) (string, error) { } return net.JoinHostPort(addrOrig, fmt.Sprintf("%d", newPort)), nil - } diff --git a/pkg/tcpserver/tcpserver.go b/pkg/tcpserver/tcpserver.go index dfcb18b..876fbb4 100644 --- a/pkg/tcpserver/tcpserver.go +++ b/pkg/tcpserver/tcpserver.go @@ -11,6 +11,8 @@ import ( "gopkg.in/yaml.v2" ) +const readTimeout = 5 + // Options of the tcp server type Options struct { Listen string @@ -24,12 +26,12 @@ type Options struct { // TCPServer instance type TCPServer struct { - options Options + options *Options listener net.Listener } // New tcp server instance with specified options -func New(options Options) (*TCPServer, error) { +func New(options *Options) (*TCPServer, error) { return &TCPServer{options: options}, nil } @@ -50,11 +52,11 @@ func (t *TCPServer) ListenAndServe() error { } func (t *TCPServer) handleConnection(conn net.Conn) error { - defer conn.Close() + defer conn.Close() //nolint buf := make([]byte, 4096) for { - if err := conn.SetReadDeadline(time.Now().Add(time.Duration(5 * time.Second))); err != nil { + if err := conn.SetReadDeadline(time.Now().Add(readTimeout * time.Second)); err != nil { gologger.Info().Msgf("%s\n", err) } _, err := conn.Read(buf) @@ -77,7 +79,7 @@ func (t *TCPServer) handleConnection(conn net.Conn) error { } } -// ListenAndServe requests over tls +// ListenAndServeTLS requests over tls func (t *TCPServer) ListenAndServeTLS() error { var tlsConfig *tls.Config if t.options.Certificate != "" && t.options.Key != "" { From 8a339ee35b4f329b9b4c2f2f830e4594769751ba Mon Sep 17 00:00:00 2001 From: mzack Date: Fri, 16 Apr 2021 20:12:26 +0200 Subject: [PATCH 41/45] bumping version --- internal/runner/banner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/runner/banner.go b/internal/runner/banner.go index c8972db..2d31eb8 100644 --- a/internal/runner/banner.go +++ b/internal/runner/banner.go @@ -12,7 +12,7 @@ const banner = ` ` // Version is the current version -const Version = `0.0.1` +const Version = `0.0.2` // showBanner is used to show the banner to the user func showBanner() { From 1b9328d08132f01a31dd1e8bd79c16017fe5854b Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Sat, 17 Apr 2021 00:47:40 +0530 Subject: [PATCH 42/45] Update README.md --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 00daf9c..d2b4e5b 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,18 @@ -

- simplehttpserver -
-

+

SimpleHTTPserver

+

Go alternative of python SimpleHTTPServer

+ + +

+ + + + + + +

-[![License](https://img.shields.io/badge/license-MIT-_red.svg)](https://opensource.org/licenses/MIT) -[![Go Report Card](https://goreportcard.com/badge/github.com/projectdiscovery/simplehttpserver)](https://goreportcard.com/report/github.com/projectdiscovery/simplehttpserver) -[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/projectdiscovery/simplehttpserver/issues) -[![GitHub Release](https://img.shields.io/github/release/projectdiscovery/simplehttpserver)](https://github.com/projectdiscovery/simplehttpserver/releases) -[![Follow on Twitter](https://img.shields.io/twitter/follow/pdiscoveryio.svg?logo=twitter)](https://twitter.com/pdiscoveryio) -[![Docker Images](https://img.shields.io/docker/pulls/projectdiscovery/simplehttpserver.svg)](https://hub.docker.com/r/projectdiscovery/simplehttpserver) -[![Chat on Discord](https://img.shields.io/discord/695645237418131507.svg?logo=discord)](https://discord.gg/KECAGdH) -simplehttpserver is a go enhanced version of the well known python simplehttpserver with in addition a fully customizable TCP server, both supporting TLS. +SimpleHTTPserver is a go enhanced version of the well known python simplehttpserver with in addition a fully customizable TCP server, both supporting TLS. # Resources From d490ff6b074db5233af1e6dd126ee9a0cc4915a7 Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Sat, 17 Apr 2021 00:57:03 +0530 Subject: [PATCH 43/45] Update README.md --- README.md | 50 +++++++++++++------------------------------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index d2b4e5b..426fd74 100644 --- a/README.md +++ b/README.md @@ -11,24 +11,21 @@

+

+ Features • + Usage • + Installation • + Run SimpleHTTPserver • + Join Discord +

-SimpleHTTPserver is a go enhanced version of the well known python simplehttpserver with in addition a fully customizable TCP server, both supporting TLS. +--- -# Resources +SimpleHTTPserver is a go enhanced version of the well known python simplehttpserver with in addition a fully customizable TCP server, both supporting TLS. -- [Features](#features) -- [Usage](#usage) -- [Installation Instructions](#installation-instructions) -- [Running simplehttpserver](#running-simplehttpserver-in-the-current-folder) -- [Thanks](#thanks) # Features -

- simplehttpserver -
-

- - HTTPS support - File server in arbitrary directory - Full request/response dump @@ -36,33 +33,12 @@ SimpleHTTPserver is a go enhanced version of the well known python simplehttpser - Configurable HTTP/TCP server with customizable response via YAML template -# Installation Instructions - - -### From Binary - -The installation is easy. You can download the pre-built binaries for your platform from the [Releases](https://github.com/projectdiscovery/simplehttpserver/releases/) page. Extract them using tar, move it to your `$PATH`and you're ready to go. - -```sh -Download latest binary from https://github.com/projectdiscovery/simplehttpserver/releases - -▶ tar -xvf simplehttpserver-linux-amd64.tar -▶ mv simplehttpserver-linux-amd64 /usr/local/bin/simplehttpserver -▶ simplehttpserver -h -``` - -### From Source - -simplehttpserver requires **go1.14+** to install successfully. Run the following command to get the repo - - -```sh -▶ GO111MODULE=on go get -v github.com/projectdiscovery/simplehttpserver -``` +# Installing SimpleHTTPserver -### From Github +SimpleHTTPserver requires **go1.14+** to install successfully. Run the following command to get the repo - ```sh -▶ git clone https://github.com/projectdiscovery/simplehttpserver.git; cd simplehttpserver; go build; mv simplehttpserver /usr/local/bin/; simplehttpserver -h +▶ GO111MODULE=on go get -v github.com/projectdiscovery/cmd/simplehttpserver ``` # Usage @@ -189,4 +165,4 @@ rules: # Thanks -simplehttpserver is made with 🖤 by the [projectdiscovery](https://projectdiscovery.io) team. Community contributions have made the project what it is. See the **[Thanks.md](https://github.com/projectdiscovery/simplehttpserver/blob/master/THANKS.md)** file for more details. +SimpleHTTPserver is made with 🖤 by the [projectdiscovery](https://projectdiscovery.io) team. From 63f26b4949f406b0f8a5c6fa293176ede8a02429 Mon Sep 17 00:00:00 2001 From: sandeep <8293321+ehsandeep@users.noreply.github.com> Date: Sat, 17 Apr 2021 01:16:28 +0530 Subject: [PATCH 44/45] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b883f1f..4f9c0f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.exe +cmd/simplehttpserver/simplehttpserver From 80695a67430e38372ce7188a24175bbaa2267722 Mon Sep 17 00:00:00 2001 From: sandeep <8293321+ehsandeep@users.noreply.github.com> Date: Sat, 17 Apr 2021 01:27:47 +0530 Subject: [PATCH 45/45] banner update --- internal/runner/banner.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/runner/banner.go b/internal/runner/banner.go index 2d31eb8..f727190 100644 --- a/internal/runner/banner.go +++ b/internal/runner/banner.go @@ -3,12 +3,12 @@ package runner import "github.com/projectdiscovery/gologger" const banner = ` - _ _ _ _ _ - ___(_)_ __ ___ _ __ | | ___| |__ | |_| |_ _ __ ___ ___ _ ____ _____ _ __ -/ __| | '_ ' _ \| '_ \| |/ _ \ '_ \| __| __| '_ \/ __|/ _ \ '__\ \ / / _ \ '__| -\__ \ | | | | | | |_) | | __/ | | | |_| |_| |_) \__ \ __/ | \ V / __/ | -|___/_|_| |_| |_| .__/|_|\___|_| |_|\__|\__| .__/|___/\___|_| \_/ \___|_| - |_| |_| + _____ _ __ __ __________________ + / ___/(_)___ ___ ____ / /__ / / / /_ __/_ __/ __ \________ ______ _____ _____ + \__ \/ / __ -__ \/ __ \/ / _ \/ /_/ / / / / / / /_/ / ___/ _ \/ ___/ | / / _ \/ ___/ + ___/ / / / / / / / /_/ / / __/ __ / / / / / / ____(__ ) __/ / | |/ / __/ / +/____/_/_/ /_/ /_/ .___/_/\___/_/ /_/ /_/ /_/ /_/ /____/\___/_/ |___/\___/_/ + /_/ - v0.0.2 ` // Version is the current version