├── .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 |
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 |
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
--------------------------------------------------------------------------------