├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── issue-report.md ├── dependabot.yml ├── release.yml └── workflows │ ├── build-test.yml │ ├── codeql-analysis.yml │ ├── dep-auto-merge.yml │ ├── dockerhub-push.yml │ ├── functional-test.yml │ ├── lint-test.yml │ ├── release-binary.yml │ └── release-test.yml ├── .gitignore ├── .goreleaser.yml ├── Dockerfile ├── LICENSE.md ├── MULLVAD.md ├── Makefile ├── README.md ├── SECURITY.md ├── cmd ├── dnsx │ └── dnsx.go ├── functional-test │ ├── main.go │ ├── run.sh │ ├── test-data │ │ └── file.txt │ └── testcases.txt └── integration-test │ ├── dns.go │ └── integration-test.go ├── go.mod ├── go.sum ├── integration_tests └── run.sh ├── internal ├── runner │ ├── banner.go │ ├── doc.go │ ├── healthcheck.go │ ├── options.go │ ├── resume.go │ ├── runner.go │ ├── runner_test.go │ ├── tests │ │ ├── AS14421.txt │ │ ├── file_input.txt │ │ └── stream_input.txt │ ├── util.go │ └── wildcard.go └── testutils │ └── integration.go ├── libs └── dnsx │ ├── cdn.go │ ├── dnsx.go │ ├── doc.go │ └── util.go └── static ├── dnsx-logo.png └── dnsx-run.png /.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/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | - package-ecosystem: "gomod" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | target-branch: "dev" 13 | commit-message: 14 | prefix: "chore" 15 | include: "scope" 16 | allow: 17 | - dependency-name: "github.com/projectdiscovery/*" 18 | groups: 19 | modules: 20 | patterns: ["github.com/projectdiscovery/*"] 21 | labels: 22 | - "Type: Maintenance" 23 | 24 | # # Maintain dependencies for docker 25 | # - package-ecosystem: "docker" 26 | # directory: "/" 27 | # schedule: 28 | # interval: "weekly" 29 | # target-branch: "dev" 30 | # commit-message: 31 | # prefix: "chore" 32 | # include: "scope" 33 | # labels: 34 | # - "Type: Maintenance" 35 | # 36 | # # Maintain dependencies for GitHub Actions 37 | # - package-ecosystem: "github-actions" 38 | # directory: "/" 39 | # schedule: 40 | # interval: "weekly" 41 | # target-branch: "dev" 42 | # commit-message: 43 | # prefix: "chore" 44 | # include: "scope" 45 | # labels: 46 | # - "Type: Maintenance" -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | authors: 4 | - dependabot 5 | categories: 6 | - title: 🎉 New Features 7 | labels: 8 | - "Type: Enhancement" 9 | - title: 🐞 Bugs Fixes 10 | labels: 11 | - "Type: Bug" 12 | - title: 🔨 Maintenance 13 | labels: 14 | - "Type: Maintenance" 15 | - title: Other Changes 16 | labels: 17 | - "*" -------------------------------------------------------------------------------- /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | name: 🔨 Build Test 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '**.go' 7 | - '**.mod' 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | name: Test Builds 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, windows-latest, macOS-latest] 17 | steps: 18 | - name: Set up Go 19 | uses: actions/setup-go@v4 20 | with: 21 | go-version: 1.21.x 22 | 23 | - name: Check out code 24 | uses: actions/checkout@v3 25 | 26 | - name: Build 27 | run: go build . 28 | working-directory: cmd/dnsx/ 29 | 30 | - name: Test 31 | run: go test ./... 32 | working-directory: . 33 | env: 34 | GH_ACTION: true 35 | PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}" 36 | 37 | - name: Integration Tests 38 | env: 39 | GH_ACTION: true 40 | PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}" 41 | run: bash run.sh 42 | working-directory: integration_tests/ 43 | 44 | - name: Race Condition Tests 45 | run: go run -race . 46 | working-directory: cmd/dnsx/ 47 | 48 | 49 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: 🚨 CodeQL Analysis 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | paths: 7 | - '**.go' 8 | - '**.mod' 9 | branches: 10 | - dev 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'go' ] 25 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 26 | 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v3 30 | 31 | # Initializes the CodeQL tools for scanning. 32 | - name: Initialize CodeQL 33 | uses: github/codeql-action/init@v2 34 | with: 35 | languages: ${{ matrix.language }} 36 | 37 | - name: Autobuild 38 | uses: github/codeql-action/autobuild@v2 39 | 40 | - name: Perform CodeQL Analysis 41 | uses: github/codeql-action/analyze@v2 -------------------------------------------------------------------------------- /.github/workflows/dep-auto-merge.yml: -------------------------------------------------------------------------------- 1 | name: 🤖 dep auto merge 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - dev 7 | workflow_dispatch: 8 | 9 | permissions: 10 | pull-requests: write 11 | issues: write 12 | repository-projects: write 13 | 14 | jobs: 15 | automerge: 16 | runs-on: ubuntu-latest 17 | if: github.actor == 'dependabot[bot]' 18 | steps: 19 | - uses: actions/checkout@v3 20 | with: 21 | token: ${{ secrets.DEPENDABOT_PAT }} 22 | 23 | - uses: ahmadnassri/action-dependabot-auto-merge@v2 24 | with: 25 | github-token: ${{ secrets.DEPENDABOT_PAT }} 26 | target: all -------------------------------------------------------------------------------- /.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-16-cores 13 | steps: 14 | - name: Git Checkout 15 | uses: actions/checkout@v3 16 | 17 | - name: Get Github tag 18 | id: meta 19 | run: | 20 | curl --silent "https://api.github.com/repos/projectdiscovery/dnsx/releases/latest" | jq -r .tag_name | xargs -I {} echo TAG={} >> $GITHUB_OUTPUT 21 | 22 | - name: Set up QEMU 23 | uses: docker/setup-qemu-action@v2 24 | 25 | - name: Set up Docker Buildx 26 | uses: docker/setup-buildx-action@v2 27 | 28 | - name: Login to DockerHub 29 | uses: docker/login-action@v2 30 | with: 31 | username: ${{ secrets.DOCKER_USERNAME }} 32 | password: ${{ secrets.DOCKER_TOKEN }} 33 | 34 | - name: Build and push 35 | uses: docker/build-push-action@v4 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/workflows/functional-test.yml: -------------------------------------------------------------------------------- 1 | name: 🧪 Functional Test 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | 8 | jobs: 9 | functional: 10 | name: Functional Test 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, windows-latest, macOS-latest] 15 | steps: 16 | - name: Set up Go 17 | uses: actions/setup-go@v4 18 | with: 19 | go-version: 1.21.x 20 | 21 | - name: Check out code 22 | uses: actions/checkout@v3 23 | 24 | - name: Functional Tests 25 | run: | 26 | chmod +x run.sh 27 | bash run.sh ${{ matrix.os }} 28 | env: 29 | PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}" 30 | working-directory: cmd/functional-test 31 | -------------------------------------------------------------------------------- /.github/workflows/lint-test.yml: -------------------------------------------------------------------------------- 1 | name: 🙏🏻 Lint Test 2 | 3 | on: 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@v3 14 | - name: "Set up Go" 15 | uses: actions/setup-go@v4 16 | with: 17 | go-version: 1.21.x 18 | - name: Run golangci-lint 19 | uses: golangci/golangci-lint-action@v3.6.0 20 | with: 21 | version: latest 22 | args: --timeout 5m 23 | working-directory: . -------------------------------------------------------------------------------- /.github/workflows/release-binary.yml: -------------------------------------------------------------------------------- 1 | name: 🎉 Release Binary 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | workflow_dispatch: 8 | 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest-16-cores 12 | steps: 13 | - name: "Check out code" 14 | uses: actions/checkout@v3 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: "Set up Go" 19 | uses: actions/setup-go@v4 20 | with: 21 | go-version: 1.21.x 22 | 23 | - name: "Create release on GitHub" 24 | uses: goreleaser/goreleaser-action@v4 25 | with: 26 | args: "release --clean" 27 | version: latest 28 | workdir: . 29 | env: 30 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 31 | SLACK_WEBHOOK: "${{ secrets.RELEASE_SLACK_WEBHOOK }}" 32 | DISCORD_WEBHOOK_ID: "${{ secrets.DISCORD_WEBHOOK_ID }}" 33 | DISCORD_WEBHOOK_TOKEN: "${{ secrets.DISCORD_WEBHOOK_TOKEN }}" -------------------------------------------------------------------------------- /.github/workflows/release-test.yml: -------------------------------------------------------------------------------- 1 | name: 🔨 Release Test 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '**.go' 7 | - '**.mod' 8 | - '**.yml' 9 | workflow_dispatch: 10 | 11 | jobs: 12 | release-test: 13 | runs-on: ubuntu-latest-16-cores 14 | steps: 15 | - name: "Check out code" 16 | uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Set up Go 21 | uses: actions/setup-go@v4 22 | with: 23 | go-version: 1.21.x 24 | 25 | - name: release test 26 | uses: goreleaser/goreleaser-action@v4 27 | with: 28 | args: "release --clean --snapshot" 29 | version: latest 30 | workdir: . -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cmd/dnsx/dnsx 2 | .DS_Store 3 | dist/ 4 | integration_tests/dnsx 5 | integration_tests/integration-test 6 | cmd/functional-test/dnsx_dev 7 | cmd/functional-test/functional-test 8 | cmd/functional-test/dnsx 9 | cmd/functional-test/log.txt 10 | cmd/functional-test/*.cfg 11 | .vscode/ 12 | -------------------------------------------------------------------------------- /.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 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ if eq .Os "darwin" }}macOS{{ else }}{{ .Os }}{{ end }}_{{ .Arch }}' 32 | 33 | checksum: 34 | algorithm: sha256 35 | 36 | announce: 37 | slack: 38 | enabled: true 39 | channel: '#release' 40 | username: GoReleaser 41 | message_template: 'New Release: {{ .ProjectName }} {{.Tag}} is published! Check it out at {{ .ReleaseURL }}' 42 | 43 | discord: 44 | enabled: true 45 | message_template: '**New Release: {{ .ProjectName }} {{.Tag}}** is published! Check it out at {{ .ReleaseURL }}' -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Base 2 | FROM golang:1.21.4-alpine AS builder 3 | 4 | RUN apk add --no-cache build-base 5 | WORKDIR /app 6 | COPY . /app 7 | RUN go mod download 8 | RUN go build ./cmd/dnsx 9 | 10 | # Release 11 | FROM alpine:3.18.2 12 | RUN apk -U upgrade --no-cache \ 13 | && apk add --no-cache bind-tools ca-certificates 14 | COPY --from=builder /app/dnsx /usr/local/bin/ 15 | 16 | ENTRYPOINT ["dnsx"] -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /MULLVAD.md: -------------------------------------------------------------------------------- 1 | # Usage with [MullvadVPN](https://mullvad.net/) 2 | As explained in [#221](https://github.com/projectdiscovery/dnsx/issues/221), VPN operators sometimes filter high DNS/UDP traffic. To avoid packet loss you can tweak a few settings in the client app. 3 | 4 | - 1. Go to *Settings > VPN settings* and set Wireguard as the default tunnel (the settings we need are only available with Wireguard). 5 | - 2. Still in the same section, raise the MTU (=maximum transmission; determines the largest packet size that can be transmitted through your network) to its maximum of 1420. 6 | - 3. Go back to *Settings > VPN settings* and add a custom DNS server (e.g Cloudfare's 1.1.1.1 & 1.0.0.1). It will disable 'DNS Protection' which is not a problem: it won't mess with our traffic system. 7 | 8 | Happy hacking! s/o to Saber for letting me know about [#221](https://github.com/projectdiscovery/dnsx/issues/221), helped me solve this problem & understand it. If this doesn't solve the problem, open an issue and tag me (@noctisatrae). -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Go parameters 2 | GOCMD=go 3 | GOBUILD=$(GOCMD) build 4 | GOMOD=$(GOCMD) mod 5 | GOTEST=$(GOCMD) test 6 | GOFLAGS := -v 7 | LDFLAGS := -s -w 8 | 9 | ifneq ($(shell go env GOOS),darwin) 10 | LDFLAGS := -extldflags "-static" 11 | endif 12 | 13 | all: build 14 | build: 15 | $(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "dnsx" cmd/dnsx/dnsx.go 16 | test: 17 | $(GOTEST) $(GOFLAGS) ./... 18 | tidy: 19 | $(GOMOD) tidy 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | dnsx 3 |
4 |

5 | 6 |

A fast and multi-purpose DNS toolkit designed for running DNS queries

7 | 8 |

9 | 10 | 11 | 12 | 13 | 14 |

15 | 16 |

17 | Features • 18 | Installation • 19 | Usage • 20 | Running `dnsx` • 21 | Wildcard • 22 | Notes • 23 | Join Discord 24 |

25 | 26 | 27 | --- 28 | 29 | `dnsx` is a fast and multi-purpose DNS toolkit designed for running various probes through the [retryabledns](https://github.com/projectdiscovery/retryabledns) library. It supports multiple DNS queries, user supplied resolvers, DNS wildcard filtering like [shuffledns](https://github.com/projectdiscovery/shuffledns) etc. 30 | 31 | 32 | # Features 33 | 34 |

35 | dnsx 36 |
37 |

38 | 39 | 40 | - Simple and Handy utility to query DNS records. 41 | - **A, AAAA, CNAME, PTR, NS, MX, TXT, SRV, SOA** query support 42 | - DNS **Resolution** / **Brute-force** support 43 | - Custom **resolver** input support 44 | - Multiple resolver format **(TCP/UDP/DOH/DOT)** support 45 | - **stdin** and **stdout** support 46 | - Automatic **wildcard** handling support 47 | 48 | # Installation Instructions 49 | 50 | 51 | `dnsx` requires **go1.21** to install successfully. Run the following command to install the latest version: 52 | 53 | ```sh 54 | go install -v github.com/projectdiscovery/dnsx/cmd/dnsx@latest 55 | ``` 56 | 57 | # Usage 58 | 59 | ```sh 60 | dnsx -h 61 | ``` 62 | 63 | This will display help for the tool. Here are all the switches it supports. 64 | 65 | ```console 66 | INPUT: 67 | -l, -list string list of sub(domains)/hosts to resolve (file or stdin) 68 | -d, -domain string list of domain to bruteforce (file or comma separated or stdin) 69 | -w, -wordlist string list of words to bruteforce (file or comma separated or stdin) 70 | 71 | QUERY: 72 | -a query A record (default) 73 | -aaaa query AAAA record 74 | -cname query CNAME record 75 | -ns query NS record 76 | -txt query TXT record 77 | -srv query SRV record 78 | -ptr query PTR record 79 | -mx query MX record 80 | -soa query SOA record 81 | -any query ANY record 82 | -axfr query AXFR 83 | -caa query CAA record 84 | -recon query all the dns records (a,aaaa,cname,ns,txt,srv,ptr,mx,soa,axfr,caa) 85 | -e, -exclude-type value dns query type to exclude (a,aaaa,cname,ns,txt,srv,ptr,mx,soa,axfr,caa) (default none) 86 | 87 | FILTER: 88 | -re, -resp display dns response 89 | -ro, -resp-only display dns response only 90 | -rc, -rcode string filter result by dns status code (eg. -rcode noerror,servfail,refused) 91 | 92 | PROBE: 93 | -cdn display cdn name 94 | -asn display host asn information 95 | 96 | RATE-LIMIT: 97 | -t, -threads int number of concurrent threads to use (default 100) 98 | -rl, -rate-limit int number of dns request/second to make (disabled as default) (default -1) 99 | 100 | UPDATE: 101 | -up, -update update dnsx to latest version 102 | -duc, -disable-update-check disable automatic dnsx update check 103 | 104 | OUTPUT: 105 | -o, -output string file to write output 106 | -j, -json write output in JSONL(ines) format 107 | -omit-raw, -or omit raw dns response from jsonl output 108 | 109 | DEBUG: 110 | -hc, -health-check run diagnostic check up 111 | -silent display only results in the output 112 | -v, -verbose display verbose output 113 | -raw, -debug display raw dns response 114 | -stats display stats of the running scan 115 | -version display version of dnsx 116 | -nc, -no-color disable color in output 117 | 118 | OPTIMIZATION: 119 | -retry int number of dns attempts to make (must be at least 1) (default 2) 120 | -hf, -hostsfile use system host file 121 | -trace perform dns tracing 122 | -trace-max-recursion int Max recursion for dns trace (default 32767) 123 | -resume resume existing scan 124 | -stream stream mode (wordlist, wildcard, stats and stop/resume will be disabled) 125 | 126 | CONFIGURATIONS: 127 | -auth configure projectdiscovery cloud (pdcp) api key (default true) 128 | -r, -resolver string list of resolvers to use (file or comma separated) 129 | -wt, -wildcard-threshold int wildcard filter threshold (default 5) 130 | -wd, -wildcard-domain string domain name for wildcard filtering (other flags will be ignored - only json output is supported) 131 | ``` 132 | 133 | ## Running dnsx 134 | 135 | ### DNS Resolving 136 | 137 | Filter active hostnames from the list of passive subdomains, obtained from various sources: 138 | 139 | ```console 140 | subfinder -silent -d hackerone.com | dnsx -silent 141 | 142 | a.ns.hackerone.com 143 | www.hackerone.com 144 | api.hackerone.com 145 | docs.hackerone.com 146 | mta-sts.managed.hackerone.com 147 | mta-sts.hackerone.com 148 | resources.hackerone.com 149 | b.ns.hackerone.com 150 | mta-sts.forwarding.hackerone.com 151 | events.hackerone.com 152 | support.hackerone.com 153 | ``` 154 | 155 | Print **A** records for the given list of subdomains: 156 | 157 | ```console 158 | subfinder -silent -d hackerone.com | dnsx -silent -a -resp 159 | 160 | www.hackerone.com [104.16.100.52] 161 | www.hackerone.com [104.16.99.52] 162 | hackerone.com [104.16.99.52] 163 | hackerone.com [104.16.100.52] 164 | api.hackerone.com [104.16.99.52] 165 | api.hackerone.com [104.16.100.52] 166 | mta-sts.forwarding.hackerone.com [185.199.108.153] 167 | mta-sts.forwarding.hackerone.com [185.199.109.153] 168 | mta-sts.forwarding.hackerone.com [185.199.110.153] 169 | mta-sts.forwarding.hackerone.com [185.199.111.153] 170 | a.ns.hackerone.com [162.159.0.31] 171 | resources.hackerone.com [52.60.160.16] 172 | resources.hackerone.com [3.98.63.202] 173 | resources.hackerone.com [52.60.165.183] 174 | resources.hackerone.com [read.uberflip.com] 175 | mta-sts.hackerone.com [185.199.110.153] 176 | mta-sts.hackerone.com [185.199.111.153] 177 | mta-sts.hackerone.com [185.199.109.153] 178 | mta-sts.hackerone.com [185.199.108.153] 179 | gslink.hackerone.com [13.35.210.17] 180 | gslink.hackerone.com [13.35.210.38] 181 | gslink.hackerone.com [13.35.210.83] 182 | gslink.hackerone.com [13.35.210.19] 183 | b.ns.hackerone.com [162.159.1.31] 184 | docs.hackerone.com [185.199.109.153] 185 | docs.hackerone.com [185.199.110.153] 186 | docs.hackerone.com [185.199.111.153] 187 | docs.hackerone.com [185.199.108.153] 188 | support.hackerone.com [104.16.51.111] 189 | support.hackerone.com [104.16.53.111] 190 | mta-sts.managed.hackerone.com [185.199.108.153] 191 | mta-sts.managed.hackerone.com [185.199.109.153] 192 | mta-sts.managed.hackerone.com [185.199.110.153] 193 | mta-sts.managed.hackerone.com [185.199.111.153] 194 | ``` 195 | 196 | Extract **A** records for the given list of subdomains: 197 | 198 | ```console 199 | subfinder -silent -d hackerone.com | dnsx -silent -a -resp-only 200 | 201 | 104.16.99.52 202 | 104.16.100.52 203 | 162.159.1.31 204 | 104.16.99.52 205 | 104.16.100.52 206 | 185.199.110.153 207 | 185.199.111.153 208 | 185.199.108.153 209 | 185.199.109.153 210 | 104.16.99.52 211 | 104.16.100.52 212 | 104.16.51.111 213 | 104.16.53.111 214 | 185.199.108.153 215 | 185.199.111.153 216 | 185.199.110.153 217 | 185.199.111.153 218 | ``` 219 | 220 | Extract **CNAME** records for the given list of subdomains: 221 | 222 | ```console 223 | subfinder -silent -d hackerone.com | dnsx -silent -cname -resp 224 | 225 | support.hackerone.com [hackerone.zendesk.com] 226 | resources.hackerone.com [read.uberflip.com] 227 | mta-sts.hackerone.com [hacker0x01.github.io] 228 | mta-sts.forwarding.hackerone.com [hacker0x01.github.io] 229 | events.hackerone.com [whitelabel.bigmarker.com] 230 | ``` 231 | Extract **ASN** records for the given list of subdomains: 232 | ```console 233 | subfinder -silent -d hackerone.com | dnsx -silent -asn 234 | 235 | b.ns.hackerone.com [AS13335, CLOUDFLARENET, US] 236 | a.ns.hackerone.com [AS13335, CLOUDFLARENET, US] 237 | hackerone.com [AS13335, CLOUDFLARENET, US] 238 | www.hackerone.com [AS13335, CLOUDFLARENET, US] 239 | api.hackerone.com [AS13335, CLOUDFLARENET, US] 240 | support.hackerone.com [AS13335, CLOUDFLARENET, US] 241 | ``` 242 | 243 | Probe using [dns status code](https://github.com/projectdiscovery/dnsx/wiki/RCODE-ID-VALUE-Mapping) on given list of (sub)domains: 244 | 245 | ```console 246 | subfinder -silent -d hackerone.com | dnsx -silent -rcode noerror,servfail,refused 247 | 248 | ns.hackerone.com [NOERROR] 249 | a.ns.hackerone.com [NOERROR] 250 | b.ns.hackerone.com [NOERROR] 251 | support.hackerone.com [NOERROR] 252 | resources.hackerone.com [NOERROR] 253 | mta-sts.hackerone.com [NOERROR] 254 | www.hackerone.com [NOERROR] 255 | mta-sts.forwarding.hackerone.com [NOERROR] 256 | docs.hackerone.com [NOERROR] 257 | ``` 258 | 259 | Extract subdomains from given network range using `PTR` query: 260 | 261 | ```console 262 | echo 173.0.84.0/24 | dnsx -silent -resp-only -ptr 263 | 264 | cors.api.paypal.com 265 | trinityadminauth.paypal.com 266 | cld-edge-origin-api.paypal.com 267 | appmanagement.paypal.com 268 | svcs.paypal.com 269 | trinitypie-serv.paypal.com 270 | ppn.paypal.com 271 | pointofsale-new.paypal.com 272 | pointofsale.paypal.com 273 | slc-a-origin-pointofsale.paypal.com 274 | fpdbs.paypal.com 275 | ``` 276 | 277 | Extract subdomains from given ASN using `PTR` query: 278 | ```console 279 | echo AS17012 | dnsx -silent -resp-only -ptr 280 | 281 | apiagw-a.paypal.com 282 | notify.paypal.com 283 | adnormserv-slc-a.paypal.com 284 | a.sandbox.paypal.com 285 | apps2.paypal-labs.com 286 | pilot-payflowpro.paypal.com 287 | www.paypallabs.com 288 | paypal-portal.com 289 | micropayments.paypal-labs.com 290 | minicart.paypal-labs.com 291 | ``` 292 | --------- 293 | 294 | ### DNS Bruteforce 295 | 296 | Bruteforce subdomains for given domain or list of domains using `d` and `w` flag: 297 | 298 | ```console 299 | dnsx -silent -d facebook.com -w dns_worldlist.txt 300 | 301 | blog.facebook.com 302 | booking.facebook.com 303 | api.facebook.com 304 | analytics.facebook.com 305 | beta.facebook.com 306 | apollo.facebook.com 307 | ads.facebook.com 308 | box.facebook.com 309 | alpha.facebook.com 310 | apps.facebook.com 311 | connect.facebook.com 312 | c.facebook.com 313 | careers.facebook.com 314 | code.facebook.com 315 | ``` 316 | 317 | Bruteforce targeted subdomain using single or multiple keyword input, as `d` or `w` flag supports file or comma separated keyword inputs: 318 | 319 | ```console 320 | dnsx -silent -d domains.txt -w jira,grafana,jenkins 321 | 322 | grafana.1688.com 323 | grafana.8x8.vc 324 | grafana.airmap.com 325 | grafana.aerius.nl 326 | jenkins.1688.com 327 | jenkins.airbnb.app 328 | jenkins.airmap.com 329 | jenkins.ahn.nl 330 | jenkins.achmea.nl 331 | jira.amocrm.com 332 | jira.amexgbt.com 333 | jira.amitree.com 334 | jira.arrival.com 335 | jira.atlassian.net 336 | jira.atlassian.com 337 | ``` 338 | 339 | Values are accepted from **stdin** for all the input types (`-list`, `-domain`, `-wordlist`). The `-list` flag defaults to `stdin`, but the same can be achieved for other input types by adding a `-` (dash) as parameter: 340 | 341 | ```console 342 | cat domains.txt | dnsx -silent -w jira,grafana,jenkins -d - 343 | 344 | grafana.1688.com 345 | grafana.8x8.vc 346 | grafana.airmap.com 347 | grafana.aerius.nl 348 | jenkins.1688.com 349 | jenkins.airbnb.app 350 | jenkins.airmap.com 351 | jenkins.ahn.nl 352 | jenkins.achmea.nl 353 | jira.amocrm.com 354 | jira.amexgbt.com 355 | jira.amitree.com 356 | jira.arrival.com 357 | jira.atlassian.net 358 | jira.atlassian.com 359 | ``` 360 | 361 | #### DNS Bruteforce with Placeholder based wordlist 362 | 363 | ```bash 364 | $ cat tld.txt 365 | 366 | com 367 | by 368 | de 369 | be 370 | al 371 | bi 372 | cg 373 | dj 374 | bs 375 | ``` 376 | 377 | ```console 378 | dnsx -d google.FUZZ -w tld.txt -resp 379 | 380 | _ __ __ 381 | __| | _ __ ___ \ \/ / 382 | / _' || '_ \ / __| \ / 383 | | (_| || | | |\__ \ / \ 384 | \__,_||_| |_||___//_/\_\ v1.1.2 385 | 386 | projectdiscovery.io 387 | 388 | google.de [142.250.194.99] 389 | google.com [142.250.76.206] 390 | google.be [172.217.27.163] 391 | google.bs [142.251.42.35] 392 | google.bi [216.58.196.67] 393 | google.al [216.58.196.68] 394 | google.by [142.250.195.4] 395 | google.cg [142.250.183.131] 396 | google.dj [142.250.192.3] 397 | ``` 398 | 399 | ### Wildcard filtering 400 | 401 | A special feature of `dnsx` is its ability to handle **multi-level DNS based wildcards**, and do it so with a very reduced number of DNS requests. Sometimes all the subdomains will resolve, which leads to lots of garbage in the output. The way `dnsx` handles this is by keeping track of how many subdomains point to an IP and if the count of the subdomains increase beyond a certain threshold, it will check for wildcards on all the levels of the hosts for that IP iteratively. 402 | 403 | ```console 404 | dnsx -l subdomain_list.txt -wd airbnb.com -o output.txt 405 | ``` 406 | 407 | --------- 408 | 409 | ### Dnsx as a library 410 | 411 | It's possible to use the library directly in your golang programs. The following code snippets is an example of use in golang programs. Please refer to [here](https://pkg.go.dev/github.com/projectdiscovery/dnsx@v1.1.0/libs/dnsx) for detailed package configuration and usage. 412 | 413 | ```go 414 | package main 415 | 416 | import ( 417 | "fmt" 418 | 419 | "github.com/projectdiscovery/dnsx/libs/dnsx" 420 | ) 421 | 422 | func main() { 423 | // Create DNS Resolver with default options 424 | dnsClient, err := dnsx.New(dnsx.DefaultOptions) 425 | if err != nil { 426 | fmt.Printf("err: %v\n", err) 427 | return 428 | } 429 | 430 | // DNS A question and returns corresponding IPs 431 | result, err := dnsClient.Lookup("hackerone.com") 432 | if err != nil { 433 | fmt.Printf("err: %v\n", err) 434 | return 435 | } 436 | for idx, msg := range result { 437 | fmt.Printf("%d: %s\n", idx+1, msg) 438 | } 439 | 440 | // Query 441 | rawResp, err := dnsClient.QueryOne("hackerone.com") 442 | if err != nil { 443 | fmt.Printf("err: %v\n", err) 444 | return 445 | } 446 | fmt.Printf("rawResp: %v\n", rawResp) 447 | 448 | jsonStr, err := rawResp.JSON() 449 | if err != nil { 450 | fmt.Printf("err: %v\n", err) 451 | return 452 | } 453 | fmt.Println(jsonStr) 454 | 455 | return 456 | } 457 | ``` 458 | 459 | # 📋 Notes 460 | 461 | - As default, `dnsx` checks for **A** record. 462 | - As default `dnsx` uses Google, Cloudflare, Quad9 [resolver](https://github.com/projectdiscovery/dnsx/blob/43af78839e237ea8cbafe571df1ab0d6cbe7f445/libs/dnsx/dnsx.go#L31). 463 | - Custom resolver list can be loaded using the `r` flag. 464 | - Domain name (`wd`) input is mandatory for wildcard elimination. 465 | - DNS record flag can not be used when using wildcard filtering. 466 | - DNS resolution (`l`) and DNS brute-forcing (`w`) can't be used together. 467 | - VPN operators tend to filter high DNS/UDP traffic, therefore the tool might experience packets loss (eg. [Mullvad VPN](https://github.com/projectdiscovery/dnsx/issues/221)). Check [this potential solution](./MULLVAD.md). 468 | 469 | `dnsx` is made with 🖤 by the [projectdiscovery](https://projectdiscovery.io) team. 470 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /cmd/functional-test/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # reading os type from arguments 4 | CURRENT_OS=$1 5 | 6 | if [ "${CURRENT_OS}" == "windows-latest" ];then 7 | extension=.exe 8 | fi 9 | 10 | echo "::group::Building functional-test binary" 11 | go build -o functional-test$extension 12 | echo "::endgroup::" 13 | 14 | echo "::group::Building dnsx binary from current branch" 15 | go build -o dnsx_dev$extension ../dnsx 16 | echo "::endgroup::" 17 | 18 | echo "::group::Building latest release of dnsx" 19 | go build -o dnsx$extension -v github.com/projectdiscovery/dnsx/cmd/dnsx 20 | echo "::endgroup::" 21 | 22 | echo 'Starting dnsx functional test' 23 | ./functional-test$extension -main ./dnsx$extension -dev ./dnsx_dev$extension -testcases testcases.txt 24 | -------------------------------------------------------------------------------- /cmd/functional-test/test-data/file.txt: -------------------------------------------------------------------------------- 1 | example.com 2 | example.com 3 | -------------------------------------------------------------------------------- /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 -srv 19 | example.com {{binary}} -silent -srv -resp 20 | example.com {{binary}} -silent -rcode 0,1,2 21 | example.com {{binary}} -silent -any 22 | example.com {{binary}} -silent -any -resp 23 | 1.1.1.1 {{binary}} -silent -ptr 24 | 1.1.1.1 {{binary}} -silent -ptr -resp 25 | www.projectdiscovery.io {{binary}} -silent -cname 26 | www.projectdiscovery.io {{binary}} -silent -cname -resp 27 | www.projectdiscovery.io {{binary}} -silent -or -------------------------------------------------------------------------------- /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(h.expectedOutput, results[0]) 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(h.expectedOutput, results[0]) 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/projectdiscovery/dnsx 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/logrusorgru/aurora v2.0.3+incompatible 7 | github.com/miekg/dns v1.1.56 8 | github.com/pkg/errors v0.9.1 9 | github.com/projectdiscovery/asnmap v1.1.1 10 | github.com/projectdiscovery/cdncheck v1.1.0 11 | github.com/projectdiscovery/clistats v0.1.1 12 | github.com/projectdiscovery/goconfig v0.0.1 13 | github.com/projectdiscovery/goflags v0.1.65 14 | github.com/projectdiscovery/gologger v1.1.42 15 | github.com/projectdiscovery/hmap v0.0.77 16 | github.com/projectdiscovery/mapcidr v1.1.34 17 | github.com/projectdiscovery/ratelimit v0.0.69 18 | github.com/projectdiscovery/retryabledns v1.0.94 19 | github.com/projectdiscovery/utils v0.4.7 20 | github.com/rs/xid v1.5.0 21 | github.com/stretchr/testify v1.9.0 22 | ) 23 | 24 | require ( 25 | aead.dev/minisign v0.2.0 // indirect 26 | github.com/Masterminds/semver/v3 v3.2.1 // indirect 27 | github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057 // indirect 28 | github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 // indirect 29 | github.com/VividCortex/ewma v1.2.0 // indirect 30 | github.com/akrylysov/pogreb v0.10.1 // indirect 31 | github.com/alecthomas/chroma/v2 v2.14.0 // indirect 32 | github.com/andybalholm/brotli v1.0.6 // indirect 33 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 34 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 35 | github.com/aymerick/douceur v0.2.0 // indirect 36 | github.com/bits-and-blooms/bitset v1.13.0 // indirect 37 | github.com/charmbracelet/glamour v0.8.0 // indirect 38 | github.com/charmbracelet/lipgloss v0.13.0 // indirect 39 | github.com/charmbracelet/x/ansi v0.3.2 // indirect 40 | github.com/cheggaaa/pb/v3 v3.1.4 // indirect 41 | github.com/cloudflare/circl v1.3.7 // indirect 42 | github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect 43 | github.com/davecgh/go-spew v1.1.1 // indirect 44 | github.com/dimchansky/utfbom v1.1.1 // indirect 45 | github.com/dlclark/regexp2 v1.11.4 // indirect 46 | github.com/docker/go-units v0.5.0 // indirect 47 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect 48 | github.com/fatih/color v1.15.0 // indirect 49 | github.com/gaissmai/bart v0.9.5 // indirect 50 | github.com/go-ole/go-ole v1.2.6 // indirect 51 | github.com/golang/protobuf v1.5.3 // indirect 52 | github.com/golang/snappy v0.0.4 // indirect 53 | github.com/google/go-github/v30 v30.1.0 // indirect 54 | github.com/google/go-querystring v1.1.0 // indirect 55 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect 56 | github.com/google/uuid v1.3.1 // indirect 57 | github.com/gorilla/css v1.0.1 // indirect 58 | github.com/json-iterator/go v1.1.12 // indirect 59 | github.com/klauspost/compress v1.17.4 // indirect 60 | github.com/klauspost/pgzip v1.2.5 // indirect 61 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 62 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect 63 | github.com/mattn/go-colorable v0.1.13 // indirect 64 | github.com/mattn/go-isatty v0.0.20 // indirect 65 | github.com/mattn/go-runewidth v0.0.16 // indirect 66 | github.com/mholt/archiver/v3 v3.5.1 // indirect 67 | github.com/microcosm-cc/bluemonday v1.0.27 // indirect 68 | github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 // indirect 69 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 70 | github.com/modern-go/reflect2 v1.0.2 // indirect 71 | github.com/muesli/reflow v0.3.0 // indirect 72 | github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect 73 | github.com/nwaples/rardecode v1.1.3 // indirect 74 | github.com/pierrec/lz4/v4 v4.1.2 // indirect 75 | github.com/pmezard/go-difflib v1.0.0 // indirect 76 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect 77 | github.com/projectdiscovery/blackrock v0.0.1 // indirect 78 | github.com/projectdiscovery/fastdialer v0.3.0 // indirect 79 | github.com/projectdiscovery/freeport v0.0.7 // indirect 80 | github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect 81 | github.com/projectdiscovery/networkpolicy v0.1.1 // indirect 82 | github.com/projectdiscovery/retryablehttp-go v1.0.97 // indirect 83 | github.com/refraction-networking/utls v1.6.7 // indirect 84 | github.com/rivo/uniseg v0.4.7 // indirect 85 | github.com/rogpeppe/go-internal v1.12.0 // indirect 86 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect 87 | github.com/shirou/gopsutil/v3 v3.23.7 // indirect 88 | github.com/shoenig/go-m1cpu v0.1.6 // indirect 89 | github.com/syndtr/goleveldb v1.0.0 // indirect 90 | github.com/tidwall/btree v1.6.0 // indirect 91 | github.com/tidwall/buntdb v1.3.0 // indirect 92 | github.com/tidwall/gjson v1.14.4 // indirect 93 | github.com/tidwall/grect v0.1.4 // indirect 94 | github.com/tidwall/match v1.1.1 // indirect 95 | github.com/tidwall/pretty v1.2.1 // indirect 96 | github.com/tidwall/rtred v0.1.2 // indirect 97 | github.com/tidwall/tinyqueue v0.1.1 // indirect 98 | github.com/tklauser/go-sysconf v0.3.12 // indirect 99 | github.com/tklauser/numcpus v0.6.1 // indirect 100 | github.com/ulikunitz/xz v0.5.11 // indirect 101 | github.com/weppos/publicsuffix-go v0.30.1 // indirect 102 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect 103 | github.com/yuin/goldmark v1.7.4 // indirect 104 | github.com/yuin/goldmark-emoji v1.0.3 // indirect 105 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 106 | github.com/zcalusic/sysinfo v1.0.2 // indirect 107 | github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 // indirect 108 | github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968 // indirect 109 | go.etcd.io/bbolt v1.3.7 // indirect 110 | go.uber.org/multierr v1.11.0 // indirect 111 | golang.org/x/crypto v0.31.0 // indirect 112 | golang.org/x/exp v0.0.0-20230420155640-133eef4313cb // indirect 113 | golang.org/x/mod v0.17.0 // indirect 114 | golang.org/x/net v0.33.0 // indirect 115 | golang.org/x/oauth2 v0.11.0 // indirect 116 | golang.org/x/sync v0.10.0 // indirect 117 | golang.org/x/sys v0.28.0 // indirect 118 | golang.org/x/term v0.27.0 // indirect 119 | golang.org/x/text v0.21.0 // indirect 120 | golang.org/x/time v0.5.0 // indirect 121 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect 122 | google.golang.org/appengine v1.6.7 // indirect 123 | google.golang.org/protobuf v1.33.0 // indirect 124 | gopkg.in/djherbis/times.v1 v1.3.0 // indirect 125 | gopkg.in/ini.v1 v1.67.0 // indirect 126 | gopkg.in/yaml.v3 v3.0.1 // indirect 127 | ) 128 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk= 2 | aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ= 3 | cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= 4 | github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= 5 | github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= 6 | github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057 h1:KFac3SiGbId8ub47e7kd2PLZeACxc1LkiiNoDOFRClE= 7 | github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057/go.mod h1:iLB2pivrPICvLOuROKmlqURtFIEsoJZaMidQfCG1+D4= 8 | github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 h1:ZbFL+BDfBqegi+/Ssh7im5+aQfBRx6it+kHnC7jaDU8= 9 | github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809/go.mod h1:upgc3Zs45jBDnBT4tVRgRcgm26ABpaP7MoTSdgysca4= 10 | github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= 11 | github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= 12 | github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= 13 | github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w= 14 | github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= 15 | github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= 16 | github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= 17 | github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= 18 | github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= 19 | github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= 20 | github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 21 | github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 22 | github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= 23 | github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 24 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= 25 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 26 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 27 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 28 | github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= 29 | github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= 30 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= 31 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 32 | github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= 33 | github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 34 | github.com/bits-and-blooms/bloom/v3 v3.5.0 h1:AKDvi1V3xJCmSR6QhcBfHbCN4Vf8FfxeWkMNQfmAGhY= 35 | github.com/bits-and-blooms/bloom/v3 v3.5.0/go.mod h1:Y8vrn7nk1tPIlmLtW2ZPV+W7StdVMor6bC1xgpjMZFs= 36 | github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= 37 | github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs= 38 | github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw= 39 | github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= 40 | github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= 41 | github.com/charmbracelet/x/ansi v0.3.2 h1:wsEwgAN+C9U06l9dCVMX0/L3x7ptvY1qmjMwyfE6USY= 42 | github.com/charmbracelet/x/ansi v0.3.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= 43 | github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30= 44 | github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= 45 | github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= 46 | github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA= 47 | github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= 48 | github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= 49 | github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= 50 | github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ= 51 | github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4= 52 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 53 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 54 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 55 | github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= 56 | github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= 57 | github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= 58 | github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 59 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 60 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 61 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= 62 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= 63 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 64 | github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= 65 | github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= 66 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 67 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 68 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 69 | github.com/gaissmai/bart v0.9.5 h1:vy+r4Px6bjZ+v2QYXAsg63vpz9IfzdW146A8Cn4GPIo= 70 | github.com/gaissmai/bart v0.9.5/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= 71 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 72 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 73 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 74 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 75 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 76 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 77 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 78 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 79 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 80 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 81 | github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 82 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 83 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 84 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 85 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 86 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 87 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 88 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 89 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 90 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 91 | github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo= 92 | github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= 93 | github.com/google/go-github/v50 v50.1.0/go.mod h1:Ev4Tre8QoKiolvbpOSG3FIi4Mlon3S2Nt9W5JYqKiwA= 94 | github.com/google/go-github/v50 v50.2.0/go.mod h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q= 95 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 96 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 97 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 98 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 99 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= 100 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 101 | github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= 102 | github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 103 | github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= 104 | github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= 105 | github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM= 106 | github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 107 | github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= 108 | github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= 109 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 110 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 111 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 112 | github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= 113 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 114 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 115 | github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 116 | github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= 117 | github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= 118 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 119 | github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= 120 | github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 121 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 122 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 123 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 124 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 125 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 126 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 127 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 128 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 129 | github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= 130 | github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 131 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 132 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 133 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 134 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 135 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 136 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 137 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 138 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 139 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 140 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 141 | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 142 | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 143 | github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= 144 | github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= 145 | github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= 146 | github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= 147 | github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= 148 | github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= 149 | github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 h1:yRZGarbxsRytL6EGgbqK2mCY+Lk5MWKQYKJT2gEglhc= 150 | github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM= 151 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 152 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 153 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 154 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 155 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 156 | github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= 157 | github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= 158 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= 159 | github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= 160 | github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= 161 | github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= 162 | github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 163 | github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= 164 | github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 165 | github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= 166 | github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= 167 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 168 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 169 | github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= 170 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 171 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 172 | github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= 173 | github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= 174 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 175 | github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= 176 | github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 177 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 178 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 179 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 180 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 181 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 182 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 183 | github.com/projectdiscovery/asnmap v1.1.1 h1:ImJiKIaACOT7HPx4Pabb5dksolzaFYsD1kID2iwsDqI= 184 | github.com/projectdiscovery/asnmap v1.1.1/go.mod h1:QT7jt9nQanj+Ucjr9BqGr1Q2veCCKSAVyUzLXfEcQ60= 185 | github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k0VbGJyft6LQ= 186 | github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss= 187 | github.com/projectdiscovery/cdncheck v1.1.0 h1:qDITidmJsejzpk3rMkauCh6sjI2GH9hW/snk0cQ3kXE= 188 | github.com/projectdiscovery/cdncheck v1.1.0/go.mod h1:sZ8U4MjHSsyaTVjBbYWHT1cwUVvUYwDX1W+WvWRicIc= 189 | github.com/projectdiscovery/clistats v0.1.1 h1:8mwbdbwTU4aT88TJvwIzTpiNeow3XnAB72JIg66c8wE= 190 | github.com/projectdiscovery/clistats v0.1.1/go.mod h1:4LtTC9Oy//RiuT1+76MfTg8Hqs7FQp1JIGBM3nHK6a0= 191 | github.com/projectdiscovery/fastdialer v0.3.0 h1:/wMptjdsrAU/wiaA/U3lSgYGaYCGJH6xm0mLei6oMxk= 192 | github.com/projectdiscovery/fastdialer v0.3.0/go.mod h1:Q0YLArvpx9GAfY/NcTPMCA9qZuVOGnuVoNYWzKBwxdQ= 193 | github.com/projectdiscovery/freeport v0.0.7 h1:Q6uXo/j8SaV/GlAHkEYQi8WQoPXyJWxyspx+aFmz9Qk= 194 | github.com/projectdiscovery/freeport v0.0.7/go.mod h1:cOhWKvNBe9xM6dFJ3RrrLvJ5vXx2NQ36SecuwjenV2k= 195 | github.com/projectdiscovery/goconfig v0.0.1 h1:36m3QjohZvemqh9bkJAakaHsm9iEZ2AcQSS18+0QX/s= 196 | github.com/projectdiscovery/goconfig v0.0.1/go.mod h1:CPO25zR+mzTtyBrsygqsHse0sp/4vB/PjaHi9upXlDw= 197 | github.com/projectdiscovery/goflags v0.1.65 h1:rjoj+5lP/FDzgeM0WILUTX9AOOnw0J0LXtl8P1SVeGE= 198 | github.com/projectdiscovery/goflags v0.1.65/go.mod h1:cg6+yrLlaekP1hnefBc/UXbH1YGWa0fuzEW9iS1aG4g= 199 | github.com/projectdiscovery/gologger v1.1.42 h1:wTF52VEhJCtZk9GDNrlnAYBrqfm5FqXv/FImkNfdOSE= 200 | github.com/projectdiscovery/gologger v1.1.42/go.mod h1:hf6vcNeKgj+BHfWQpMV4UBsfE8d+9wauWvcI31ZwuXs= 201 | github.com/projectdiscovery/hmap v0.0.77 h1:pI7pmW+CN19LAkMsSFKC0K1SK1+sonto2hwte/7eOaQ= 202 | github.com/projectdiscovery/hmap v0.0.77/go.mod h1:LkV8r/enq9G9HnY2YzMpU4CCmFz3dstOlhmeaObVQfk= 203 | github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 h1:ZScLodGSezQVwsQDtBSMFp72WDq0nNN+KE/5DHKY5QE= 204 | github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI= 205 | github.com/projectdiscovery/mapcidr v1.1.34 h1:udr83vQ7oz3kEOwlsU6NC6o08leJzSDQtls1wmXN/kM= 206 | github.com/projectdiscovery/mapcidr v1.1.34/go.mod h1:1+1R6OkKSAKtWDXE9RvxXtXPoajXTYX0eiEdkqlhQqQ= 207 | github.com/projectdiscovery/networkpolicy v0.1.1 h1:iv9gECukD5KAZp98KVh+T3TEPTkY6dr3sKsdbh9XyZU= 208 | github.com/projectdiscovery/networkpolicy v0.1.1/go.mod h1:/Hg2ieLewSe/BagFF+UYXAQo3NwmVMq16MSAl492XkU= 209 | github.com/projectdiscovery/ratelimit v0.0.69 h1:co8QoB/hyIW9HwT/oA5aa1ENAdg8CCasMzBb9cbfxY8= 210 | github.com/projectdiscovery/ratelimit v0.0.69/go.mod h1:5inCjvHaTFMVlS2ac6FHJa0XGGRmF1p+GWdyGrJzSkI= 211 | github.com/projectdiscovery/retryabledns v1.0.94 h1:MvxtRcmvxhxikxT7p/E40hcYRWRiL5fg/JQ8bpBaz+0= 212 | github.com/projectdiscovery/retryabledns v1.0.94/go.mod h1:croGTyMM4yNlrSWA/X7xNe3c0c7mDmCdbm8goLd8Bak= 213 | github.com/projectdiscovery/retryablehttp-go v1.0.97 h1:6nee/vJjiZP3vOhyqLcpSADM3vqmcC2QOvaMIo+dKWQ= 214 | github.com/projectdiscovery/retryablehttp-go v1.0.97/go.mod h1:ZvwB6IsIHf0YlovcEQufZ6OTluyWfxRd360SrKd9fPk= 215 | github.com/projectdiscovery/utils v0.4.7 h1:kw0BCox/G4eCaz5MW0U4HP6z/n0K4X7qv7v1wNuXSLA= 216 | github.com/projectdiscovery/utils v0.4.7/go.mod h1:B4k3whXzeBD+HrlvlqCv+KNgcAuzrTIL+OADkk0z8Bw= 217 | github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM= 218 | github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= 219 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 220 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 221 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 222 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 223 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 224 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 225 | github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= 226 | github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 227 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= 228 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= 229 | github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= 230 | github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= 231 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= 232 | github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 233 | github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= 234 | github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= 235 | github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 236 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 237 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 238 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 239 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 240 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 241 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 242 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 243 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 244 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 245 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 246 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 247 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 248 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 249 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 250 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 251 | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= 252 | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= 253 | github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= 254 | github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= 255 | github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= 256 | github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= 257 | github.com/tidwall/buntdb v1.3.0 h1:gdhWO+/YwoB2qZMeAU9JcWWsHSYU3OvcieYgFRS0zwA= 258 | github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU= 259 | github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 260 | github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= 261 | github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 262 | github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= 263 | github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= 264 | github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= 265 | github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= 266 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 267 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 268 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 269 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 270 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 271 | github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= 272 | github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= 273 | github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= 274 | github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= 275 | github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= 276 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 277 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 278 | github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= 279 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 280 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 281 | github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 282 | github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 283 | github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= 284 | github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 285 | github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= 286 | github.com/weppos/publicsuffix-go v0.30.1-0.20230422193905-8fecedd899db/go.mod h1:aiQaH1XpzIfgrJq3S1iw7w+3EDbRP7mF5fmwUhWyRUs= 287 | github.com/weppos/publicsuffix-go v0.30.1 h1:8q+QwBS1MY56Zjfk/50ycu33NN8aa1iCCEQwo/71Oos= 288 | github.com/weppos/publicsuffix-go v0.30.1/go.mod h1:s41lQh6dIsDWIC1OWh7ChWJXLH0zkJ9KHZVqA7vHyuQ= 289 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= 290 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= 291 | github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU= 292 | github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= 293 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 294 | github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= 295 | github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= 296 | github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= 297 | github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4= 298 | github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= 299 | github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 300 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= 301 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 302 | github.com/zcalusic/sysinfo v1.0.2 h1:nwTTo2a+WQ0NXwo0BGRojOJvJ/5XKvQih+2RrtWqfxc= 303 | github.com/zcalusic/sysinfo v1.0.2/go.mod h1:kluzTYflRWo6/tXVMJPdEjShsbPpsFRyy+p1mBQPC30= 304 | github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= 305 | github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 h1:Nzukz5fNOBIHOsnP+6I79kPx3QhLv8nBy2mfFhBRq30= 306 | github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= 307 | github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= 308 | github.com/zmap/zcertificate v0.0.1/go.mod h1:q0dlN54Jm4NVSSuzisusQY0hqDWvu92C+TWveAxiVWk= 309 | github.com/zmap/zcrypto v0.0.0-20201128221613-3719af1573cf/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= 310 | github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= 311 | github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968 h1:YOQ1vXEwE4Rnj+uQ/3oCuJk5wgVsvUyW+glsndwYuyA= 312 | github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968/go.mod h1:xIuOvYCZX21S5Z9bK1BMrertTGX/F8hgAPw7ERJRNS0= 313 | github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8= 314 | go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= 315 | go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= 316 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 317 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 318 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 319 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 320 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 321 | golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 322 | golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 323 | golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 324 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 325 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 326 | golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 327 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 328 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 329 | golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= 330 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 331 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 332 | golang.org/x/exp v0.0.0-20230420155640-133eef4313cb h1:rhjz/8Mbfa8xROFiH+MQphmAmgqRM0bOMnytznhWEXk= 333 | golang.org/x/exp v0.0.0-20230420155640-133eef4313cb/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= 334 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 335 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 336 | golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= 337 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 338 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 339 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 340 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 341 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 342 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 343 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 344 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 345 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 346 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 347 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 348 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 349 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 350 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 351 | golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 352 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 353 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 354 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 355 | golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= 356 | golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= 357 | golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= 358 | golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= 359 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 360 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 361 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 362 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 363 | golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= 364 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 365 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 366 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 367 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 368 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 369 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 370 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 371 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 375 | golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 377 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 378 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 379 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 380 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 381 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 382 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 383 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 384 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 385 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 386 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 387 | golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 388 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 389 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 390 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 391 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 392 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 393 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 394 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 395 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 396 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 397 | golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= 398 | golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= 399 | golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 400 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 401 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 402 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 403 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 404 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 405 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 406 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 407 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 408 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 409 | golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 410 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 411 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 412 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 413 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 414 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 415 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 416 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 417 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 418 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= 419 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 420 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 421 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 422 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 423 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 424 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 425 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 426 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 427 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 428 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 429 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 430 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 431 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 432 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 433 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 434 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 435 | gopkg.in/djherbis/times.v1 v1.3.0 h1:uxMS4iMtH6Pwsxog094W0FYldiNnfY/xba00vq6C2+o= 436 | gopkg.in/djherbis/times.v1 v1.3.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv6mUY0P8= 437 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 438 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 439 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 440 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 441 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 442 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 443 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 444 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 445 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 446 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 447 | -------------------------------------------------------------------------------- /integration_tests/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "::group::Build dnsx" 4 | rm integration-test dnsx 2>/dev/null 5 | cd ../cmd/dnsx 6 | go build 7 | mv dnsx ../../integration_tests/dnsx 8 | echo "::endgroup::" 9 | echo "::group::Build nuclei integration-test" 10 | cd ../integration-test 11 | go build 12 | mv integration-test ../../integration_tests/integration-test 13 | cd ../../integration_tests 14 | echo "::endgroup::" 15 | ./integration-test 16 | if [ $? -eq 0 ] 17 | then 18 | exit 0 19 | else 20 | exit 1 21 | fi 22 | -------------------------------------------------------------------------------- /internal/runner/banner.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "github.com/projectdiscovery/gologger" 5 | "github.com/projectdiscovery/utils/auth/pdcp" 6 | updateutils "github.com/projectdiscovery/utils/update" 7 | ) 8 | 9 | const banner = ` 10 | _ __ __ 11 | __| | _ __ ___ \ \/ / 12 | / _' || '_ \ / __| \ / 13 | | (_| || | | |\__ \ / \ 14 | \__,_||_| |_||___//_/\_\ 15 | ` 16 | 17 | // Name 18 | const ToolName = `dnsx` 19 | 20 | // version is the current version of dnsx 21 | const version = `1.2.2` 22 | 23 | // showBanner is used to show the banner to the user 24 | func showBanner() { 25 | gologger.Print().Msgf("%s\n", banner) 26 | gologger.Print().Msgf("\t\tprojectdiscovery.io\n\n") 27 | } 28 | 29 | // GetUpdateCallback returns a callback function that updates dnsx 30 | func GetUpdateCallback() func() { 31 | return func() { 32 | showBanner() 33 | updateutils.GetUpdateToolCallback("dnsx", version)() 34 | } 35 | } 36 | 37 | // AuthWithPDCP is used to authenticate with PDCP 38 | func AuthWithPDCP() { 39 | showBanner() 40 | pdcp.CheckNValidateCredentials("dnsx") 41 | } 42 | -------------------------------------------------------------------------------- /internal/runner/doc.go: -------------------------------------------------------------------------------- 1 | // Package runner executes the enumeration process. 2 | package runner 3 | -------------------------------------------------------------------------------- /internal/runner/healthcheck.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "runtime" 7 | "strings" 8 | 9 | "github.com/projectdiscovery/goflags" 10 | fileutil "github.com/projectdiscovery/utils/file" 11 | ) 12 | 13 | func DoHealthCheck(options *Options, flagSet *goflags.FlagSet) string { 14 | // RW permissions on config file 15 | cfgFilePath, _ := flagSet.GetConfigFilePath() 16 | var test strings.Builder 17 | test.WriteString(fmt.Sprintf("Version: %s\n", version)) 18 | test.WriteString(fmt.Sprintf("Operative System: %s\n", runtime.GOOS)) 19 | test.WriteString(fmt.Sprintf("Architecture: %s\n", runtime.GOARCH)) 20 | test.WriteString(fmt.Sprintf("Go Version: %s\n", runtime.Version())) 21 | test.WriteString(fmt.Sprintf("Compiler: %s\n", runtime.Compiler)) 22 | 23 | var testResult string 24 | ok, err := fileutil.IsReadable(cfgFilePath) 25 | if ok { 26 | testResult = "Ok" 27 | } else { 28 | testResult = "Ko" 29 | } 30 | if err != nil { 31 | testResult += fmt.Sprintf(" (%s)", err) 32 | } 33 | test.WriteString(fmt.Sprintf("Config file \"%s\" Read => %s\n", cfgFilePath, testResult)) 34 | ok, err = fileutil.IsWriteable(cfgFilePath) 35 | if ok { 36 | testResult = "Ok" 37 | } else { 38 | testResult = "Ko" 39 | } 40 | if err != nil { 41 | testResult += fmt.Sprintf(" (%s)", err) 42 | } 43 | test.WriteString(fmt.Sprintf("Config file \"%s\" Write => %s\n", cfgFilePath, testResult)) 44 | c4, err := net.Dial("tcp4", "scanme.sh:80") 45 | if err == nil && c4 != nil { 46 | c4.Close() 47 | } 48 | testResult = "Ok" 49 | if err != nil { 50 | testResult = fmt.Sprintf("Ko (%s)", err) 51 | } 52 | test.WriteString(fmt.Sprintf("IPv4 connectivity to scanme.sh:80 => %s\n", testResult)) 53 | c6, err := net.Dial("tcp6", "scanme.sh:80") 54 | if err == nil && c6 != nil { 55 | c6.Close() 56 | } 57 | testResult = "Ok" 58 | if err != nil { 59 | testResult = fmt.Sprintf("Ko (%s)", err) 60 | } 61 | test.WriteString(fmt.Sprintf("IPv6 connectivity to scanme.sh:80 => %s\n", testResult)) 62 | u, err := net.Dial("udp", "scanme.sh:53") 63 | if err == nil && c6 != nil { 64 | u.Close() 65 | } 66 | testResult = "Ok" 67 | if err != nil { 68 | testResult = fmt.Sprintf("Ko (%s)", err) 69 | } 70 | test.WriteString(fmt.Sprintf("UDP connectivity to scanme.sh:53 => %s\n", testResult)) 71 | 72 | return test.String() 73 | } 74 | -------------------------------------------------------------------------------- /internal/runner/options.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/projectdiscovery/goconfig" 10 | "github.com/projectdiscovery/goflags" 11 | "github.com/projectdiscovery/gologger" 12 | "github.com/projectdiscovery/gologger/levels" 13 | "github.com/projectdiscovery/utils/auth/pdcp" 14 | "github.com/projectdiscovery/utils/env" 15 | fileutil "github.com/projectdiscovery/utils/file" 16 | updateutils "github.com/projectdiscovery/utils/update" 17 | ) 18 | 19 | const ( 20 | DefaultResumeFile = "resume.cfg" 21 | ) 22 | 23 | var PDCPApiKey string 24 | 25 | type Options struct { 26 | Resolvers string 27 | Hosts string 28 | Domains string 29 | WordList string 30 | Threads int 31 | RateLimit int 32 | Retries int 33 | OutputFormat string 34 | OutputFile string 35 | Raw bool 36 | Silent bool 37 | Verbose bool 38 | Version bool 39 | NoColor bool 40 | Response bool 41 | ResponseOnly bool 42 | A bool 43 | AAAA bool 44 | NS bool 45 | CNAME bool 46 | PTR bool 47 | MX bool 48 | SOA bool 49 | ANY bool 50 | TXT bool 51 | SRV bool 52 | AXFR bool 53 | JSON bool 54 | OmitRaw bool 55 | Trace bool 56 | TraceMaxRecursion int 57 | WildcardThreshold int 58 | WildcardDomain string 59 | ShowStatistics bool 60 | rcodes map[int]struct{} 61 | RCode string 62 | hasRCodes bool 63 | Resume bool 64 | resumeCfg *ResumeCfg 65 | HostsFile bool 66 | Stream bool 67 | CAA bool 68 | QueryAll bool 69 | ExcludeType []string 70 | OutputCDN bool 71 | ASN bool 72 | HealthCheck bool 73 | DisableUpdateCheck bool 74 | PdcpAuth string 75 | Proxy string 76 | } 77 | 78 | // ShouldLoadResume resume file 79 | func (options *Options) ShouldLoadResume() bool { 80 | return options.Resume && fileutil.FileExists(DefaultResumeFile) 81 | } 82 | 83 | // ShouldSaveResume file 84 | func (options *Options) ShouldSaveResume() bool { 85 | return true 86 | } 87 | 88 | // ParseOptions parses the command line options for application 89 | func ParseOptions() *Options { 90 | options := &Options{} 91 | flagSet := goflags.NewFlagSet() 92 | flagSet.SetDescription(`dnsx is a fast and multi-purpose DNS toolkit allow to run multiple probes using retryabledns library.`) 93 | 94 | flagSet.CreateGroup("input", "Input", 95 | flagSet.StringVarP(&options.Hosts, "list", "l", "", "list of sub(domains)/hosts to resolve (file or stdin)"), 96 | flagSet.StringVarP(&options.Domains, "domain", "d", "", "list of domain to bruteforce (file or comma separated or stdin)"), 97 | flagSet.StringVarP(&options.WordList, "wordlist", "w", "", "list of words to bruteforce (file or comma separated or stdin)"), 98 | ) 99 | 100 | queries := goflags.AllowdTypes{ 101 | "none": goflags.EnumVariable(0), 102 | "a": goflags.EnumVariable(1), 103 | "aaaa": goflags.EnumVariable(2), 104 | "cname": goflags.EnumVariable(3), 105 | "ns": goflags.EnumVariable(4), 106 | "txt": goflags.EnumVariable(5), 107 | "srv": goflags.EnumVariable(6), 108 | "ptr": goflags.EnumVariable(7), 109 | "mx": goflags.EnumVariable(8), 110 | "soa": goflags.EnumVariable(9), 111 | "axfr": goflags.EnumVariable(10), 112 | "caa": goflags.EnumVariable(11), 113 | "any": goflags.EnumVariable(12), 114 | } 115 | 116 | flagSet.CreateGroup("query", "Query", 117 | flagSet.BoolVar(&options.A, "a", false, "query A record (default)"), 118 | flagSet.BoolVar(&options.AAAA, "aaaa", false, "query AAAA record"), 119 | flagSet.BoolVar(&options.CNAME, "cname", false, "query CNAME record"), 120 | flagSet.BoolVar(&options.NS, "ns", false, "query NS record"), 121 | flagSet.BoolVar(&options.TXT, "txt", false, "query TXT record"), 122 | flagSet.BoolVar(&options.SRV, "srv", false, "query SRV record"), 123 | flagSet.BoolVar(&options.PTR, "ptr", false, "query PTR record"), 124 | flagSet.BoolVar(&options.MX, "mx", false, "query MX record"), 125 | flagSet.BoolVar(&options.SOA, "soa", false, "query SOA record"), 126 | flagSet.BoolVar(&options.ANY, "any", false, "query ANY record"), 127 | flagSet.BoolVar(&options.AXFR, "axfr", false, "query AXFR"), 128 | flagSet.BoolVar(&options.CAA, "caa", false, "query CAA record"), 129 | flagSet.BoolVarP(&options.QueryAll, "recon", "all", false, "query all the dns records (a,aaaa,cname,ns,txt,srv,ptr,mx,soa,axfr,caa)"), 130 | flagSet.EnumSliceVarP(&options.ExcludeType, "exclude-type", "e", []goflags.EnumVariable{0}, "dns query type to exclude (a,aaaa,cname,ns,txt,srv,ptr,mx,soa,axfr,caa)", queries), 131 | ) 132 | 133 | flagSet.CreateGroup("filter", "Filter", 134 | flagSet.BoolVarP(&options.Response, "resp", "re", false, "display dns response"), 135 | flagSet.BoolVarP(&options.ResponseOnly, "resp-only", "ro", false, "display dns response only"), 136 | flagSet.StringVarP(&options.RCode, "rcode", "rc", "", "filter result by dns status code (eg. -rcode noerror,servfail,refused)"), 137 | ) 138 | 139 | flagSet.CreateGroup("probe", "Probe", 140 | flagSet.BoolVar(&options.OutputCDN, "cdn", false, "display cdn name"), 141 | flagSet.BoolVar(&options.ASN, "asn", false, "display host asn information"), 142 | ) 143 | 144 | flagSet.CreateGroup("rate-limit", "Rate-limit", 145 | flagSet.IntVarP(&options.Threads, "threads", "t", 100, "number of concurrent threads to use"), 146 | flagSet.IntVarP(&options.RateLimit, "rate-limit", "rl", -1, "number of dns request/second to make (disabled as default)"), 147 | ) 148 | 149 | flagSet.CreateGroup("update", "Update", 150 | flagSet.CallbackVarP(GetUpdateCallback(), "update", "up", "update dnsx to latest version"), 151 | flagSet.BoolVarP(&options.DisableUpdateCheck, "disable-update-check", "duc", false, "disable automatic dnsx update check"), 152 | ) 153 | 154 | flagSet.CreateGroup("output", "Output", 155 | flagSet.StringVarP(&options.OutputFile, "output", "o", "", "file to write output"), 156 | flagSet.BoolVarP(&options.JSON, "json", "j", false, "write output in JSONL(ines) format"), 157 | flagSet.BoolVarP(&options.OmitRaw, "or", "omit-raw", false, "omit raw dns response from jsonl output"), 158 | ) 159 | 160 | flagSet.CreateGroup("debug", "Debug", 161 | flagSet.BoolVarP(&options.HealthCheck, "health-check", "hc", false, "run diagnostic check up"), 162 | flagSet.BoolVar(&options.Silent, "silent", false, "display only results in the output"), 163 | flagSet.BoolVarP(&options.Verbose, "verbose", "v", false, "display verbose output"), 164 | flagSet.BoolVarP(&options.Raw, "debug", "raw", false, "display raw dns response"), 165 | flagSet.BoolVar(&options.ShowStatistics, "stats", false, "display stats of the running scan"), 166 | flagSet.BoolVar(&options.Version, "version", false, "display version of dnsx"), 167 | flagSet.BoolVarP(&options.NoColor, "no-color", "nc", false, "disable color in output"), 168 | ) 169 | 170 | flagSet.CreateGroup("optimization", "Optimization", 171 | flagSet.IntVar(&options.Retries, "retry", 2, "number of dns attempts to make (must be at least 1)"), 172 | flagSet.BoolVarP(&options.HostsFile, "hostsfile", "hf", false, "use system host file"), 173 | flagSet.BoolVar(&options.Trace, "trace", false, "perform dns tracing"), 174 | flagSet.IntVar(&options.TraceMaxRecursion, "trace-max-recursion", 255, "Max recursion for dns trace"), 175 | flagSet.BoolVar(&options.Resume, "resume", false, "resume existing scan"), 176 | flagSet.BoolVar(&options.Stream, "stream", false, "stream mode (wordlist, wildcard, stats and stop/resume will be disabled)"), 177 | ) 178 | 179 | flagSet.CreateGroup("configs", "Configurations", 180 | flagSet.DynamicVar(&options.PdcpAuth, "auth", "true", "configure ProjectDiscovery Cloud Platform (PDCP) api key"), 181 | flagSet.StringVarP(&options.Resolvers, "resolver", "r", "", "list of resolvers to use (file or comma separated)"), 182 | flagSet.IntVarP(&options.WildcardThreshold, "wildcard-threshold", "wt", 5, "wildcard filter threshold"), 183 | flagSet.StringVarP(&options.WildcardDomain, "wildcard-domain", "wd", "", "domain name for wildcard filtering (other flags will be ignored - only json output is supported)"), 184 | flagSet.StringVar(&options.Proxy, "proxy", "", "proxy to use (eg socks5://127.0.0.1:8080)"), 185 | ) 186 | 187 | _ = flagSet.Parse() 188 | 189 | if options.HealthCheck { 190 | gologger.Print().Msgf("%s\n", DoHealthCheck(options, flagSet)) 191 | os.Exit(0) 192 | } 193 | 194 | options.configureQueryOptions() 195 | 196 | // Read the inputs and configure the logging 197 | options.configureOutput() 198 | 199 | err := options.configureRcodes() 200 | if err != nil { 201 | gologger.Fatal().Msgf("%s\n", err) 202 | } 203 | 204 | err = options.configureResume() 205 | if err != nil { 206 | gologger.Fatal().Msgf("%s\n", err) 207 | } 208 | 209 | // api key hierarchy: cli flag > env var > .pdcp/credential file 210 | if options.PdcpAuth == "true" { 211 | AuthWithPDCP() 212 | } else if len(options.PdcpAuth) == 36 { 213 | PDCPApiKey = options.PdcpAuth 214 | ph := pdcp.PDCPCredHandler{} 215 | if _, err := ph.GetCreds(); err == pdcp.ErrNoCreds { 216 | apiServer := env.GetEnvOrDefault("PDCP_API_SERVER", pdcp.DefaultApiServer) 217 | if validatedCreds, err := ph.ValidateAPIKey(PDCPApiKey, apiServer, "dnsx"); err == nil { 218 | _ = ph.SaveCreds(validatedCreds) 219 | } 220 | } 221 | } 222 | 223 | showBanner() 224 | 225 | if options.Version { 226 | gologger.Info().Msgf("Current Version: %s\n", version) 227 | os.Exit(0) 228 | } 229 | 230 | if !options.DisableUpdateCheck { 231 | latestVersion, err := updateutils.GetToolVersionCallback("dnsx", version)() 232 | if err != nil { 233 | if options.Verbose { 234 | gologger.Error().Msgf("dnsx version check failed: %v", err.Error()) 235 | } 236 | } else { 237 | gologger.Info().Msgf("Current dnsx version %v %v", version, updateutils.GetVersionDescription(version, latestVersion)) 238 | } 239 | } 240 | 241 | options.validateOptions() 242 | 243 | return options 244 | } 245 | 246 | func (options *Options) validateOptions() { 247 | if options.Response && options.ResponseOnly { 248 | gologger.Fatal().Msgf("resp and resp-only can't be used at the same time") 249 | } 250 | 251 | if options.Retries == 0 { 252 | gologger.Fatal().Msgf("retries must be at least 1") 253 | } 254 | 255 | wordListPresent := options.WordList != "" 256 | domainsPresent := options.Domains != "" 257 | hostsPresent := options.Hosts != "" 258 | 259 | if hostsPresent && (wordListPresent || domainsPresent) { 260 | gologger.Fatal().Msgf("list(l) flag can not be used domain(d) or wordlist(w) flag") 261 | } 262 | 263 | if wordListPresent && !domainsPresent { 264 | gologger.Fatal().Msg("missing domain(d) flag required with wordlist(w) input") 265 | } 266 | if domainsPresent && !wordListPresent { 267 | gologger.Fatal().Msgf("missing wordlist(w) flag required with domain(d) input") 268 | } 269 | 270 | // stdin can be set only on one flag 271 | if argumentHasStdin(options.Domains) && argumentHasStdin(options.WordList) { 272 | if options.Stream { 273 | gologger.Fatal().Msgf("argument stdin not supported in stream mode") 274 | } 275 | gologger.Fatal().Msgf("stdin can be set for one flag") 276 | } 277 | 278 | if options.Stream { 279 | if wordListPresent { 280 | gologger.Fatal().Msgf("wordlist not supported in stream mode") 281 | } 282 | if domainsPresent { 283 | gologger.Fatal().Msgf("domains not supported in stream mode") 284 | } 285 | if options.Resume { 286 | gologger.Fatal().Msgf("resume not supported in stream mode") 287 | } 288 | if options.WildcardDomain != "" { 289 | gologger.Fatal().Msgf("wildcard not supported in stream mode") 290 | } 291 | if options.ShowStatistics { 292 | gologger.Fatal().Msgf("stats not supported in stream mode") 293 | } 294 | } 295 | } 296 | 297 | func argumentHasStdin(arg string) bool { 298 | return arg == stdinMarker 299 | } 300 | 301 | // configureOutput configures the output on the screen 302 | func (options *Options) configureOutput() { 303 | // If the user desires verbose output, show verbose output 304 | if options.Verbose { 305 | gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose) 306 | } 307 | if options.Silent { 308 | gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent) 309 | } 310 | } 311 | 312 | func (options *Options) configureRcodes() error { 313 | options.rcodes = make(map[int]struct{}) 314 | rcodes := strings.Split(options.RCode, ",") 315 | for _, rcode := range rcodes { 316 | var rc int 317 | switch strings.ToLower(rcode) { 318 | case "": 319 | continue 320 | case "noerror": 321 | rc = 0 322 | case "formerr": 323 | rc = 1 324 | case "servfail": 325 | rc = 2 326 | case "nxdomain": 327 | rc = 3 328 | case "notimp": 329 | rc = 4 330 | case "refused": 331 | rc = 5 332 | case "yxdomain": 333 | rc = 6 334 | case "yxrrset": 335 | rc = 7 336 | case "nxrrset": 337 | rc = 8 338 | case "notauth": 339 | rc = 9 340 | case "notzone": 341 | rc = 10 342 | case "badsig", "badvers": 343 | rc = 16 344 | case "badkey": 345 | rc = 17 346 | case "badtime": 347 | rc = 18 348 | case "badmode": 349 | rc = 19 350 | case "badname": 351 | rc = 20 352 | case "badalg": 353 | rc = 21 354 | case "badtrunc": 355 | rc = 22 356 | case "badcookie": 357 | rc = 23 358 | default: 359 | var err error 360 | rc, err = strconv.Atoi(rcode) 361 | if err != nil { 362 | return errors.New("invalid rcode value") 363 | } 364 | } 365 | 366 | options.rcodes[rc] = struct{}{} 367 | } 368 | 369 | options.hasRCodes = options.RCode != "" 370 | return nil 371 | } 372 | 373 | func (options *Options) configureResume() error { 374 | options.resumeCfg = &ResumeCfg{} 375 | if options.Resume && fileutil.FileExists(DefaultResumeFile) { 376 | return goconfig.Load(&options.resumeCfg, DefaultResumeFile) 377 | 378 | } 379 | return nil 380 | } 381 | 382 | func (options *Options) configureQueryOptions() { 383 | queryMap := map[string]*bool{ 384 | "a": &options.A, 385 | "aaaa": &options.AAAA, 386 | "cname": &options.CNAME, 387 | "ns": &options.NS, 388 | "txt": &options.TXT, 389 | "srv": &options.SRV, 390 | "ptr": &options.PTR, 391 | "mx": &options.MX, 392 | "soa": &options.SOA, 393 | "axfr": &options.AXFR, 394 | "caa": &options.CAA, 395 | "any": &options.ANY, 396 | } 397 | 398 | if options.QueryAll { 399 | for _, val := range queryMap { 400 | *val = true 401 | } 402 | options.Response = true 403 | // the ANY query type is not supported by the retryabledns library, 404 | // thus it's hard to filter the results when it's used in combination with other query types 405 | options.ExcludeType = append(options.ExcludeType, "any") 406 | } 407 | 408 | for _, et := range options.ExcludeType { 409 | if val, ok := queryMap[et]; ok { 410 | *val = false 411 | } 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /internal/runner/runner.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "fmt" 7 | "io" 8 | "os" 9 | "strings" 10 | "sync" 11 | "time" 12 | 13 | "github.com/logrusorgru/aurora" 14 | "github.com/miekg/dns" 15 | "github.com/pkg/errors" 16 | asnmap "github.com/projectdiscovery/asnmap/libs" 17 | "github.com/projectdiscovery/clistats" 18 | "github.com/projectdiscovery/dnsx/libs/dnsx" 19 | "github.com/projectdiscovery/goconfig" 20 | "github.com/projectdiscovery/gologger" 21 | "github.com/projectdiscovery/hmap/store/hybrid" 22 | "github.com/projectdiscovery/mapcidr" 23 | "github.com/projectdiscovery/mapcidr/asn" 24 | "github.com/projectdiscovery/ratelimit" 25 | "github.com/projectdiscovery/retryabledns" 26 | fileutil "github.com/projectdiscovery/utils/file" 27 | iputil "github.com/projectdiscovery/utils/ip" 28 | sliceutil "github.com/projectdiscovery/utils/slice" 29 | ) 30 | 31 | // Runner is a client for running the enumeration process. 32 | type Runner struct { 33 | options *Options 34 | dnsx *dnsx.DNSX 35 | wgoutputworker *sync.WaitGroup 36 | wgresolveworkers *sync.WaitGroup 37 | wgwildcardworker *sync.WaitGroup 38 | workerchan chan string 39 | outputchan chan string 40 | wildcardworkerchan chan string 41 | wildcards map[string]struct{} 42 | wildcardsmutex sync.RWMutex 43 | wildcardscache map[string][]string 44 | wildcardscachemutex sync.Mutex 45 | limiter *ratelimit.Limiter 46 | hm *hybrid.HybridMap 47 | stats clistats.StatisticsClient 48 | tmpStdinFile string 49 | aurora aurora.Aurora 50 | } 51 | 52 | func New(options *Options) (*Runner, error) { 53 | retryabledns.CheckInternalIPs = true 54 | 55 | dnsxOptions := dnsx.DefaultOptions 56 | dnsxOptions.MaxRetries = options.Retries 57 | dnsxOptions.TraceMaxRecursion = options.TraceMaxRecursion 58 | dnsxOptions.Hostsfile = options.HostsFile 59 | dnsxOptions.OutputCDN = options.OutputCDN 60 | dnsxOptions.Proxy = options.Proxy 61 | if options.Resolvers != "" { 62 | dnsxOptions.BaseResolvers = []string{} 63 | // If it's a file load resolvers from it 64 | if fileutil.FileExists(options.Resolvers) { 65 | rs, err := linesInFile(options.Resolvers) 66 | if err != nil { 67 | gologger.Fatal().Msgf("%s\n", err) 68 | } 69 | for _, rr := range rs { 70 | dnsxOptions.BaseResolvers = append(dnsxOptions.BaseResolvers, prepareResolver(rr)) 71 | } 72 | } else { 73 | // otherwise gets comma separated ones 74 | for _, rr := range strings.Split(options.Resolvers, ",") { 75 | dnsxOptions.BaseResolvers = append(dnsxOptions.BaseResolvers, prepareResolver(rr)) 76 | } 77 | } 78 | } 79 | 80 | var questionTypes []uint16 81 | if options.A { 82 | questionTypes = append(questionTypes, dns.TypeA) 83 | } 84 | if options.AAAA { 85 | questionTypes = append(questionTypes, dns.TypeAAAA) 86 | } 87 | if options.CNAME { 88 | questionTypes = append(questionTypes, dns.TypeCNAME) 89 | } 90 | if options.PTR { 91 | questionTypes = append(questionTypes, dns.TypePTR) 92 | } 93 | if options.SOA { 94 | questionTypes = append(questionTypes, dns.TypeSOA) 95 | } 96 | if options.ANY { 97 | questionTypes = append(questionTypes, dns.TypeANY) 98 | } 99 | if options.TXT { 100 | questionTypes = append(questionTypes, dns.TypeTXT) 101 | } 102 | if options.SRV { 103 | questionTypes = append(questionTypes, dns.TypeSRV) 104 | } 105 | if options.MX { 106 | questionTypes = append(questionTypes, dns.TypeMX) 107 | } 108 | if options.NS { 109 | questionTypes = append(questionTypes, dns.TypeNS) 110 | } 111 | if options.CAA { 112 | questionTypes = append(questionTypes, dns.TypeCAA) 113 | } 114 | 115 | // If no option is specified or wildcard filter has been requested use query type A 116 | if len(questionTypes) == 0 || options.WildcardDomain != "" { 117 | options.A = true 118 | questionTypes = append(questionTypes, dns.TypeA) 119 | } 120 | dnsxOptions.QuestionTypes = questionTypes 121 | dnsxOptions.QueryAll = options.QueryAll 122 | 123 | dnsX, err := dnsx.New(dnsxOptions) 124 | if err != nil { 125 | return nil, err 126 | } 127 | 128 | limiter := ratelimit.NewUnlimited(context.Background()) 129 | if options.RateLimit > 0 { 130 | limiter = ratelimit.New(context.Background(), uint(options.RateLimit), time.Second) 131 | } 132 | 133 | hm, err := hybrid.New(hybrid.DefaultDiskOptions) 134 | if err != nil { 135 | return nil, err 136 | } 137 | 138 | var stats clistats.StatisticsClient 139 | if options.ShowStatistics { 140 | stats, err = clistats.New() 141 | if err != nil { 142 | return nil, err 143 | } 144 | } 145 | 146 | if os.Getenv("NO_COLOR") == "true" { 147 | options.NoColor = true 148 | } 149 | 150 | r := Runner{ 151 | options: options, 152 | dnsx: dnsX, 153 | wgoutputworker: &sync.WaitGroup{}, 154 | wgresolveworkers: &sync.WaitGroup{}, 155 | wgwildcardworker: &sync.WaitGroup{}, 156 | workerchan: make(chan string), 157 | wildcardworkerchan: make(chan string), 158 | wildcards: make(map[string]struct{}), 159 | wildcardscache: make(map[string][]string), 160 | limiter: limiter, 161 | hm: hm, 162 | stats: stats, 163 | aurora: aurora.NewAurora(!options.NoColor), 164 | } 165 | 166 | return &r, nil 167 | } 168 | 169 | func (r *Runner) InputWorkerStream() { 170 | var sc *bufio.Scanner 171 | // attempt to load list from file 172 | if fileutil.FileExists(r.options.Hosts) { 173 | f, _ := os.Open(r.options.Hosts) 174 | sc = bufio.NewScanner(f) 175 | } else if fileutil.HasStdin() { 176 | sc = bufio.NewScanner(os.Stdin) 177 | } 178 | 179 | for sc.Scan() { 180 | item := strings.TrimSpace(sc.Text()) 181 | switch { 182 | case iputil.IsCIDR(item): 183 | hostsC, _ := mapcidr.IPAddressesAsStream(item) 184 | for host := range hostsC { 185 | r.workerchan <- host 186 | } 187 | case asn.IsASN(item): 188 | hostsC, _ := asn.GetIPAddressesAsStream(item) 189 | for host := range hostsC { 190 | r.workerchan <- host 191 | } 192 | default: 193 | r.workerchan <- item 194 | } 195 | } 196 | close(r.workerchan) 197 | } 198 | 199 | func (r *Runner) InputWorker() { 200 | r.hm.Scan(func(k, _ []byte) error { 201 | if r.options.ShowStatistics { 202 | r.stats.IncrementCounter("requests", len(r.dnsx.Options.QuestionTypes)) 203 | } 204 | item := string(k) 205 | if r.options.resumeCfg != nil { 206 | r.options.resumeCfg.current = item 207 | r.options.resumeCfg.currentIndex++ 208 | if r.options.resumeCfg.currentIndex <= r.options.resumeCfg.Index { 209 | return nil 210 | } 211 | } 212 | r.workerchan <- item 213 | return nil 214 | }) 215 | close(r.workerchan) 216 | } 217 | 218 | func (r *Runner) prepareInput() error { 219 | var ( 220 | dataDomains chan string 221 | sc chan string 222 | err error 223 | ) 224 | 225 | // copy stdin to a temporary file 226 | hasStdin := fileutil.HasStdin() 227 | if hasStdin { 228 | tmpStdinFile, err := fileutil.GetTempFileName() 229 | if err != nil { 230 | return err 231 | } 232 | r.tmpStdinFile = tmpStdinFile 233 | 234 | stdinFile, err := os.Create(r.tmpStdinFile) 235 | if err != nil { 236 | return err 237 | } 238 | if _, err := io.Copy(stdinFile, os.Stdin); err != nil { 239 | return err 240 | } 241 | // closes the file as we will read it multiple times to build the iterations 242 | stdinFile.Close() 243 | defer os.RemoveAll(r.tmpStdinFile) 244 | } 245 | 246 | if r.options.Domains != "" { 247 | dataDomains, err = r.preProcessArgument(r.options.Domains) 248 | if err != nil { 249 | return err 250 | } 251 | sc = dataDomains 252 | } 253 | 254 | if sc == nil { 255 | // attempt to load list from file 256 | if fileutil.FileExists(r.options.Hosts) { 257 | f, err := fileutil.ReadFile(r.options.Hosts) 258 | if err != nil { 259 | return err 260 | } 261 | sc = f 262 | } else if argumentHasStdin(r.options.Hosts) || hasStdin { 263 | sc, err = fileutil.ReadFile(r.tmpStdinFile) 264 | if err != nil { 265 | return err 266 | } 267 | } else { 268 | return errors.New("hosts file or stdin not provided") 269 | } 270 | } 271 | 272 | numHosts := 0 273 | for item := range sc { 274 | item := normalize(item) 275 | var hosts []string 276 | switch { 277 | case strings.Contains(item, "FUZZ"): 278 | fuzz, err := r.preProcessArgument(r.options.WordList) 279 | if err != nil { 280 | return err 281 | } 282 | for r := range fuzz { 283 | subdomain := strings.ReplaceAll(item, "FUZZ", r) 284 | hosts = append(hosts, subdomain) 285 | } 286 | numHosts += r.addHostsToHMapFromList(hosts) 287 | case r.options.WordList != "": 288 | // prepare wordlist 289 | prefixes, err := r.preProcessArgument(r.options.WordList) 290 | if err != nil { 291 | return err 292 | } 293 | for prefix := range prefixes { 294 | // domains Cartesian product with wordlist 295 | subdomain := strings.TrimSpace(prefix) + "." + item 296 | hosts = append(hosts, subdomain) 297 | } 298 | numHosts += r.addHostsToHMapFromList(hosts) 299 | case iputil.IsCIDR(item): 300 | hostC, err := mapcidr.IPAddressesAsStream(item) 301 | if err != nil { 302 | return err 303 | } 304 | numHosts += r.addHostsToHMapFromChan(hostC) 305 | case asn.IsASN(item): 306 | hostC, err := asn.GetIPAddressesAsStream(item) 307 | if err != nil { 308 | return err 309 | } 310 | numHosts += r.addHostsToHMapFromChan(hostC) 311 | default: 312 | hosts = []string{item} 313 | numHosts += r.addHostsToHMapFromList(hosts) 314 | } 315 | } 316 | if r.options.ShowStatistics { 317 | r.stats.AddStatic("hosts", numHosts) 318 | r.stats.AddStatic("startedAt", time.Now()) 319 | r.stats.AddCounter("requests", 0) 320 | r.stats.AddCounter("total", uint64(numHosts*len(r.dnsx.Options.QuestionTypes))) 321 | r.stats.AddDynamic("summary", makePrintCallback()) 322 | // nolint:errcheck 323 | r.stats.Start() 324 | r.stats.GetStatResponse(time.Second*5, func(s string, err error) error { 325 | if err != nil && r.options.Verbose { 326 | gologger.Error().Msgf("Could not read statistics: %s\n", err) 327 | } 328 | return nil 329 | }) 330 | } 331 | return nil 332 | } 333 | 334 | func (r *Runner) addHostsToHMapFromList(hosts []string) (numHosts int) { 335 | for _, host := range hosts { 336 | // Used just to get the exact number of targets 337 | if _, ok := r.hm.Get(host); ok { 338 | continue 339 | } 340 | numHosts++ 341 | // nolint:errcheck 342 | r.hm.Set(host, nil) 343 | } 344 | return 345 | } 346 | 347 | func (r *Runner) addHostsToHMapFromChan(hosts chan string) (numHosts int) { 348 | for host := range hosts { 349 | // Used just to get the exact number of targets 350 | if _, ok := r.hm.Get(host); ok { 351 | continue 352 | } 353 | numHosts++ 354 | // nolint:errcheck 355 | r.hm.Set(host, nil) 356 | } 357 | return 358 | } 359 | 360 | func (r *Runner) preProcessArgument(arg string) (chan string, error) { 361 | // read from: 362 | // file 363 | switch { 364 | case fileutil.FileExists(arg): 365 | return fileutil.ReadFile(arg) 366 | // stdin 367 | case argumentHasStdin(arg): 368 | return fileutil.ReadFile(r.tmpStdinFile) 369 | // inline 370 | case arg != "": 371 | data := strings.ReplaceAll(arg, Comma, NewLine) 372 | return fileutil.ReadFileWithReader(strings.NewReader(data)) 373 | default: 374 | return nil, errors.New("empty argument") 375 | } 376 | } 377 | 378 | func normalize(data string) string { 379 | return strings.TrimSpace(data) 380 | } 381 | 382 | // nolint:deadcode 383 | func makePrintCallback() func(stats clistats.StatisticsClient) interface{} { 384 | builder := &strings.Builder{} 385 | return func(stats clistats.StatisticsClient) interface{} { 386 | builder.WriteRune('[') 387 | startedAt, _ := stats.GetStatic("startedAt") 388 | duration := time.Since(startedAt.(time.Time)) 389 | builder.WriteString(fmtDuration(duration)) 390 | builder.WriteRune(']') 391 | 392 | hosts, _ := stats.GetStatic("hosts") 393 | builder.WriteString(" | Hosts: ") 394 | builder.WriteString(clistats.String(hosts)) 395 | 396 | requests, _ := stats.GetCounter("requests") 397 | total, _ := stats.GetCounter("total") 398 | 399 | builder.WriteString(" | RPS: ") 400 | builder.WriteString(clistats.String(uint64(float64(requests) / duration.Seconds()))) 401 | 402 | builder.WriteString(" | Requests: ") 403 | builder.WriteString(clistats.String(requests)) 404 | builder.WriteRune('/') 405 | builder.WriteString(clistats.String(total)) 406 | builder.WriteRune(' ') 407 | builder.WriteRune('(') 408 | //nolint:gomnd // this is not a magic number 409 | builder.WriteString(clistats.String(uint64(float64(requests) / float64(total) * 100.0))) 410 | builder.WriteRune('%') 411 | builder.WriteRune(')') 412 | builder.WriteRune('\n') 413 | 414 | fmt.Fprintf(os.Stderr, "%s", builder.String()) 415 | statString := builder.String() 416 | builder.Reset() 417 | return statString 418 | } 419 | } 420 | 421 | // SaveResumeConfig to file 422 | func (r *Runner) SaveResumeConfig() error { 423 | var resumeCfg ResumeCfg 424 | resumeCfg.Index = r.options.resumeCfg.currentIndex 425 | resumeCfg.ResumeFrom = r.options.resumeCfg.current 426 | return goconfig.Save(resumeCfg, DefaultResumeFile) 427 | } 428 | 429 | func (r *Runner) Run() error { 430 | if r.options.Stream { 431 | return r.runStream() 432 | } 433 | 434 | return r.run() 435 | } 436 | 437 | func (r *Runner) run() error { 438 | err := r.prepareInput() 439 | if err != nil { 440 | return err 441 | } 442 | 443 | // if resume is enabled inform the user 444 | if r.options.ShouldLoadResume() && r.options.resumeCfg.Index > 0 { 445 | gologger.Debug().Msgf("Resuming scan using file %s. Restarting at position %d: %s\n", DefaultResumeFile, r.options.resumeCfg.Index, r.options.resumeCfg.ResumeFrom) 446 | } 447 | 448 | r.startWorkers() 449 | 450 | r.wgresolveworkers.Wait() 451 | if r.stats != nil { 452 | err = r.stats.Stop() 453 | if err != nil { 454 | return err 455 | } 456 | } 457 | 458 | close(r.outputchan) 459 | r.wgoutputworker.Wait() 460 | 461 | if r.options.WildcardDomain != "" { 462 | gologger.Print().Msgf("Starting to filter wildcard subdomains\n") 463 | ipDomain := make(map[string]map[string]struct{}) 464 | listIPs := []string{} 465 | // prepare in memory structure similarly to shuffledns 466 | r.hm.Scan(func(k, v []byte) error { 467 | var dnsdata retryabledns.DNSData 468 | err := dnsdata.Unmarshal(v) 469 | if err != nil { 470 | // the item has no record - ignore 471 | return nil 472 | } 473 | 474 | for _, a := range dnsdata.A { 475 | _, ok := ipDomain[a] 476 | if !ok { 477 | ipDomain[a] = make(map[string]struct{}) 478 | listIPs = append(listIPs, a) 479 | } 480 | ipDomain[a][string(k)] = struct{}{} 481 | } 482 | 483 | return nil 484 | }) 485 | 486 | // wildcard workers 487 | numThreads := r.options.Threads 488 | if numThreads > len(listIPs) { 489 | numThreads = len(listIPs) 490 | } 491 | for i := 0; i < numThreads; i++ { 492 | r.wgwildcardworker.Add(1) 493 | go r.wildcardWorker() 494 | } 495 | 496 | seen := make(map[string]struct{}) 497 | for _, a := range listIPs { 498 | hosts := ipDomain[a] 499 | if len(hosts) >= r.options.WildcardThreshold { 500 | for host := range hosts { 501 | if _, ok := seen[host]; !ok { 502 | seen[host] = struct{}{} 503 | r.wildcardworkerchan <- host 504 | } 505 | } 506 | } 507 | } 508 | close(r.wildcardworkerchan) 509 | r.wgwildcardworker.Wait() 510 | 511 | // we need to restart output 512 | r.startOutputWorker() 513 | seen = make(map[string]struct{}) 514 | seenRemovedSubdomains := make(map[string]struct{}) 515 | numRemovedSubdomains := 0 516 | for _, A := range listIPs { 517 | for host := range ipDomain[A] { 518 | if host == r.options.WildcardDomain { 519 | if _, ok := seen[host]; !ok { 520 | seen[host] = struct{}{} 521 | _ = r.lookupAndOutput(host) 522 | } 523 | } else if _, ok := r.wildcards[host]; !ok { 524 | if _, ok := seen[host]; !ok { 525 | seen[host] = struct{}{} 526 | _ = r.lookupAndOutput(host) 527 | } 528 | } else { 529 | if _, ok := seenRemovedSubdomains[host]; !ok { 530 | numRemovedSubdomains++ 531 | seenRemovedSubdomains[host] = struct{}{} 532 | } 533 | } 534 | } 535 | } 536 | close(r.outputchan) 537 | // waiting output worker 538 | r.wgoutputworker.Wait() 539 | gologger.Print().Msgf("%d wildcard subdomains removed\n", numRemovedSubdomains) 540 | } 541 | 542 | return nil 543 | } 544 | 545 | func (r *Runner) lookupAndOutput(host string) error { 546 | if r.options.JSON { 547 | if data, ok := r.hm.Get(host); ok { 548 | var dnsData retryabledns.DNSData 549 | err := dnsData.Unmarshal(data) 550 | if err != nil { 551 | return err 552 | } 553 | dnsDataJson, err := dnsData.JSON() 554 | if err != nil { 555 | return err 556 | } 557 | r.outputchan <- dnsDataJson 558 | return err 559 | } 560 | } 561 | 562 | r.outputchan <- host 563 | return nil 564 | } 565 | 566 | func (r *Runner) runStream() error { 567 | r.startWorkers() 568 | 569 | r.wgresolveworkers.Wait() 570 | 571 | close(r.outputchan) 572 | r.wgoutputworker.Wait() 573 | 574 | return nil 575 | } 576 | 577 | func (r *Runner) HandleOutput() { 578 | defer r.wgoutputworker.Done() 579 | 580 | // setup output 581 | var ( 582 | foutput *os.File 583 | w *bufio.Writer 584 | ) 585 | if r.options.OutputFile != "" { 586 | var err error 587 | foutput, err = os.OpenFile(r.options.OutputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 588 | if err != nil { 589 | gologger.Fatal().Msgf("%s\n", err) 590 | } 591 | defer foutput.Close() 592 | w = bufio.NewWriter(foutput) 593 | defer w.Flush() 594 | } 595 | for item := range r.outputchan { 596 | if foutput != nil { 597 | // uses a buffer to write to file 598 | _, _ = w.WriteString(item + "\n") 599 | } 600 | // writes sequentially to stdout 601 | gologger.Silent().Msgf("%s\n", item) 602 | } 603 | } 604 | 605 | func (r *Runner) startOutputWorker() { 606 | // output worker 607 | r.outputchan = make(chan string) 608 | r.wgoutputworker.Add(1) 609 | go r.HandleOutput() 610 | } 611 | 612 | func (r *Runner) startWorkers() { 613 | if r.options.Stream { 614 | go r.InputWorkerStream() 615 | } else { 616 | go r.InputWorker() 617 | } 618 | 619 | r.startOutputWorker() 620 | // resolve workers 621 | for i := 0; i < r.options.Threads; i++ { 622 | r.wgresolveworkers.Add(1) 623 | go r.worker() 624 | } 625 | } 626 | 627 | func (r *Runner) worker() { 628 | defer r.wgresolveworkers.Done() 629 | for domain := range r.workerchan { 630 | if isURL(domain) { 631 | domain = extractDomain(domain) 632 | } 633 | r.limiter.Take() 634 | dnsData := dnsx.ResponseData{} 635 | // Ignoring errors as partial results are still good 636 | dnsData.DNSData, _ = r.dnsx.QueryMultiple(domain) 637 | // Just skipping nil responses (in case of critical errors) 638 | if dnsData.DNSData == nil { 639 | continue 640 | } 641 | 642 | if dnsData.Host == "" || dnsData.Timestamp.IsZero() { 643 | continue 644 | } 645 | 646 | // results from hosts file are always returned 647 | if !dnsData.HostsFile { 648 | // skip responses not having the expected response code 649 | if len(r.options.rcodes) > 0 { 650 | if _, ok := r.options.rcodes[dnsData.StatusCodeRaw]; !ok { 651 | continue 652 | } 653 | } 654 | } 655 | 656 | if !r.options.Raw { 657 | dnsData.Raw = "" 658 | } 659 | 660 | if r.options.Trace { 661 | dnsData.TraceData, _ = r.dnsx.Trace(domain) 662 | if dnsData.TraceData != nil { 663 | for _, data := range dnsData.TraceData.DNSData { 664 | if r.options.Raw && data.RawResp != nil { 665 | rawRespString := data.RawResp.String() 666 | data.Raw = rawRespString 667 | // join the whole chain in raw field 668 | dnsData.Raw += fmt.Sprintln(rawRespString) 669 | } 670 | data.RawResp = nil 671 | } 672 | } 673 | } 674 | 675 | if r.options.AXFR { 676 | hasAxfrData := false 677 | axfrData, _ := r.dnsx.AXFR(domain) 678 | if axfrData != nil { 679 | dnsData.AXFRData = axfrData 680 | hasAxfrData = len(axfrData.DNSData) > 0 681 | } 682 | 683 | // if the query type is only AFXR then output only if we have results (ref: https://github.com/projectdiscovery/dnsx/issues/230#issuecomment-1256659249) 684 | if len(r.dnsx.Options.QuestionTypes) == 1 && !hasAxfrData && !r.options.JSON { 685 | continue 686 | } 687 | } 688 | // add flags for cdn 689 | if r.options.OutputCDN { 690 | dnsData.IsCDNIP, dnsData.CDNName, _ = r.dnsx.CdnCheck(domain) 691 | } 692 | if r.options.ASN { 693 | results := []*asnmap.Response{} 694 | ips := dnsData.A 695 | if ips == nil { 696 | ips, _ = r.dnsx.Lookup(domain) 697 | } 698 | for _, ip := range ips { 699 | if data, err := asnmap.DefaultClient.GetData(ip); err == nil { 700 | results = append(results, data...) 701 | } 702 | } 703 | if iputil.IsIP(domain) { 704 | if data, err := asnmap.DefaultClient.GetData(domain); err == nil { 705 | results = append(results, data...) 706 | } 707 | } 708 | if len(results) > 0 { 709 | cidrs, _ := asnmap.GetCIDR(results) 710 | dnsData.ASN = &dnsx.AsnResponse{ 711 | AsNumber: fmt.Sprintf("AS%v", results[0].ASN), 712 | AsName: results[0].Org, 713 | AsCountry: results[0].Country, 714 | } 715 | for _, cidr := range cidrs { 716 | dnsData.ASN.AsRange = append(dnsData.ASN.AsRange, cidr.String()) 717 | } 718 | } 719 | } 720 | // if wildcard filtering just store the data 721 | if r.options.WildcardDomain != "" { 722 | _ = r.storeDNSData(dnsData.DNSData) 723 | continue 724 | } 725 | if r.options.JSON { 726 | var marshalOptions []dnsx.MarshalOption 727 | if r.options.OmitRaw { 728 | marshalOptions = append(marshalOptions, dnsx.WithoutAllRecords()) 729 | } 730 | jsons, _ := dnsData.JSON(marshalOptions...) 731 | r.outputchan <- jsons 732 | continue 733 | } 734 | if r.options.Raw { 735 | r.outputchan <- dnsData.Raw 736 | continue 737 | } 738 | if r.options.hasRCodes { 739 | r.outputResponseCode(domain, dnsData.StatusCodeRaw) 740 | continue 741 | } 742 | if r.options.A { 743 | r.outputRecordType(domain, dnsData.A, "A", dnsData.CDNName, dnsData.ASN) 744 | } 745 | if r.options.AAAA { 746 | r.outputRecordType(domain, dnsData.AAAA, "AAAA", dnsData.CDNName, dnsData.ASN) 747 | } 748 | if r.options.CNAME { 749 | r.outputRecordType(domain, dnsData.CNAME, "CNAME", dnsData.CDNName, dnsData.ASN) 750 | } 751 | if r.options.PTR { 752 | r.outputRecordType(domain, dnsData.PTR, "PTR", dnsData.CDNName, dnsData.ASN) 753 | } 754 | if r.options.MX { 755 | r.outputRecordType(domain, dnsData.MX, "MX", dnsData.CDNName, dnsData.ASN) 756 | } 757 | if r.options.NS { 758 | r.outputRecordType(domain, dnsData.NS, "NS", dnsData.CDNName, dnsData.ASN) 759 | } 760 | if r.options.SOA { 761 | r.outputRecordType(domain, sliceutil.Dedupe(dnsData.GetSOARecords()), "SOA", dnsData.CDNName, dnsData.ASN) 762 | } 763 | if r.options.ANY { 764 | allParsedRecords := sliceutil.Merge( 765 | dnsData.A, 766 | dnsData.AAAA, 767 | dnsData.CNAME, 768 | dnsData.MX, 769 | dnsData.PTR, 770 | sliceutil.Dedupe(dnsData.GetSOARecords()), 771 | dnsData.NS, 772 | dnsData.TXT, 773 | dnsData.SRV, 774 | dnsData.CAA, 775 | ) 776 | r.outputRecordType(domain, allParsedRecords, "ANY", dnsData.CDNName, dnsData.ASN) 777 | } 778 | if r.options.TXT { 779 | r.outputRecordType(domain, dnsData.TXT, "TXT", dnsData.CDNName, dnsData.ASN) 780 | } 781 | if r.options.SRV { 782 | r.outputRecordType(domain, dnsData.SRV, "SRV", dnsData.CDNName, dnsData.ASN) 783 | } 784 | if r.options.CAA { 785 | r.outputRecordType(domain, dnsData.CAA, "CAA", dnsData.CDNName, dnsData.ASN) 786 | } 787 | } 788 | } 789 | 790 | func (r *Runner) outputRecordType(domain string, items interface{}, queryType, cdnName string, asn *dnsx.AsnResponse) { 791 | var details string 792 | if cdnName != "" { 793 | details = fmt.Sprintf(" [%s]", cdnName) 794 | } 795 | if asn != nil { 796 | details = fmt.Sprintf("%s %s", details, asn.String()) 797 | } 798 | var records []string 799 | 800 | switch items := items.(type) { 801 | case []string: 802 | records = items 803 | case []retryabledns.SOA: 804 | for _, item := range items { 805 | records = append(records, item.NS, item.Mbox) 806 | } 807 | } 808 | 809 | for _, item := range records { 810 | item := strings.ToLower(item) 811 | if r.options.ResponseOnly { 812 | r.outputchan <- fmt.Sprintf("%s%s", item, details) 813 | } else if r.options.Response { 814 | r.outputchan <- fmt.Sprintf("%s [%s] [%s] %s", domain, r.aurora.Magenta(queryType), r.aurora.Green(item).String(), details) 815 | } else { 816 | // just prints out the domain if it has a record type and exit 817 | r.outputchan <- fmt.Sprintf("%s%s", domain, details) 818 | break 819 | } 820 | } 821 | } 822 | 823 | func (r *Runner) outputResponseCode(domain string, responsecode int) { 824 | responseCodeExt, ok := dns.RcodeToString[responsecode] 825 | if ok { 826 | r.outputchan <- domain + " [" + responseCodeExt + "]" 827 | } 828 | } 829 | 830 | func (r *Runner) storeDNSData(dnsdata *retryabledns.DNSData) error { 831 | data, err := dnsdata.Marshal() 832 | if err != nil { 833 | return err 834 | } 835 | return r.hm.Set(dnsdata.Host, data) 836 | } 837 | 838 | // Close running instance 839 | func (r *Runner) Close() { 840 | r.hm.Close() 841 | } 842 | 843 | func (r *Runner) wildcardWorker() { 844 | defer r.wgwildcardworker.Done() 845 | 846 | for { 847 | host, more := <-r.wildcardworkerchan 848 | if !more { 849 | break 850 | } 851 | 852 | if r.IsWildcard(host) { 853 | // mark this host as a wildcard subdomain 854 | r.wildcardsmutex.Lock() 855 | r.wildcards[host] = struct{}{} 856 | r.wildcardsmutex.Unlock() 857 | } 858 | } 859 | } 860 | -------------------------------------------------------------------------------- /internal/runner/runner_test.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/projectdiscovery/hmap/store/hybrid" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestRunner_singleDomain_prepareInput(t *testing.T) { 13 | options := &Options{ 14 | Domains: "one.one.one.one", 15 | } 16 | hm, err := hybrid.New(hybrid.DefaultDiskOptions) 17 | require.Nil(t, err, "could not create hybrid map") 18 | r := Runner{ 19 | options: options, 20 | hm: hm, 21 | } 22 | // call the prepareInput 23 | err = r.prepareInput() 24 | require.Nil(t, err, "failed to prepare input") 25 | expected := []string{"one.one.one.one"} 26 | got := []string{} 27 | r.hm.Scan(func(k, v []byte) error { 28 | got = append(got, string(k)) 29 | return nil 30 | }) 31 | require.ElementsMatch(t, expected, got, "could not match expected output") 32 | } 33 | 34 | func TestRunner_domainWildCard_prepareInput(t *testing.T) { 35 | options := &Options{ 36 | Domains: "projectdiscovery.io", 37 | WordList: "jenkins,beta", 38 | } 39 | hm, err := hybrid.New(hybrid.DefaultDiskOptions) 40 | require.Nil(t, err, "could not create hybrid map") 41 | r := Runner{ 42 | options: options, 43 | hm: hm, 44 | } 45 | // call the prepareInput 46 | err = r.prepareInput() 47 | require.Nil(t, err, "failed to prepare input") 48 | expected := []string{"jenkins.projectdiscovery.io", "beta.projectdiscovery.io"} 49 | got := []string{} 50 | r.hm.Scan(func(k, v []byte) error { 51 | got = append(got, string(k)) 52 | return nil 53 | }) 54 | require.ElementsMatch(t, expected, got, "could not match expected output") 55 | } 56 | 57 | func TestRunner_cidrInput_prepareInput(t *testing.T) { 58 | options := &Options{ 59 | Domains: "173.0.84.0/30", 60 | } 61 | hm, err := hybrid.New(hybrid.DefaultDiskOptions) 62 | require.Nil(t, err, "could not create hybrid map") 63 | r := Runner{ 64 | options: options, 65 | hm: hm, 66 | } 67 | // call the prepareInput 68 | err = r.prepareInput() 69 | require.Nil(t, err, "failed to prepare input") 70 | expected := []string{"173.0.84.0", "173.0.84.1", "173.0.84.2", "173.0.84.3"} 71 | got := []string{} 72 | r.hm.Scan(func(k, v []byte) error { 73 | got = append(got, string(k)) 74 | return nil 75 | }) 76 | require.ElementsMatch(t, expected, got, "could not match expected output") 77 | } 78 | 79 | func TestRunner_asnInput_prepareInput(t *testing.T) { 80 | options := &Options{ 81 | Domains: "AS14421", 82 | } 83 | hm, err := hybrid.New(hybrid.DefaultDiskOptions) 84 | require.Nil(t, err, "could not create hybrid map") 85 | r := Runner{ 86 | options: options, 87 | hm: hm, 88 | } 89 | // call the prepareInput 90 | err = r.prepareInput() 91 | require.Nil(t, err, "failed to prepare input") 92 | expectedOutputFile := "tests/AS14421.txt" 93 | // read the expected IPs from the file 94 | fileContent, err := os.ReadFile(expectedOutputFile) 95 | require.Nil(t, err, "could not read the expectedOutputFile file") 96 | expected := strings.Split(strings.ReplaceAll(string(fileContent), "\r\n", "\n"), "\n") 97 | got := []string{} 98 | r.hm.Scan(func(k, v []byte) error { 99 | got = append(got, string(k)) 100 | return nil 101 | }) 102 | require.ElementsMatch(t, expected, got, "could not match expected output") 103 | } 104 | 105 | func TestRunner_fileInput_prepareInput(t *testing.T) { 106 | options := &Options{ 107 | Hosts: "tests/file_input.txt", 108 | } 109 | hm, err := hybrid.New(hybrid.DefaultDiskOptions) 110 | require.Nil(t, err, "could not create hybrid map") 111 | r := Runner{ 112 | options: options, 113 | hm: hm, 114 | } 115 | // call the prepareInput 116 | err = r.prepareInput() 117 | require.Nil(t, err, "failed to prepare input") 118 | expected := []string{"one.one.one.one", "example.com"} 119 | got := []string{} 120 | r.hm.Scan(func(k, v []byte) error { 121 | got = append(got, string(k)) 122 | return nil 123 | }) 124 | require.ElementsMatch(t, expected, got, "could not match expected output") 125 | } 126 | 127 | func TestRunner_InputWorkerStream(t *testing.T) { 128 | options := &Options{ 129 | Hosts: "tests/stream_input.txt", 130 | } 131 | r := Runner{ 132 | options: options, 133 | workerchan: make(chan string), 134 | } 135 | go r.InputWorkerStream() 136 | var got []string 137 | for c := range r.workerchan { 138 | got = append(got, c) 139 | } 140 | expected := []string{"173.0.84.0", "173.0.84.1", "173.0.84.2", "173.0.84.3", "one.one.one.one"} 141 | // read the expected IPs from the file 142 | fileContent, err := os.ReadFile("tests/AS14421.txt") 143 | require.Nil(t, err, "could not read the expectedOutputFile file") 144 | expected = append(expected, strings.Split(strings.ReplaceAll(string(fileContent), "\r\n", "\n"), "\n")...) 145 | require.ElementsMatch(t, expected, got, "could not match expected output") 146 | } 147 | -------------------------------------------------------------------------------- /internal/runner/tests/AS14421.txt: -------------------------------------------------------------------------------- 1 | 216.101.17.0 2 | 216.101.17.1 3 | 216.101.17.2 4 | 216.101.17.3 5 | 216.101.17.4 6 | 216.101.17.5 7 | 216.101.17.6 8 | 216.101.17.7 9 | 216.101.17.8 10 | 216.101.17.9 11 | 216.101.17.10 12 | 216.101.17.11 13 | 216.101.17.12 14 | 216.101.17.13 15 | 216.101.17.14 16 | 216.101.17.15 17 | 216.101.17.16 18 | 216.101.17.17 19 | 216.101.17.18 20 | 216.101.17.19 21 | 216.101.17.20 22 | 216.101.17.21 23 | 216.101.17.22 24 | 216.101.17.23 25 | 216.101.17.24 26 | 216.101.17.25 27 | 216.101.17.26 28 | 216.101.17.27 29 | 216.101.17.28 30 | 216.101.17.29 31 | 216.101.17.30 32 | 216.101.17.31 33 | 216.101.17.32 34 | 216.101.17.33 35 | 216.101.17.34 36 | 216.101.17.35 37 | 216.101.17.36 38 | 216.101.17.37 39 | 216.101.17.38 40 | 216.101.17.39 41 | 216.101.17.40 42 | 216.101.17.41 43 | 216.101.17.42 44 | 216.101.17.43 45 | 216.101.17.44 46 | 216.101.17.45 47 | 216.101.17.46 48 | 216.101.17.47 49 | 216.101.17.48 50 | 216.101.17.49 51 | 216.101.17.50 52 | 216.101.17.51 53 | 216.101.17.52 54 | 216.101.17.53 55 | 216.101.17.54 56 | 216.101.17.55 57 | 216.101.17.56 58 | 216.101.17.57 59 | 216.101.17.58 60 | 216.101.17.59 61 | 216.101.17.60 62 | 216.101.17.61 63 | 216.101.17.62 64 | 216.101.17.63 65 | 216.101.17.64 66 | 216.101.17.65 67 | 216.101.17.66 68 | 216.101.17.67 69 | 216.101.17.68 70 | 216.101.17.69 71 | 216.101.17.70 72 | 216.101.17.71 73 | 216.101.17.72 74 | 216.101.17.73 75 | 216.101.17.74 76 | 216.101.17.75 77 | 216.101.17.76 78 | 216.101.17.77 79 | 216.101.17.78 80 | 216.101.17.79 81 | 216.101.17.80 82 | 216.101.17.81 83 | 216.101.17.82 84 | 216.101.17.83 85 | 216.101.17.84 86 | 216.101.17.85 87 | 216.101.17.86 88 | 216.101.17.87 89 | 216.101.17.88 90 | 216.101.17.89 91 | 216.101.17.90 92 | 216.101.17.91 93 | 216.101.17.92 94 | 216.101.17.93 95 | 216.101.17.94 96 | 216.101.17.95 97 | 216.101.17.96 98 | 216.101.17.97 99 | 216.101.17.98 100 | 216.101.17.99 101 | 216.101.17.100 102 | 216.101.17.101 103 | 216.101.17.102 104 | 216.101.17.103 105 | 216.101.17.104 106 | 216.101.17.105 107 | 216.101.17.106 108 | 216.101.17.107 109 | 216.101.17.108 110 | 216.101.17.109 111 | 216.101.17.110 112 | 216.101.17.111 113 | 216.101.17.112 114 | 216.101.17.113 115 | 216.101.17.114 116 | 216.101.17.115 117 | 216.101.17.116 118 | 216.101.17.117 119 | 216.101.17.118 120 | 216.101.17.119 121 | 216.101.17.120 122 | 216.101.17.121 123 | 216.101.17.122 124 | 216.101.17.123 125 | 216.101.17.124 126 | 216.101.17.125 127 | 216.101.17.126 128 | 216.101.17.127 129 | 216.101.17.128 130 | 216.101.17.129 131 | 216.101.17.130 132 | 216.101.17.131 133 | 216.101.17.132 134 | 216.101.17.133 135 | 216.101.17.134 136 | 216.101.17.135 137 | 216.101.17.136 138 | 216.101.17.137 139 | 216.101.17.138 140 | 216.101.17.139 141 | 216.101.17.140 142 | 216.101.17.141 143 | 216.101.17.142 144 | 216.101.17.143 145 | 216.101.17.144 146 | 216.101.17.145 147 | 216.101.17.146 148 | 216.101.17.147 149 | 216.101.17.148 150 | 216.101.17.149 151 | 216.101.17.150 152 | 216.101.17.151 153 | 216.101.17.152 154 | 216.101.17.153 155 | 216.101.17.154 156 | 216.101.17.155 157 | 216.101.17.156 158 | 216.101.17.157 159 | 216.101.17.158 160 | 216.101.17.159 161 | 216.101.17.160 162 | 216.101.17.161 163 | 216.101.17.162 164 | 216.101.17.163 165 | 216.101.17.164 166 | 216.101.17.165 167 | 216.101.17.166 168 | 216.101.17.167 169 | 216.101.17.168 170 | 216.101.17.169 171 | 216.101.17.170 172 | 216.101.17.171 173 | 216.101.17.172 174 | 216.101.17.173 175 | 216.101.17.174 176 | 216.101.17.175 177 | 216.101.17.176 178 | 216.101.17.177 179 | 216.101.17.178 180 | 216.101.17.179 181 | 216.101.17.180 182 | 216.101.17.181 183 | 216.101.17.182 184 | 216.101.17.183 185 | 216.101.17.184 186 | 216.101.17.185 187 | 216.101.17.186 188 | 216.101.17.187 189 | 216.101.17.188 190 | 216.101.17.189 191 | 216.101.17.190 192 | 216.101.17.191 193 | 216.101.17.192 194 | 216.101.17.193 195 | 216.101.17.194 196 | 216.101.17.195 197 | 216.101.17.196 198 | 216.101.17.197 199 | 216.101.17.198 200 | 216.101.17.199 201 | 216.101.17.200 202 | 216.101.17.201 203 | 216.101.17.202 204 | 216.101.17.203 205 | 216.101.17.204 206 | 216.101.17.205 207 | 216.101.17.206 208 | 216.101.17.207 209 | 216.101.17.208 210 | 216.101.17.209 211 | 216.101.17.210 212 | 216.101.17.211 213 | 216.101.17.212 214 | 216.101.17.213 215 | 216.101.17.214 216 | 216.101.17.215 217 | 216.101.17.216 218 | 216.101.17.217 219 | 216.101.17.218 220 | 216.101.17.219 221 | 216.101.17.220 222 | 216.101.17.221 223 | 216.101.17.222 224 | 216.101.17.223 225 | 216.101.17.224 226 | 216.101.17.225 227 | 216.101.17.226 228 | 216.101.17.227 229 | 216.101.17.228 230 | 216.101.17.229 231 | 216.101.17.230 232 | 216.101.17.231 233 | 216.101.17.232 234 | 216.101.17.233 235 | 216.101.17.234 236 | 216.101.17.235 237 | 216.101.17.236 238 | 216.101.17.237 239 | 216.101.17.238 240 | 216.101.17.239 241 | 216.101.17.240 242 | 216.101.17.241 243 | 216.101.17.242 244 | 216.101.17.243 245 | 216.101.17.244 246 | 216.101.17.245 247 | 216.101.17.246 248 | 216.101.17.247 249 | 216.101.17.248 250 | 216.101.17.249 251 | 216.101.17.250 252 | 216.101.17.251 253 | 216.101.17.252 254 | 216.101.17.253 255 | 216.101.17.254 256 | 216.101.17.255 -------------------------------------------------------------------------------- /internal/runner/tests/file_input.txt: -------------------------------------------------------------------------------- 1 | one.one.one.one 2 | example.com -------------------------------------------------------------------------------- /internal/runner/tests/stream_input.txt: -------------------------------------------------------------------------------- 1 | 173.0.84.0/30 2 | AS14421 3 | one.one.one.one -------------------------------------------------------------------------------- /internal/runner/util.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "strings" 7 | "time" 8 | 9 | fileutil "github.com/projectdiscovery/utils/file" 10 | ) 11 | 12 | const ( 13 | stdinMarker = "-" 14 | Comma = "," 15 | NewLine = "\n" 16 | ) 17 | 18 | func linesInFile(fileName string) ([]string, error) { 19 | result := []string{} 20 | f, err := fileutil.ReadFile(fileName) 21 | if err != nil { 22 | return nil, err 23 | } 24 | for line := range f { 25 | result = append(result, line) 26 | } 27 | return result, nil 28 | } 29 | 30 | // isURL tests a string to determine if it is a well-structured url or not. 31 | func isURL(toTest string) bool { 32 | _, err := url.ParseRequestURI(toTest) 33 | if err != nil { 34 | return false 35 | } 36 | 37 | u, err := url.Parse(toTest) 38 | if err != nil || u.Scheme == "" || u.Host == "" { 39 | return false 40 | } 41 | 42 | return true 43 | } 44 | 45 | func extractDomain(URL string) string { 46 | u, err := url.Parse(URL) 47 | if err != nil { 48 | return "" 49 | } 50 | 51 | return u.Hostname() 52 | } 53 | 54 | func prepareResolver(resolver string) string { 55 | resolver = strings.TrimSpace(resolver) 56 | if !strings.Contains(resolver, ":") { 57 | resolver += ":53" 58 | } 59 | return resolver 60 | } 61 | 62 | func fmtDuration(d time.Duration) string { 63 | d = d.Round(time.Second) 64 | h := d / time.Hour 65 | d -= h * time.Hour 66 | m := d / time.Minute 67 | d -= m * time.Minute 68 | s := d / time.Second 69 | return fmt.Sprintf("%d:%02d:%02d", h, m, s) 70 | } 71 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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(strings.TrimSpace(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(strings.TrimSpace(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 | -------------------------------------------------------------------------------- /libs/dnsx/cdn.go: -------------------------------------------------------------------------------- 1 | package dnsx 2 | 3 | import ( 4 | "net" 5 | 6 | errorutil "github.com/projectdiscovery/utils/errors" 7 | iputil "github.com/projectdiscovery/utils/ip" 8 | ) 9 | 10 | // CdnCheck verifies if the given ip is part of Cdn ranges 11 | func (d *DNSX) CdnCheck(domain string) (bool, string, error) { 12 | if d.cdn == nil { 13 | return false, "", errorutil.New("cdn client not initialized") 14 | } 15 | ips, err := net.LookupIP(domain) 16 | if err != nil { 17 | return false, "", err 18 | } 19 | ipv4Ips := []net.IP{} 20 | // filter ipv4s for ips 21 | for _, ip := range ips { 22 | if iputil.IsIPv4(ip) { 23 | ipv4Ips = append(ipv4Ips, ip) 24 | } 25 | } 26 | if len(ipv4Ips) < 1 { 27 | return false, "", errorutil.New("No IPV4s found in lookup for %v", domain) 28 | } 29 | ipAddr := ipv4Ips[0].String() 30 | if !iputil.IsIP(ipAddr) { 31 | return false, "", errorutil.New("%s is not a valid ip", ipAddr) 32 | } 33 | return d.cdn.CheckCDN(net.ParseIP((ipAddr))) 34 | } 35 | -------------------------------------------------------------------------------- /libs/dnsx/dnsx.go: -------------------------------------------------------------------------------- 1 | package dnsx 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | 8 | miekgdns "github.com/miekg/dns" 9 | "github.com/projectdiscovery/cdncheck" 10 | retryabledns "github.com/projectdiscovery/retryabledns" 11 | iputil "github.com/projectdiscovery/utils/ip" 12 | sliceutil "github.com/projectdiscovery/utils/slice" 13 | ) 14 | 15 | // DNSX is structure to perform dns lookups 16 | type DNSX struct { 17 | dnsClient *retryabledns.Client 18 | Options *Options 19 | cdn *cdncheck.Client 20 | } 21 | 22 | // Options contains configuration options 23 | type Options struct { 24 | BaseResolvers []string 25 | MaxRetries int 26 | QuestionTypes []uint16 27 | Trace bool 28 | TraceMaxRecursion int 29 | Hostsfile bool 30 | OutputCDN bool 31 | QueryAll bool 32 | Proxy string 33 | } 34 | 35 | // ResponseData to show output result 36 | type ResponseData struct { 37 | *retryabledns.DNSData 38 | IsCDNIP bool `json:"cdn,omitempty" csv:"cdn"` 39 | CDNName string `json:"cdn-name,omitempty" csv:"cdn-name"` 40 | ASN *AsnResponse `json:"asn,omitempty" csv:"asn"` 41 | } 42 | type AsnResponse struct { 43 | AsNumber string `json:"as-number,omitempty" csv:"as_number"` 44 | AsName string `json:"as-name,omitempty" csv:"as_name"` 45 | AsCountry string `json:"as-country,omitempty" csv:"as_country"` 46 | AsRange []string `json:"as-range,omitempty" csv:"as_range"` 47 | } 48 | 49 | func (o *AsnResponse) String() string { 50 | return fmt.Sprintf("[%v, %v, %v]", o.AsNumber, o.AsName, o.AsCountry) 51 | } 52 | 53 | type MarshalOption func(d *ResponseData) 54 | 55 | func WithoutAllRecords() MarshalOption { 56 | return func(d *ResponseData) { 57 | d.AllRecords = nil 58 | } 59 | } 60 | 61 | func (d *ResponseData) JSON(options ...MarshalOption) (string, error) { 62 | dataToMarshal := *d 63 | for _, option := range options { 64 | option(d) 65 | } 66 | b, err := json.Marshal(dataToMarshal) 67 | return string(b), err 68 | } 69 | 70 | // DefaultOptions contains the default configuration options 71 | var DefaultOptions = Options{ 72 | BaseResolvers: DefaultResolvers, 73 | MaxRetries: 5, 74 | QuestionTypes: []uint16{miekgdns.TypeA}, 75 | TraceMaxRecursion: 255, 76 | Hostsfile: true, 77 | } 78 | 79 | // DefaultResolvers contains the list of resolvers known to be trusted. 80 | var DefaultResolvers = []string{ 81 | "udp:1.1.1.1:53", // Cloudflare 82 | "udp:1.0.0.1:53", // Cloudflare 83 | "udp:8.8.8.8:53", // Google 84 | "udp:8.8.4.4:53", // Google 85 | "udp:9.9.9.9:53", // Quad9 86 | "udp:149.112.112.112:53", // Quad9 87 | "udp:208.67.222.222:53", // Open DNS 88 | "udp:208.67.220.220:53", // Open DNS 89 | } 90 | 91 | // New creates a dns resolver 92 | func New(options Options) (*DNSX, error) { 93 | retryablednsOptions := retryabledns.Options{ 94 | BaseResolvers: options.BaseResolvers, 95 | MaxRetries: options.MaxRetries, 96 | Hostsfile: options.Hostsfile, 97 | Proxy: options.Proxy, 98 | } 99 | 100 | dnsClient, err := retryabledns.NewWithOptions(retryablednsOptions) 101 | if err != nil { 102 | return nil, err 103 | } 104 | dnsClient.TCPFallback = true 105 | dnsx := &DNSX{dnsClient: dnsClient, Options: &options} 106 | if options.OutputCDN { 107 | dnsx.cdn = cdncheck.New() 108 | } 109 | return dnsx, nil 110 | } 111 | 112 | // Lookup performs a DNS A question and returns corresponding IPs 113 | func (d *DNSX) Lookup(hostname string) ([]string, error) { 114 | if iputil.IsIP(hostname) { 115 | return []string{hostname}, nil 116 | } 117 | 118 | dnsdata, err := d.dnsClient.Resolve(hostname) 119 | if err != nil { 120 | return nil, err 121 | } 122 | 123 | if dnsdata == nil || len(dnsdata.A) == 0 { 124 | return []string{}, errors.New("no ips found") 125 | } 126 | 127 | return dnsdata.A, nil 128 | } 129 | 130 | // QueryOne performs a DNS question of a specified type and returns raw responses 131 | func (d *DNSX) QueryOne(hostname string) (*retryabledns.DNSData, error) { 132 | return d.dnsClient.Query(hostname, d.Options.QuestionTypes[0]) 133 | } 134 | 135 | // QueryMultiple performs a DNS question of the specified types and returns raw responses 136 | func (d *DNSX) QueryMultiple(hostname string) (*retryabledns.DNSData, error) { 137 | // Omit PTR queries unless the input is an IP address to decrease execution time, as PTR queries can lead to timeouts. 138 | filteredQuestionTypes := d.Options.QuestionTypes 139 | if d.Options.QueryAll { 140 | isIP := iputil.IsIP(hostname) 141 | if !isIP { 142 | filteredQuestionTypes = sliceutil.PruneEqual(filteredQuestionTypes, miekgdns.TypePTR) 143 | } else { 144 | filteredQuestionTypes = []uint16{miekgdns.TypePTR} 145 | } 146 | } 147 | return d.dnsClient.QueryMultiple(hostname, filteredQuestionTypes) 148 | } 149 | 150 | // Trace performs a DNS trace of the specified types and returns raw responses 151 | func (d *DNSX) Trace(hostname string) (*retryabledns.TraceData, error) { 152 | return d.dnsClient.Trace(hostname, d.Options.QuestionTypes[0], d.Options.TraceMaxRecursion) 153 | } 154 | 155 | // Trace performs a DNS trace of the specified types and returns raw responses 156 | func (d *DNSX) AXFR(hostname string) (*retryabledns.AXFRData, error) { 157 | return d.dnsClient.AXFR(hostname) 158 | } 159 | -------------------------------------------------------------------------------- /libs/dnsx/doc.go: -------------------------------------------------------------------------------- 1 | // Package dnsx contains the library logic 2 | package dnsx 3 | -------------------------------------------------------------------------------- /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 "ANY": 25 | rt = dns.TypeANY 26 | case "MX": 27 | rt = dns.TypeMX 28 | case "TXT": 29 | rt = dns.TypeTXT 30 | case "SRV": 31 | rt = dns.TypeSRV 32 | case "AAAA": 33 | rt = dns.TypeAAAA 34 | default: 35 | rt = dns.TypeNone 36 | err = fmt.Errorf("incorrect type") 37 | } 38 | 39 | return 40 | } 41 | -------------------------------------------------------------------------------- /static/dnsx-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectdiscovery/dnsx/d4003075936404c5baeab67162c17d10be9a914e/static/dnsx-logo.png -------------------------------------------------------------------------------- /static/dnsx-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/projectdiscovery/dnsx/d4003075936404c5baeab67162c17d10be9a914e/static/dnsx-run.png --------------------------------------------------------------------------------