├── cmd ├── functional-test │ ├── test-data │ │ └── file.txt │ ├── run.sh │ ├── testcases.txt │ └── main.go ├── dnsx │ └── dnsx.go └── integration-test │ ├── integration-test.go │ └── dns.go ├── libs └── dnsx │ ├── doc.go │ ├── util.go │ └── dnsx.go ├── static ├── dnsx-logo.png └── dnsx-run.png ├── internal ├── runner │ ├── doc.go │ ├── resume.go │ ├── banner.go │ ├── util.go │ ├── wildcard.go │ ├── options.go │ └── runner.go └── testutils │ └── integration.go ├── .gitignore ├── SECURITY.md ├── Dockerfile ├── Makefile ├── integration_tests └── run.sh ├── .github ├── workflows │ ├── lint-test.yml │ ├── functional-test.yml │ ├── build-test.yml │ ├── release-binary.yml │ ├── codeql-analysis.yml │ └── dockerhub-push.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── issue-report.md └── dependabot.yml ├── .goreleaser.yml ├── LICENSE.md ├── go.mod ├── README.md └── go.sum /cmd/functional-test/test-data/file.txt: -------------------------------------------------------------------------------- 1 | example.com 2 | example.com 3 | -------------------------------------------------------------------------------- /libs/dnsx/doc.go: -------------------------------------------------------------------------------- 1 | // Package dnsx contains the library logic 2 | package dnsx 3 | -------------------------------------------------------------------------------- /static/dnsx-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/e1abrador/dnsx/master/static/dnsx-logo.png -------------------------------------------------------------------------------- /static/dnsx-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/e1abrador/dnsx/master/static/dnsx-run.png -------------------------------------------------------------------------------- /internal/runner/doc.go: -------------------------------------------------------------------------------- 1 | // Package runner executes the enumeration process. 2 | package runner 3 | -------------------------------------------------------------------------------- /internal/runner/resume.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | type ResumeCfg struct { 4 | ResumeFrom string 5 | Index int 6 | current string 7 | currentIndex int 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cmd/dnsx/dnsx 2 | .DS_Store 3 | dist/ 4 | cmd/functional-test/dnsx_dev 5 | cmd/functional-test/functional-test 6 | cmd/functional-test/dnsx 7 | cmd/functional-test/log.txt 8 | cmd/functional-test/*.cfg 9 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | DO NOT CREATE AN ISSUE to report a security problem. Instead, please send an email to security@projectdiscovery.io and we will acknowledge it within 3 working days. 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.17.3-alpine3.14 AS build-env 2 | RUN go install -v github.com/projectdiscovery/dnsx/cmd/dnsx@latest 3 | 4 | FROM alpine:3.14 5 | RUN apk add --no-cache bind-tools ca-certificates 6 | COPY --from=build-env /go/bin/dnsx /usr/local/bin/dnsx 7 | ENTRYPOINT ["dnsx"] 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Go parameters 2 | GOCMD=go 3 | GOBUILD=$(GOCMD) build 4 | GOMOD=$(GOCMD) mod 5 | GOTEST=$(GOCMD) test 6 | GOGET=$(GOCMD) get 7 | 8 | all: build 9 | build: 10 | $(GOBUILD) -v -ldflags="-extldflags=-static" -o "dnsx" cmd/dnsx/dnsx.go 11 | test: 12 | $(GOTEST) -v ./... 13 | tidy: 14 | $(GOMOD) tidy 15 | -------------------------------------------------------------------------------- /integration_tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm integration-test dnsx 2>/dev/null 4 | cd ../cmd/dnsx 5 | go build 6 | mv dnsx ../../integration_tests/dnsx 7 | cd ../integration-test 8 | go build 9 | mv integration-test ../../integration_tests/integration-test 10 | cd ../../integration_tests 11 | ./integration-test 12 | if [ $? -eq 0 ] 13 | then 14 | exit 0 15 | else 16 | exit 1 17 | fi 18 | -------------------------------------------------------------------------------- /cmd/functional-test/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 'Building functional-test binary' 4 | go build 5 | 6 | echo 'Building DNSX binary from current branch' 7 | go build -o dnsx_dev ../dnsx 8 | 9 | echo 'Installing latest release of DNSX' 10 | GO111MODULE=on go build -v github.com/projectdiscovery/dnsx/cmd/dnsx 11 | 12 | echo 'Starting DNSX functional test' 13 | ./functional-test -main ./dnsx -dev ./dnsx_dev -testcases testcases.txt 14 | -------------------------------------------------------------------------------- /.github/workflows/lint-test.yml: -------------------------------------------------------------------------------- 1 | name: 🙏🏻 Lint Test 2 | on: 3 | push: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | jobs: 8 | lint: 9 | name: Lint Test 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v2 14 | - name: Run golangci-lint 15 | uses: golangci/golangci-lint-action@v2 16 | with: 17 | version: latest 18 | args: --timeout 5m 19 | working-directory: . -------------------------------------------------------------------------------- /.github/workflows/functional-test.yml: -------------------------------------------------------------------------------- 1 | name: 🧪 Functional Test 2 | on: 3 | push: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | 8 | jobs: 9 | functional: 10 | name: Functional Test 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Set up Go 14 | uses: actions/setup-go@v2 15 | with: 16 | go-version: 1.17 17 | 18 | - name: Check out code 19 | uses: actions/checkout@v2 20 | 21 | - name: Functional Tests 22 | run: | 23 | chmod +x run.sh 24 | bash run.sh 25 | working-directory: cmd/functional-test 26 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod tidy 4 | 5 | builds: 6 | - env: 7 | - CGO_ENABLED=0 8 | goos: 9 | - windows 10 | - linux 11 | - darwin 12 | goarch: 13 | - amd64 14 | - 386 15 | - arm 16 | - arm64 17 | 18 | ignore: 19 | - goos: darwin 20 | goarch: '386' 21 | - goos: windows 22 | goarch: 'arm' 23 | - goos: windows 24 | goarch: 'arm64' 25 | 26 | binary: '{{ .ProjectName }}' 27 | main: cmd/dnsx/dnsx.go 28 | 29 | archives: 30 | - format: zip 31 | replacements: 32 | darwin: macOS 33 | 34 | checksum: 35 | algorithm: sha256 -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | 3 | contact_links: 4 | - name: Ask an question / advise on using dnsx 5 | url: https://github.com/projectdiscovery/dnsx/discussions/categories/q-a 6 | about: Ask a question or request support for using dnsx 7 | 8 | - name: Share idea / feature to discuss for dnsx 9 | url: https://github.com/projectdiscovery/dnsx/discussions/categories/ideas 10 | about: Share idea / feature to discuss for dnsx 11 | 12 | - name: Connect with PD Team (Discord) 13 | url: https://discord.gg/projectdiscovery 14 | about: Connect with PD Team for direct communication -------------------------------------------------------------------------------- /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | name: 🔨 Build Test 2 | on: 3 | push: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | 8 | jobs: 9 | build: 10 | name: Test Builds 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-go@v2 15 | with: 16 | go-version: 1.17 17 | 18 | - name: Test 19 | run: go test . 20 | working-directory: cmd/dnsx/ 21 | 22 | - name: Integration Tests 23 | run: bash run.sh 24 | working-directory: integration_tests/ 25 | 26 | - name: Build 27 | run: go build . 28 | working-directory: cmd/dnsx/ 29 | -------------------------------------------------------------------------------- /libs/dnsx/util.go: -------------------------------------------------------------------------------- 1 | package dnsx 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/miekg/dns" 8 | ) 9 | 10 | // StringToRequestType conversion helper 11 | func StringToRequestType(tp string) (rt uint16, err error) { 12 | tp = strings.TrimSpace(strings.ToUpper(tp)) 13 | switch tp { 14 | case "A": 15 | rt = dns.TypeA 16 | case "NS": 17 | rt = dns.TypeNS 18 | case "CNAME": 19 | rt = dns.TypeCNAME 20 | case "SOA": 21 | rt = dns.TypeSOA 22 | case "PTR": 23 | rt = dns.TypePTR 24 | case "MX": 25 | rt = dns.TypeMX 26 | case "TXT": 27 | rt = dns.TypeTXT 28 | case "AAAA": 29 | rt = dns.TypeAAAA 30 | default: 31 | rt = dns.TypeNone 32 | err = fmt.Errorf("incorrect type") 33 | } 34 | 35 | return 36 | } 37 | -------------------------------------------------------------------------------- /internal/runner/banner.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import "github.com/projectdiscovery/gologger" 4 | 5 | const banner = ` 6 | _ __ __ 7 | __| | _ __ ___ \ \/ / 8 | / _' || '_ \ / __| \ / 9 | | (_| || | | |\__ \ / \ 10 | \__,_||_| |_||___//_/\_\ v1.0.7 11 | ` 12 | 13 | // Version is the current version of dnsx 14 | const Version = `1.0.7` 15 | 16 | // showBanner is used to show the banner to the user 17 | func showBanner() { 18 | gologger.Print().Msgf("%s\n", banner) 19 | gologger.Print().Msgf("\t\tprojectdiscovery.io\n\n") 20 | 21 | gologger.Print().Msgf("Use with caution. You are responsible for your actions\n") 22 | gologger.Print().Msgf("Developers assume no liability and are not responsible for any misuse or damage.\n") 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/release-binary.yml: -------------------------------------------------------------------------------- 1 | name: 🎉 Release Binary 2 | on: 3 | create: 4 | tags: 5 | - v* 6 | workflow_dispatch: 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: "Check out code" 14 | uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | - 18 | name: "Set up Go" 19 | uses: actions/setup-go@v2 20 | with: 21 | go-version: 1.17 22 | - 23 | env: 24 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 25 | name: "Create release on GitHub" 26 | uses: goreleaser/goreleaser-action@v2 27 | with: 28 | args: "release --rm-dist" 29 | version: latest 30 | workdir: . -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Request feature to implement in this project 4 | labels: 'Type: Enhancement' 5 | --- 6 | 7 | 13 | 14 | ### Please describe your feature request: 15 | 16 | 17 | ### Describe the use case of this feature: 18 | 19 | -------------------------------------------------------------------------------- /cmd/functional-test/testcases.txt: -------------------------------------------------------------------------------- 1 | example.com {{binary}} -silent 2 | example.com {{binary}} -silent -l test-data/file.txt 3 | example.com {{binary}} -silent -raw 4 | example.com {{binary}} -silent -resp 5 | example.com {{binary}} -silent -resp-only 6 | example.com {{binary}} -silent -a 7 | example.com {{binary}} -silent -a -resp 8 | example.com {{binary}} -silent -aaaa 9 | example.com {{binary}} -silent -aaaa -resp 10 | example.com {{binary}} -silent -ns 11 | example.com {{binary}} -silent -ns -resp 12 | example.com {{binary}} -silent -mx 13 | example.com {{binary}} -silent -mx -resp 14 | example.com {{binary}} -silent -soa 15 | example.com {{binary}} -silent -soa -resp 16 | example.com {{binary}} -silent -txt 17 | example.com {{binary}} -silent -txt -resp 18 | example.com {{binary}} -silent -rcode 0,1,2 19 | 1.1.1.1 {{binary}} -silent -ptr 20 | 1.1.1.1 {{binary}} -silent -ptr -resp 21 | www.projectdiscovery.io {{binary}} -silent -cname 22 | www.projectdiscovery.io {{binary}} -silent -cname -resp -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: 🚨 CodeQL Analysis 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: 7 | - dev 8 | 9 | jobs: 10 | analyze: 11 | name: Analyze 12 | runs-on: ubuntu-latest 13 | permissions: 14 | actions: read 15 | contents: read 16 | security-events: write 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | language: [ 'go' ] 22 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 23 | 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v2 27 | 28 | # Initializes the CodeQL tools for scanning. 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v1 31 | with: 32 | languages: ${{ matrix.language }} 33 | 34 | - name: Autobuild 35 | uses: github/codeql-action/autobuild@v1 36 | 37 | - name: Perform CodeQL Analysis 38 | uses: github/codeql-action/analyze@v1 -------------------------------------------------------------------------------- /cmd/dnsx/dnsx.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | 7 | "github.com/projectdiscovery/dnsx/internal/runner" 8 | "github.com/projectdiscovery/gologger" 9 | ) 10 | 11 | func main() { 12 | // Parse the command line flags and read config files 13 | options := runner.ParseOptions() 14 | 15 | dnsxRunner, err := runner.New(options) 16 | if err != nil { 17 | gologger.Fatal().Msgf("Could not create runner: %s\n", err) 18 | } 19 | 20 | // Setup graceful exits 21 | c := make(chan os.Signal, 1) 22 | signal.Notify(c, os.Interrupt) 23 | go func() { 24 | for range c { 25 | gologger.Info().Msgf("CTRL+C pressed: Exiting\n") 26 | dnsxRunner.Close() 27 | if options.ShouldSaveResume() { 28 | gologger.Info().Msgf("Creating resume file: %s\n", runner.DefaultResumeFile) 29 | err := dnsxRunner.SaveResumeConfig() 30 | if err != nil { 31 | gologger.Error().Msgf("Couldn't create resume file: %s\n", err) 32 | } 33 | } 34 | os.Exit(1) 35 | } 36 | }() 37 | 38 | // nolint:errcheck 39 | dnsxRunner.Run() 40 | dnsxRunner.Close() 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 ProjectDiscovery, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | 9 | # Maintain dependencies for GitHub Actions 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: "weekly" 14 | target-branch: "dev" 15 | commit-message: 16 | prefix: "chore" 17 | include: "scope" 18 | 19 | # Maintain dependencies for go modules 20 | - package-ecosystem: "gomod" 21 | directory: "/" 22 | schedule: 23 | interval: "weekly" 24 | target-branch: "dev" 25 | commit-message: 26 | prefix: "chore" 27 | include: "scope" 28 | 29 | # Maintain dependencies for docker 30 | - package-ecosystem: "docker" 31 | directory: "/" 32 | schedule: 33 | interval: "weekly" 34 | target-branch: "dev" 35 | commit-message: 36 | prefix: "chore" 37 | include: "scope" -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/projectdiscovery/dnsx 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/golang/snappy v0.0.4 // indirect 7 | github.com/logrusorgru/aurora v2.0.3+incompatible 8 | github.com/miekg/dns v1.1.43 9 | github.com/onsi/gomega v1.16.0 // indirect 10 | github.com/pkg/errors v0.9.1 11 | github.com/projectdiscovery/clistats v0.0.8 12 | github.com/projectdiscovery/fileutil v0.0.0-20210926202739-6050d0acf73c 13 | github.com/projectdiscovery/goconfig v0.0.0-20210804090219-f893ccd0c69c 14 | github.com/projectdiscovery/goflags v0.0.7 // indirect 15 | github.com/projectdiscovery/gologger v1.1.4 16 | github.com/projectdiscovery/hmap v0.0.1 17 | github.com/projectdiscovery/iputil v0.0.0-20210804143329-3a30fcde43f3 18 | github.com/projectdiscovery/mapcidr v0.0.8 19 | github.com/projectdiscovery/retryabledns v1.0.13-0.20210927160332-db15799e2e4d 20 | github.com/projectdiscovery/stringsutil v0.0.0-20210830151154-f567170afdd9 // indirect 21 | github.com/rs/xid v1.3.0 22 | go.uber.org/atomic v1.9.0 // indirect 23 | go.uber.org/ratelimit v0.2.0 24 | golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 // indirect 25 | golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 // indirect 26 | golang.org/x/text v0.3.7 // indirect 27 | ) 28 | -------------------------------------------------------------------------------- /.github/workflows/dockerhub-push.yml: -------------------------------------------------------------------------------- 1 | name: 🌥 Docker Push 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["🎉 Release Binary"] 6 | types: 7 | - completed 8 | workflow_dispatch: 9 | 10 | jobs: 11 | docker: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Git Checkout 15 | uses: actions/checkout@v2 16 | 17 | - name: Get Github tag 18 | id: meta 19 | run: | 20 | echo "::set-output name=tag::$(curl --silent "https://api.github.com/repos/projectdiscovery/dnsx/releases/latest" | jq -r .tag_name)" 21 | 22 | - name: Set up QEMU 23 | uses: docker/setup-qemu-action@v1 24 | 25 | - name: Set up Docker Buildx 26 | uses: docker/setup-buildx-action@v1 27 | 28 | - name: Login to DockerHub 29 | uses: docker/login-action@v1 30 | with: 31 | username: ${{ secrets.DOCKER_USERNAME }} 32 | password: ${{ secrets.DOCKER_TOKEN }} 33 | 34 | - name: Build and push 35 | uses: docker/build-push-action@v2 36 | with: 37 | context: . 38 | platforms: linux/amd64,linux/arm64,linux/arm 39 | push: true 40 | tags: projectdiscovery/dnsx:latest,projectdiscovery/dnsx:${{ steps.meta.outputs.tag }} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue report 3 | about: Create a report to help us to improve the project 4 | labels: 'Type: Bug' 5 | 6 | --- 7 | 8 | 13 | 14 | 15 | 16 | ### dnsx version: 17 | 18 | 19 | 20 | 21 | ### Current Behavior: 22 | 23 | 24 | ### Expected Behavior: 25 | 26 | 27 | ### Steps To Reproduce: 28 | 33 | 34 | 35 | ### Anything else: 36 | -------------------------------------------------------------------------------- /internal/runner/util.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net/url" 7 | "os" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func linesInFile(fileName string) ([]string, error) { 13 | result := []string{} 14 | f, err := os.Open(fileName) 15 | if err != nil { 16 | return result, err 17 | } 18 | defer f.Close() 19 | scanner := bufio.NewScanner(f) 20 | for scanner.Scan() { 21 | line := scanner.Text() 22 | result = append(result, line) 23 | } 24 | return result, nil 25 | } 26 | 27 | // isURL tests a string to determine if it is a well-structured url or not. 28 | func isURL(toTest string) bool { 29 | _, err := url.ParseRequestURI(toTest) 30 | if err != nil { 31 | return false 32 | } 33 | 34 | u, err := url.Parse(toTest) 35 | if err != nil || u.Scheme == "" || u.Host == "" { 36 | return false 37 | } 38 | 39 | return true 40 | } 41 | 42 | func extractDomain(URL string) string { 43 | u, err := url.Parse(URL) 44 | if err != nil { 45 | return "" 46 | } 47 | 48 | return u.Hostname() 49 | } 50 | 51 | func prepareResolver(resolver string) string { 52 | resolver = strings.TrimSpace(resolver) 53 | if !strings.Contains(resolver, ":") { 54 | resolver += ":53" 55 | } 56 | return resolver 57 | } 58 | 59 | func fmtDuration(d time.Duration) string { 60 | d = d.Round(time.Second) 61 | h := d / time.Hour 62 | d -= h * time.Hour 63 | m := d / time.Minute 64 | d -= m * time.Minute 65 | s := d / time.Second 66 | return fmt.Sprintf("%d:%02d:%02d", h, m, s) 67 | } 68 | -------------------------------------------------------------------------------- /cmd/integration-test/integration-test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/logrusorgru/aurora" 9 | "github.com/projectdiscovery/dnsx/internal/testutils" 10 | ) 11 | 12 | var ( 13 | debug = os.Getenv("DEBUG") == "true" 14 | customTest = os.Getenv("TEST") 15 | protocol = os.Getenv("PROTO") 16 | 17 | errored = false 18 | ) 19 | 20 | func main() { 21 | success := aurora.Green("[✓]").String() 22 | failed := aurora.Red("[✘]").String() 23 | 24 | tests := map[string]map[string]testutils.TestCase{ 25 | "dns": dnsTestcases, 26 | } 27 | for proto, tests := range tests { 28 | if protocol == "" || protocol == proto { 29 | fmt.Printf("Running test cases for \"%s\"\n", aurora.Blue(proto)) 30 | 31 | for name, test := range tests { 32 | if customTest != "" && !strings.Contains(name, customTest) { 33 | continue // only run tests user asked 34 | } 35 | err := test.Execute() 36 | if err != nil { 37 | fmt.Fprintf(os.Stderr, "%s Test \"%s\" failed: %s\n", failed, name, err) 38 | errored = true 39 | } else { 40 | fmt.Printf("%s Test \"%s\" passed!\n", success, name) 41 | } 42 | } 43 | } 44 | } 45 | if errored { 46 | os.Exit(1) 47 | } 48 | } 49 | 50 | func errIncorrectResultsCount(results []string) error { 51 | return fmt.Errorf("incorrect number of results %s", strings.Join(results, "\n\t")) 52 | } 53 | 54 | func errIncorrectResult(expected, got string) error { 55 | return fmt.Errorf("incorrect result: expected \"%s\" got \"%s\"", expected, got) 56 | } 57 | -------------------------------------------------------------------------------- /internal/testutils/integration.go: -------------------------------------------------------------------------------- 1 | package testutils 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "strings" 8 | ) 9 | 10 | // RunDnsxAndGetResults returns a list of results 11 | func RunDnsxAndGetResults(question string, debug bool, extra ...string) ([]string, error) { 12 | cmd := exec.Command("bash", "-c") 13 | cmdLine := `echo "` + question + `" | ./dnsx ` 14 | cmdLine += strings.Join(extra, " ") 15 | if debug { 16 | cmdLine += " -debug" 17 | cmd.Stderr = os.Stderr 18 | } else { 19 | cmdLine += " -silent" 20 | } 21 | 22 | cmd.Args = append(cmd.Args, cmdLine) 23 | 24 | data, err := cmd.Output() 25 | if err != nil { 26 | return nil, err 27 | } 28 | parts := []string{} 29 | items := strings.Split(string(data), "\n") 30 | for _, i := range items { 31 | if i != "" { 32 | parts = append(parts, i) 33 | } 34 | } 35 | return parts, nil 36 | } 37 | func RunDnsxBinaryAndGetResults(target string, dnsxBinary string, debug bool, args []string) ([]string, error) { 38 | cmd := exec.Command("bash", "-c") 39 | cmdLine := fmt.Sprintf(`echo %s | %s `, target, dnsxBinary) 40 | cmdLine += strings.Join(args, " ") 41 | if debug { 42 | cmdLine += " -debug" 43 | cmd.Stderr = os.Stderr 44 | } else { 45 | cmdLine += " -silent" 46 | } 47 | 48 | cmd.Args = append(cmd.Args, cmdLine) 49 | data, err := cmd.Output() 50 | if err != nil { 51 | return nil, err 52 | } 53 | parts := []string{} 54 | items := strings.Split(string(data), "\n") 55 | for _, i := range items { 56 | if i != "" { 57 | parts = append(parts, i) 58 | } 59 | } 60 | return parts,nil 61 | } 62 | 63 | // TestCase is a single integration test case 64 | type TestCase interface { 65 | // Execute executes a test case and returns any errors if occurred 66 | Execute() error 67 | } 68 | -------------------------------------------------------------------------------- /internal/runner/wildcard.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/rs/xid" 7 | ) 8 | 9 | // IsWildcard checks if a host is wildcard 10 | func (r *Runner) IsWildcard(host string) bool { 11 | orig := make(map[string]struct{}) 12 | wildcards := make(map[string]struct{}) 13 | 14 | in, err := r.dnsx.QueryOne(host) 15 | if err != nil || in == nil { 16 | return false 17 | } 18 | for _, A := range in.A { 19 | orig[A] = struct{}{} 20 | } 21 | 22 | subdomainPart := strings.TrimSuffix(host, "."+r.options.WildcardDomain) 23 | subdomainTokens := strings.Split(subdomainPart, ".") 24 | 25 | // Build an array by preallocating a slice of a length 26 | // and create the wildcard generation prefix. 27 | // We use a rand prefix at the beginning like %rand%.domain.tld 28 | // A permutation is generated for each level of the subdomain. 29 | var hosts []string 30 | hosts = append(hosts, r.options.WildcardDomain) 31 | 32 | if len(subdomainTokens) > 0 { 33 | for i := 1; i < len(subdomainTokens); i++ { 34 | newhost := strings.Join(subdomainTokens[i:], ".") + "." + r.options.WildcardDomain 35 | hosts = append(hosts, newhost) 36 | } 37 | } 38 | 39 | // Iterate over all the hosts generated for rand. 40 | for _, h := range hosts { 41 | r.wildcardscachemutex.Lock() 42 | listip, ok := r.wildcardscache[h] 43 | r.wildcardscachemutex.Unlock() 44 | if !ok { 45 | in, err := r.dnsx.QueryOne(xid.New().String() + "." + h) 46 | if err != nil || in == nil { 47 | continue 48 | } 49 | listip = in.A 50 | r.wildcardscachemutex.Lock() 51 | r.wildcardscache[h] = in.A 52 | r.wildcardscachemutex.Unlock() 53 | } 54 | 55 | // Get all the records and add them to the wildcard map 56 | for _, A := range listip { 57 | if _, ok := wildcards[A]; !ok { 58 | wildcards[A] = struct{}{} 59 | } 60 | } 61 | } 62 | 63 | // check if original ip are among wildcards 64 | for a := range orig { 65 | if _, ok := wildcards[a]; ok { 66 | return true 67 | } 68 | } 69 | 70 | return false 71 | } 72 | -------------------------------------------------------------------------------- /cmd/functional-test/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "strings" 10 | 11 | "github.com/logrusorgru/aurora" 12 | "github.com/pkg/errors" 13 | 14 | "github.com/projectdiscovery/dnsx/internal/testutils" 15 | ) 16 | 17 | var ( 18 | debug = os.Getenv("DEBUG") == "true" 19 | success = aurora.Green("[✓]").String() 20 | failed = aurora.Red("[✘]").String() 21 | errored = false 22 | 23 | mainDnsxBinary = flag.String("main", "", "Main Branch Dnsx Binary") 24 | devDnsxBinary = flag.String("dev", "", "Dev Branch Dnsx Binary") 25 | testcases = flag.String("testcases", "", "Test cases file for dnsx functional tests") 26 | ) 27 | 28 | func main() { 29 | flag.Parse() 30 | 31 | if err := runFunctionalTests(); err != nil { 32 | log.Fatalf("Could not run functional tests: %s\n", err) 33 | } 34 | if errored { 35 | os.Exit(1) 36 | } 37 | } 38 | 39 | func runFunctionalTests() error { 40 | file, err := os.Open(*testcases) 41 | if err != nil { 42 | return errors.Wrap(err, "could not open test cases") 43 | } 44 | defer file.Close() 45 | 46 | scanner := bufio.NewScanner(file) 47 | for scanner.Scan() { 48 | text := strings.TrimSpace(scanner.Text()) 49 | if text == "" { 50 | continue 51 | } 52 | if err := runIndividualTestCase(text); err != nil { 53 | errored = true 54 | fmt.Fprintf(os.Stderr, "%s Test \"%s\" failed: %s\n", failed, text, err) 55 | } else { 56 | fmt.Printf("%s Test \"%s\" passed!\n", success, text) 57 | } 58 | } 59 | return nil 60 | } 61 | 62 | func runIndividualTestCase(testcase string) error { 63 | parts := strings.Fields(testcase) 64 | 65 | var finalArgs []string 66 | var target string 67 | if len(parts) > 1 { 68 | finalArgs = parts[2:] 69 | target = parts[0] 70 | } 71 | mainOutput, err := testutils.RunDnsxBinaryAndGetResults(target, *mainDnsxBinary, debug, finalArgs) 72 | if err != nil { 73 | return errors.Wrap(err, "could not run dnsx main test") 74 | } 75 | devOutput, err := testutils.RunDnsxBinaryAndGetResults(target, *devDnsxBinary, debug, finalArgs) 76 | if err != nil { 77 | return errors.Wrap(err, "could not run dnsx dev test") 78 | } 79 | if len(mainOutput) == len(devOutput) { 80 | return nil 81 | } 82 | return fmt.Errorf("%s main is not equal to %s dev", mainOutput, devOutput) 83 | } 84 | -------------------------------------------------------------------------------- /libs/dnsx/dnsx.go: -------------------------------------------------------------------------------- 1 | package dnsx 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | 7 | miekgdns "github.com/miekg/dns" 8 | "github.com/projectdiscovery/iputil" 9 | retryabledns "github.com/projectdiscovery/retryabledns" 10 | ) 11 | 12 | // DNSX is structure to perform dns lookups 13 | type DNSX struct { 14 | dnsClient *retryabledns.Client 15 | Options *Options 16 | } 17 | 18 | // Options contains configuration options 19 | type Options struct { 20 | BaseResolvers []string 21 | MaxRetries int 22 | QuestionTypes []uint16 23 | Trace bool 24 | TraceMaxRecursion int 25 | Hostsfile bool 26 | } 27 | 28 | // DefaultOptions contains the default configuration options 29 | var DefaultOptions = Options{ 30 | BaseResolvers: DefaultResolvers, 31 | MaxRetries: 5, 32 | QuestionTypes: []uint16{miekgdns.TypeA}, 33 | TraceMaxRecursion: math.MaxUint16, 34 | Hostsfile: true, 35 | } 36 | 37 | // DefaultResolvers contains the list of resolvers known to be trusted. 38 | var DefaultResolvers = []string{ 39 | "udp:1.1.1.1:53", // Cloudflare 40 | "udp:1.0.0.1:53", // Cloudflare 41 | "udp:8.8.8.8:53", // Google 42 | "udp:8.8.4.4:53", // Google 43 | } 44 | 45 | // New creates a dns resolver 46 | func New(options Options) (*DNSX, error) { 47 | retryablednsOptions := retryabledns.Options{ 48 | BaseResolvers: options.BaseResolvers, 49 | MaxRetries: options.MaxRetries, 50 | Hostsfile: options.Hostsfile, 51 | } 52 | 53 | dnsClient := retryabledns.NewWithOptions(retryablednsOptions) 54 | 55 | return &DNSX{dnsClient: dnsClient, Options: &options}, nil 56 | } 57 | 58 | // Lookup performs a DNS A question and returns corresponding IPs 59 | func (d *DNSX) Lookup(hostname string) ([]string, error) { 60 | if iputil.IsIP(hostname) { 61 | return []string{hostname}, nil 62 | } 63 | 64 | dnsdata, err := d.dnsClient.Resolve(hostname) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | if dnsdata == nil || len(dnsdata.A) == 0 { 70 | return []string{}, errors.New("no ips found") 71 | } 72 | 73 | return dnsdata.A, nil 74 | } 75 | 76 | // QueryOne performs a DNS question of a specified type and returns raw responses 77 | func (d *DNSX) QueryOne(hostname string) (*retryabledns.DNSData, error) { 78 | return d.dnsClient.Query(hostname, d.Options.QuestionTypes[0]) 79 | } 80 | 81 | // QueryMultiple performs a DNS question of the specified types and returns raw responses 82 | func (d *DNSX) QueryMultiple(hostname string) (*retryabledns.DNSData, error) { 83 | return d.dnsClient.QueryMultiple(hostname, d.Options.QuestionTypes) 84 | } 85 | 86 | // Trace performs a DNS trace of the specified types and returns raw responses 87 | func (d *DNSX) Trace(hostname string) (*retryabledns.TraceData, error) { 88 | return d.dnsClient.Trace(hostname, d.Options.QuestionTypes[0], d.Options.TraceMaxRecursion) 89 | } 90 | -------------------------------------------------------------------------------- /cmd/integration-test/dns.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | "strings" 6 | 7 | "github.com/miekg/dns" 8 | "github.com/projectdiscovery/dnsx/internal/testutils" 9 | ) 10 | 11 | var dnsTestcases = map[string]testutils.TestCase{ 12 | "DNS A Request": &dnsARequest{question: "projectdiscovery.io", expectedOutput: "projectdiscovery.io"}, 13 | "DNS AAAA Request": &dnsAAAARequest{question: "projectdiscovery.io", expectedOutput: "projectdiscovery.io"}, 14 | } 15 | 16 | type dnsARequest struct { 17 | question string 18 | expectedOutput string 19 | } 20 | 21 | func (h *dnsARequest) Execute() error { 22 | handler := &dnshandler{ 23 | answers: []answer{ 24 | {question: h.question, questionType: dns.TypeA, values: []string{"1.2.3.4"}}, 25 | }, 26 | } 27 | srv := &dns.Server{ 28 | Handler: handler, 29 | Addr: "127.0.0.1:15000", 30 | Net: "udp", 31 | } 32 | go srv.ListenAndServe() //nolint 33 | defer srv.Shutdown() //nolint 34 | 35 | var extra []string 36 | extra = append(extra, "-r", "127.0.0.1:15000") 37 | extra = append(extra, "-a") 38 | 39 | results, err := testutils.RunDnsxAndGetResults(h.question, debug, extra...) 40 | if err != nil { 41 | return err 42 | } 43 | if len(results) != 1 { 44 | return errIncorrectResultsCount(results) 45 | } 46 | 47 | if h.expectedOutput != "" && !strings.EqualFold(results[0], h.expectedOutput) { 48 | return errIncorrectResult(results[0], h.expectedOutput) 49 | } 50 | 51 | return nil 52 | } 53 | 54 | type dnsAAAARequest struct { 55 | question string 56 | expectedOutput string 57 | } 58 | 59 | func (h *dnsAAAARequest) Execute() error { 60 | handler := &dnshandler{ 61 | answers: []answer{ 62 | {question: h.question, questionType: dns.TypeAAAA, values: []string{"2001:db8:3333:4444:5555:6666:7777:8888"}}, 63 | }, 64 | } 65 | srv := &dns.Server{ 66 | Handler: handler, 67 | Addr: "127.0.0.1:15000", 68 | Net: "udp", 69 | } 70 | go srv.ListenAndServe() //nolint 71 | defer srv.Shutdown() //nolint 72 | 73 | var extra []string 74 | extra = append(extra, "-r", "127.0.0.1:15000") 75 | extra = append(extra, "-aaaa") 76 | 77 | results, err := testutils.RunDnsxAndGetResults(h.question, debug, extra...) 78 | if err != nil { 79 | return err 80 | } 81 | if len(results) != 1 { 82 | return errIncorrectResultsCount(results) 83 | } 84 | 85 | if h.expectedOutput != "" && !strings.EqualFold(results[0], h.expectedOutput) { 86 | return errIncorrectResult(results[0], h.expectedOutput) 87 | } 88 | 89 | return nil 90 | } 91 | 92 | type answer struct { 93 | question string 94 | questionType uint16 95 | values []string 96 | } 97 | 98 | type dnshandler struct { 99 | answers []answer 100 | } 101 | 102 | func (t *dnshandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { 103 | question := r.Question[0].Name 104 | question = strings.TrimSuffix(question, ".") 105 | questionType := r.Question[0].Qtype 106 | for _, answer := range t.answers { 107 | if strings.EqualFold(question, answer.question) && answer.questionType == questionType { 108 | resp := buildAnswer(r, answer) 109 | w.WriteMsg(resp) //nolint 110 | } 111 | } 112 | } 113 | 114 | func buildAnswer(r *dns.Msg, ans answer) *dns.Msg { 115 | msg := dns.Msg{} 116 | msg.SetReply(r) 117 | msg.Authoritative = true 118 | switch ans.questionType { 119 | case dns.TypeA: 120 | for _, value := range ans.values { 121 | msg.Answer = append(msg.Answer, &dns.A{ 122 | Hdr: dns.RR_Header{Name: dns.Fqdn(ans.question), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60}, 123 | A: net.ParseIP(value), 124 | }) 125 | } 126 | case dns.TypeAAAA: 127 | for _, value := range ans.values { 128 | msg.Answer = append(msg.Answer, &dns.AAAA{ 129 | Hdr: dns.RR_Header{Name: dns.Fqdn(ans.question), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 60}, 130 | AAAA: net.ParseIP(value), 131 | }) 132 | } 133 | } 134 | return &msg 135 | } 136 | -------------------------------------------------------------------------------- /internal/runner/options.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "errors" 5 | "math" 6 | "os" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/projectdiscovery/fileutil" 11 | "github.com/projectdiscovery/goconfig" 12 | "github.com/projectdiscovery/goflags" 13 | "github.com/projectdiscovery/gologger" 14 | "github.com/projectdiscovery/gologger/levels" 15 | ) 16 | 17 | const ( 18 | DefaultResumeFile = "resume.cfg" 19 | ) 20 | 21 | type Options struct { 22 | Resolvers string 23 | Hosts string 24 | Threads int 25 | RateLimit int 26 | Retries int 27 | OutputFormat string 28 | OutputFile string 29 | Raw bool 30 | Silent bool 31 | Verbose bool 32 | Version bool 33 | Response bool 34 | ResponseOnly bool 35 | A bool 36 | AAAA bool 37 | NS bool 38 | CNAME bool 39 | PTR bool 40 | MX bool 41 | SOA bool 42 | TXT bool 43 | JSON bool 44 | Trace bool 45 | TraceMaxRecursion int 46 | WildcardThreshold int 47 | WildcardDomain string 48 | ShowStatistics bool 49 | rcodes map[int]struct{} 50 | RCode string 51 | hasRCodes bool 52 | Resume bool 53 | resumeCfg *ResumeCfg 54 | FlushInterval int 55 | HostsFile bool 56 | } 57 | 58 | // ShouldLoadResume resume file 59 | func (options *Options) ShouldLoadResume() bool { 60 | return options.Resume && fileutil.FileExists(DefaultResumeFile) 61 | } 62 | 63 | // ShouldSaveResume file 64 | func (options *Options) ShouldSaveResume() bool { 65 | return true 66 | } 67 | 68 | // ParseOptions parses the command line options for application 69 | func ParseOptions() *Options { 70 | options := &Options{} 71 | flagSet := goflags.NewFlagSet() 72 | flagSet.SetDescription(`dnsx is a fast and multi-purpose DNS toolkit allow to run multiple probes using retryabledns library.`) 73 | 74 | createGroup(flagSet, "input", "Input", 75 | flagSet.StringVarP(&options.Hosts, "list", "l", "", "File input with list of sub(domains)/hosts"), 76 | ) 77 | 78 | createGroup(flagSet, "query", "Query", 79 | flagSet.BoolVar(&options.A, "a", false, "Query A record (default)"), 80 | flagSet.BoolVar(&options.AAAA, "aaaa", false, "Query AAAA record"), 81 | flagSet.BoolVar(&options.CNAME, "cname", false, "Query CNAME record"), 82 | flagSet.BoolVar(&options.NS, "ns", false, "Query NS record"), 83 | flagSet.BoolVar(&options.TXT, "txt", false, "Query TXT record"), 84 | flagSet.BoolVar(&options.PTR, "ptr", false, "Query PTR record"), 85 | flagSet.BoolVar(&options.MX, "mx", false, "Query MX record"), 86 | flagSet.BoolVar(&options.SOA, "soa", false, "Query SOA record"), 87 | ) 88 | 89 | createGroup(flagSet, "filters", "Filters", 90 | flagSet.BoolVar(&options.Response, "resp", false, "Display DNS response"), 91 | flagSet.BoolVar(&options.ResponseOnly, "resp-only", false, "Display DNS response only"), 92 | flagSet.StringVarP(&options.RCode, "rc", "rcode", "", "Display DNS status code (eg. -rcode noerror,servfail,refused)"), 93 | ) 94 | 95 | createGroup(flagSet, "rate-limit", "Rate-limit", 96 | flagSet.IntVarP(&options.Threads, "c", "t", 100, "Number of concurrent threads to use"), 97 | flagSet.IntVarP(&options.RateLimit, "rate-limit", "rl", -1, "Number of DNS request/second (disabled as default)"), 98 | ) 99 | 100 | createGroup(flagSet, "output", "Output", 101 | flagSet.StringVarP(&options.OutputFile, "output", "o", "", "File to write output"), 102 | flagSet.BoolVar(&options.JSON, "json", false, "Write output in JSONL(ines) format"), 103 | ) 104 | 105 | createGroup(flagSet, "debug", "Debug", 106 | flagSet.BoolVar(&options.Silent, "silent", false, "Show only results in the output"), 107 | flagSet.BoolVarP(&options.Verbose, "verbose", "v", false, "Verbose output"), 108 | flagSet.BoolVarP(&options.Raw, "debug", "raw", false, "Display RAW DNS response"), 109 | flagSet.BoolVar(&options.ShowStatistics, "stats", false, "Display stats of the running scan"), 110 | flagSet.BoolVar(&options.Version, "version", false, "Show version of dnsx"), 111 | ) 112 | 113 | createGroup(flagSet, "optimization", "Optimization", 114 | flagSet.IntVar(&options.Retries, "retry", 1, "Number of DNS retries"), 115 | flagSet.BoolVarP(&options.HostsFile, "hostsfile", "hf", false, "Parse system host file"), 116 | flagSet.BoolVar(&options.Trace, "trace", false, "Perform DNS trace"), 117 | flagSet.IntVar(&options.TraceMaxRecursion, "trace-max-recursion", math.MaxInt16, "Max recursion for dns trace"), 118 | flagSet.IntVar(&options.FlushInterval, "flush-interval", 10, "Flush interval of output file"), 119 | flagSet.BoolVar(&options.Resume, "resume", false, "Resume"), 120 | ) 121 | 122 | createGroup(flagSet, "configs", "Configurations", 123 | flagSet.StringVarP(&options.Resolvers, "resolver", "r", "", "List of resolvers (file or comma separated)"), 124 | flagSet.IntVarP(&options.WildcardThreshold, "wildcard-threshold", "wt", 5, "Wildcard Filter Threshold"), 125 | flagSet.StringVarP(&options.WildcardDomain, "wildcard-domain", "wd", "", "Domain name for wildcard filtering (other flags will be ignored)"), 126 | ) 127 | 128 | _ = flagSet.Parse() 129 | 130 | // Read the inputs and configure the logging 131 | options.configureOutput() 132 | 133 | err := options.configureRcodes() 134 | if err != nil { 135 | gologger.Fatal().Msgf("%s\n", err) 136 | } 137 | 138 | err = options.configureResume() 139 | if err != nil { 140 | gologger.Fatal().Msgf("%s\n", err) 141 | } 142 | 143 | showBanner() 144 | 145 | if options.Version { 146 | gologger.Info().Msgf("Current Version: %s\n", Version) 147 | os.Exit(0) 148 | } 149 | 150 | options.validateOptions() 151 | 152 | return options 153 | } 154 | 155 | func (options *Options) validateOptions() { 156 | if options.Response && options.ResponseOnly { 157 | gologger.Fatal().Msgf("resp and resp-only can't be used at the same time") 158 | } 159 | } 160 | 161 | // configureOutput configures the output on the screen 162 | func (options *Options) configureOutput() { 163 | // If the user desires verbose output, show verbose output 164 | if options.Verbose { 165 | gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose) 166 | } 167 | if options.Silent { 168 | gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent) 169 | } 170 | } 171 | 172 | func (options *Options) configureRcodes() error { 173 | options.rcodes = make(map[int]struct{}) 174 | rcodes := strings.Split(options.RCode, ",") 175 | for _, rcode := range rcodes { 176 | var rc int 177 | switch strings.ToLower(rcode) { 178 | case "": 179 | continue 180 | case "noerror": 181 | rc = 0 182 | case "formerr": 183 | rc = 1 184 | case "servfail": 185 | rc = 2 186 | case "nxdomain": 187 | rc = 3 188 | case "notimp": 189 | rc = 4 190 | case "refused": 191 | rc = 5 192 | case "yxdomain": 193 | rc = 6 194 | case "yxrrset": 195 | rc = 7 196 | case "nxrrset": 197 | rc = 8 198 | case "notauth": 199 | rc = 9 200 | case "notzone": 201 | rc = 10 202 | case "badsig", "badvers": 203 | rc = 16 204 | case "badkey": 205 | rc = 17 206 | case "badtime": 207 | rc = 18 208 | case "badmode": 209 | rc = 19 210 | case "badname": 211 | rc = 20 212 | case "badalg": 213 | rc = 21 214 | case "badtrunc": 215 | rc = 22 216 | case "badcookie": 217 | rc = 23 218 | default: 219 | var err error 220 | rc, err = strconv.Atoi(rcode) 221 | if err != nil { 222 | return errors.New("invalid rcode value") 223 | } 224 | } 225 | 226 | options.rcodes[rc] = struct{}{} 227 | } 228 | 229 | options.hasRCodes = options.RCode != "" 230 | 231 | // Set rcode to 0 if none was specified 232 | if len(options.rcodes) == 0 { 233 | options.rcodes[0] = struct{}{} 234 | } 235 | 236 | return nil 237 | } 238 | 239 | func (options *Options) configureResume() error { 240 | options.resumeCfg = &ResumeCfg{} 241 | if options.Resume && fileutil.FileExists(DefaultResumeFile) { 242 | return goconfig.Load(&options.resumeCfg, DefaultResumeFile) 243 | 244 | } 245 | return nil 246 | } 247 | 248 | func createGroup(flagSet *goflags.FlagSet, groupName, description string, flags ...*goflags.FlagData) { 249 | flagSet.SetGroup(groupName, description) 250 | for _, currentFlag := range flags { 251 | currentFlag.Group(groupName) 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
3 | 20 | Features • 21 | Installation • 22 | Usage • 23 | Running dnsx • 24 | Wildcard • 25 | Notes • 26 | Join Discord 27 |
28 | 29 | 30 | --- 31 | 32 | 33 | **dnsx** is a fast and multi-purpose DNS toolkit allow to run multiple probes using [retryabledns](https://github.com/projectdiscovery/retryabledns) library, that allows you to perform multiple DNS queries of your choice with a list of user supplied resolvers, additionally supports DNS wildcard filtering like [shuffledns](https://github.com/projectdiscovery/shuffledns). 34 | 35 | 36 | # Features 37 | 38 |
40 |