diff --git a/README.md b/README.md index 367e6f62..d24b3abf 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,9 @@ OUTPUT: -nc, -no-color disable colors in cli output -v, -verbose display verbose output -version display project version + +DEBUG: + -health-check, -hc run diagnostic check up ``` ## Using tlsx as library diff --git a/cmd/tlsx/main.go b/cmd/tlsx/main.go index 558581e8..a900f9b4 100644 --- a/cmd/tlsx/main.go +++ b/cmd/tlsx/main.go @@ -124,10 +124,19 @@ func readFlags() error { flagSet.BoolVar(&options.Version, "version", false, "display project version"), ) + flagSet.CreateGroup("debug", "Debug", + flagSet.BoolVarP(&options.HealthCheck, "hc", "health-check", false, "run diagnostic check up"), + ) + if err := flagSet.Parse(); err != nil { return errorutils.NewWithErr(err).Msgf("could not parse flags") } + if options.HealthCheck { + gologger.Print().Msgf("%s\n", runner.DoHealthCheck(flagSet)) + os.Exit(0) + } + if cfgFile != "" { if err := flagSet.MergeConfigFile(cfgFile); err != nil { return errorutils.NewWithErr(err).Msgf("could not read config file") diff --git a/go.mod b/go.mod index 55b23756..e99ab304 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/miekg/dns v1.1.50 github.com/projectdiscovery/dnsx v1.1.1 github.com/projectdiscovery/fastdialer v0.0.22 + github.com/projectdiscovery/fileutil v0.0.3 github.com/projectdiscovery/goflags v0.1.6 github.com/projectdiscovery/gologger v1.1.7 github.com/projectdiscovery/mapcidr v1.0.3 @@ -60,7 +61,6 @@ require ( github.com/projectdiscovery/asnmap v0.0.1 // indirect github.com/projectdiscovery/blackrock v0.0.0-20220628111055-35616c71b2dc // indirect github.com/projectdiscovery/cdncheck v0.0.3 // indirect - github.com/projectdiscovery/fileutil v0.0.3 // indirect github.com/projectdiscovery/hmap v0.0.2 // indirect github.com/projectdiscovery/iputil v0.0.2 // indirect github.com/projectdiscovery/networkpolicy v0.0.3 // indirect diff --git a/internal/runner/healthcheck.go b/internal/runner/healthcheck.go new file mode 100644 index 00000000..d2433ade --- /dev/null +++ b/internal/runner/healthcheck.go @@ -0,0 +1,81 @@ +package runner + +import ( + "fmt" + "net" + "runtime" + "strings" + + "github.com/projectdiscovery/fileutil" + "github.com/projectdiscovery/goflags" + "github.com/projectdiscovery/tlsx/pkg/tlsx/openssl" + "github.com/projectdiscovery/tlsx/pkg/tlsx/tls" + "github.com/projectdiscovery/tlsx/pkg/tlsx/ztls" +) + +func DoHealthCheck(flagSet *goflags.FlagSet) string { + // RW permissions on config file + cfgFilePath, _ := flagSet.GetConfigFilePath() + var test strings.Builder + test.WriteString(fmt.Sprintf("Version: %s\n", version)) + test.WriteString(fmt.Sprintf("Operative System: %s\n", runtime.GOOS)) + test.WriteString(fmt.Sprintf("Architecture: %s\n", runtime.GOARCH)) + test.WriteString(fmt.Sprintf("Go Version: %s\n", runtime.Version())) + test.WriteString(fmt.Sprintf("Compiler: %s\n", runtime.Compiler)) + + var testResult string + ok, err := fileutil.IsReadable(cfgFilePath) + if ok { + testResult = "Ok" + } else { + testResult = "Ko" + } + if err != nil { + testResult += fmt.Sprintf(" (%s)", err) + } + test.WriteString(fmt.Sprintf("Config file \"%s\" Read => %s\n", cfgFilePath, testResult)) + ok, err = fileutil.IsWriteable(cfgFilePath) + if ok { + testResult = "Ok" + } else { + testResult = "Ko" + } + if err != nil { + testResult += fmt.Sprintf(" (%s)", err) + } + test.WriteString(fmt.Sprintf("Config file \"%s\" Write => %s\n", cfgFilePath, testResult)) + c4, err := net.Dial("tcp4", "scanme.sh:80") + if err == nil && c4 != nil { + c4.Close() + } + testResult = "Ok" + if err != nil { + testResult = fmt.Sprintf("Ko (%s)", err) + } + test.WriteString(fmt.Sprintf("IPv4 connectivity to scanme.sh:80 => %s\n", testResult)) + c6, err := net.Dial("tcp6", "scanme.sh:80") + if err == nil && c6 != nil { + c6.Close() + } + testResult = "Ok" + if err != nil { + testResult = fmt.Sprintf("Ko (%s)", err) + } + test.WriteString(fmt.Sprintf("IPv6 connectivity to scanme.sh:80 => %s\n", testResult)) + + test.WriteString("Supported Engines\n") + test.WriteString("ctls\n") + test.WriteString(fmt.Sprintf("TLS: %s\n", strings.Join(tls.SupportedTlsVersions, ", "))) + test.WriteString(fmt.Sprintf("Ciphers: %s\n", strings.Join(tls.AllCiphersNames, ", "))) + + test.WriteString("ztls\n") + test.WriteString(fmt.Sprintf("TLS: %s\n", strings.Join(ztls.SupportedTlsVersions, ", "))) + test.WriteString(fmt.Sprintf("Ciphers: %s\n", strings.Join(ztls.AllCiphersNames, ", "))) + + if openssl.IsAvailable() { + test.WriteString("openssl\n") + test.WriteString(fmt.Sprintf("location: %s\n", openssl.BinaryPath)) + } + + return test.String() +} diff --git a/pkg/tlsx/clients/clients.go b/pkg/tlsx/clients/clients.go index 6e7f706f..35a7f1c7 100644 --- a/pkg/tlsx/clients/clients.go +++ b/pkg/tlsx/clients/clients.go @@ -133,6 +133,8 @@ type Options struct { ClientHello bool // ServerHello include server hello (only ztls) ServerHello bool + // HealthCheck performs a capabilities healthcheck + HealthCheck bool // Fastdialer is a fastdialer dialer instance Fastdialer *fastdialer.Dialer diff --git a/pkg/tlsx/openssl/common.go b/pkg/tlsx/openssl/common.go index e7c84de3..a5577575 100644 --- a/pkg/tlsx/openssl/common.go +++ b/pkg/tlsx/openssl/common.go @@ -21,7 +21,7 @@ var ( ) var ( - binaryPath = "" + BinaryPath = "" OPENSSL_CONF = "" IsLibreSSL = false PkgTag = "" // Header or Tag value that will be reflected in all errors (include openssl(libressl) and version) @@ -45,11 +45,11 @@ CipherString = DEFAULT:@SECLEVEL=1 func init() { if runtime.GOOS == "windows" { - binaryPath, _ = exec.LookPath("openssl.exe") + BinaryPath, _ = exec.LookPath("openssl.exe") } else { - binaryPath, _ = exec.LookPath("openssl") + BinaryPath, _ = exec.LookPath("openssl") } - if binaryPath == "" { + if BinaryPath == "" { // not available or failed to get return gologger.Debug().Label("openssl").Msgf("openssl binary not found skipping") return @@ -94,12 +94,12 @@ func openSSLSetup() errorutils.Error { // check if openssl if available for use func IsAvailable() bool { - return binaryPath != "" + return BinaryPath != "" } // UseOpenSSLBinary From Path func UseOpenSSLBinary(binpath string) { - binaryPath = binpath + BinaryPath = binpath if err := openSSLSetup(); err != nil { // do not fallback gologger.Fatal().Label("openssl").Msgf(err.Error()) diff --git a/pkg/tlsx/openssl/openssl_exec.go b/pkg/tlsx/openssl/openssl_exec.go index 70b8f31e..ea1aa4d6 100644 --- a/pkg/tlsx/openssl/openssl_exec.go +++ b/pkg/tlsx/openssl/openssl_exec.go @@ -34,7 +34,7 @@ func execOpenSSL(ctx context.Context, args []string) (*CMDOUT, error) { 3. error purely realted to I/O and command execution */ var outbuff, inbuff, errbuff bytes.Buffer - cmd := exec.CommandContext(ctx, binaryPath) + cmd := exec.CommandContext(ctx, BinaryPath) if !IsLibreSSL { newenv := "OPENSSL_CONF=" + OPENSSL_CONF cmd.Env = append(os.Environ(), newenv) @@ -74,7 +74,7 @@ func getResponse(ctx context.Context, opts *Options) (*Response, errorutils.Erro } result, err := execOpenSSL(ctx, args) if err != nil { - return nil, errorutils.NewWithErr(err).WithTag(PkgTag, binaryPath).Msgf("failed to execute openssl got %v", result.Stderr).Msgf(commadFormat, result.Command) + return nil, errorutils.NewWithErr(err).WithTag(PkgTag, BinaryPath).Msgf("failed to execute openssl got %v", result.Stderr).Msgf(commadFormat, result.Command) } response := &Response{} if !strings.Contains(result.Stdout, "CONNECTED") {