├── .dockerignore ├── .github ├── dependabot.yml ├── release.yml └── workflows │ ├── build-test.yml │ ├── dep-auto-merge.yml │ ├── dockerhub-push.yml │ ├── lint-test.yml │ ├── release-binary.yml │ └── release-test.yml ├── .gitignore ├── .goreleaser.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cmd └── asnmap │ └── asnmap.go ├── examples └── simple.go ├── go.mod ├── go.sum ├── libs ├── cidr.go ├── client.go ├── client_test.go ├── config.go ├── default_client.go ├── resolver.go ├── resolver_test.go ├── types.go └── types_test.go └── runner ├── banner.go ├── options.go ├── output.go ├── runner.go └── runner_test.go /.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | *.idea 3 | **/.git 4 | *.gitignore 5 | *.sh 6 | Makefile 7 | LICENSE 8 | README.md 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | 9 | # Maintain dependencies for go modules 10 | - package-ecosystem: "gomod" 11 | directory: "/" 12 | schedule: 13 | interval: "weekly" 14 | target-branch: "dev" 15 | commit-message: 16 | prefix: "chore" 17 | include: "scope" 18 | labels: 19 | - "Type: Maintenance" 20 | allow: 21 | - dependency-name: "github.com/projectdiscovery/*" 22 | 23 | # # Maintain dependencies for docker 24 | - package-ecosystem: "docker" 25 | directory: "/" 26 | schedule: 27 | interval: "weekly" 28 | target-branch: "dev" 29 | commit-message: 30 | prefix: "chore" 31 | include: "scope" 32 | labels: 33 | - "Type: Maintenance" 34 | 35 | # Maintain dependencies for GitHub Actions 36 | - package-ecosystem: "github-actions" 37 | directory: "/" 38 | schedule: 39 | interval: "weekly" 40 | target-branch: "dev" 41 | commit-message: 42 | prefix: "chore" 43 | include: "scope" 44 | labels: 45 | - "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 | workflow_dispatch: 6 | 7 | jobs: 8 | build: 9 | name: Test Builds 10 | runs-on: ubuntu-latest-16-cores 11 | steps: 12 | - name: Set up Go 13 | uses: actions/setup-go@v4 14 | with: 15 | go-version: 1.21.x 16 | 17 | - name: Check out code 18 | uses: actions/checkout@v3 19 | 20 | - name: Build 21 | run: go build . 22 | working-directory: cmd/asnmap/ 23 | 24 | - name: Example Code Tests 25 | run: go build . 26 | working-directory: examples/ 27 | 28 | - name: Run test 29 | run: go test ./... 30 | env: 31 | PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}" 32 | -------------------------------------------------------------------------------- /.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/asnmap/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 39 | push: true 40 | tags: projectdiscovery/asnmap:latest,projectdiscovery/asnmap:${{ steps.meta.outputs.TAG }} 41 | -------------------------------------------------------------------------------- /.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-16-cores 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v3 14 | 15 | - name: "Set up Go" 16 | uses: actions/setup-go@v4 17 | with: 18 | go-version: 1.21.x 19 | 20 | - name: Run golangci-lint 21 | uses: golangci/golangci-lint-action@v3.4.0 22 | with: 23 | version: latest 24 | args: --timeout 5m 25 | working-directory: . 26 | -------------------------------------------------------------------------------- /.github/workflows/release-binary.yml: -------------------------------------------------------------------------------- 1 | name: 🎉 Release Binary 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 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 }}" 34 | -------------------------------------------------------------------------------- /.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 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.idea 8 | .vscode 9 | .devcontainer 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | dist/ 20 | /asnmap 21 | /cmd/asnmap/asnmap -------------------------------------------------------------------------------- /.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/asnmap/asnmap.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 git build-base gcc musl-dev 5 | WORKDIR /app 6 | COPY . /app 7 | RUN go mod download 8 | RUN go build ./cmd/asnmap 9 | 10 | FROM alpine:3.18.0 11 | RUN apk -U upgrade --no-cache \ 12 | && apk add --no-cache bind-tools ca-certificates 13 | COPY --from=builder /app/asnmap /usr/local/bin/ 14 | 15 | ENTRYPOINT ["asnmap"] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ProjectDiscovery 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Go parameters 2 | GOCMD=go 3 | GOBUILD=$(GOCMD) build 4 | GOMOD=$(GOCMD) mod 5 | GOTEST=$(GOCMD) test 6 | GOFLAGS := -v 7 | # This should be disabled if the binary uses pprof 8 | LDFLAGS := -s -w 9 | 10 | ifneq ($(shell go env GOOS),darwin) 11 | LDFLAGS := -extldflags "-static" 12 | endif 13 | 14 | 15 | .PHONY: cli test tidy 16 | 17 | cli, build: 18 | $(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "asnmap" ./cmd/asnmap/ 19 | test: 20 | $(GOCLEAN) -testcache 21 | $(GOTEST) -v ./... 22 | tidy: 23 | $(GOMOD) tidy 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | asnmap 3 |
4 |

5 | 6 |

Go CLI and Library for quickly mapping organization network ranges using ASN information.

7 | 8 |

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

15 | 16 |

17 | Features • 18 | Installation • 19 | Usage • 20 | Running asnmap • 21 | Join Discord 22 |

23 | 24 | 25 | **** 26 | 27 | 28 | 29 | # Features 30 | 31 | ![image](https://user-images.githubusercontent.com/8293321/192092220-5d734305-fd3e-43fb-919a-91ff5296dfd2.png) 32 | 33 | 34 | - **ASN to CIDR** Lookup 35 | - **ORG to CIDR** Lookup 36 | - **DNS to CIDR** Lookup 37 | - **IP to CIDR** Lookup 38 | - **ASN/DNS/IP/ORG** input 39 | - **JSON/CSV/TEXT** output 40 | - STD **IN/OUT** support 41 | 42 | ## Installation 43 | 44 | asnmap requires **Go 1.21** to install successfully. To install, just run the below command or download pre-compiled binary from [release page](https://github.com/projectdiscovery/asnmap/releases). 45 | 46 | ```console 47 | go install github.com/projectdiscovery/asnmap/cmd/asnmap@latest 48 | ``` 49 | 50 | ## Usage 51 | 52 | ```console 53 | asnmap -h 54 | ``` 55 | 56 | This will display help for the tool. Here are all the flag it supports. 57 | 58 | ```console 59 | Usage: 60 | ./asnmap [flags] 61 | 62 | Flags: 63 | INPUT: 64 | -a, -asn string[] target asn to lookup, example: -a AS5650 65 | -i, -ip string[] target ip to lookup, example: -i 100.19.12.21, -i 2a10:ad40:: 66 | -d, -domain string[] target domain to lookup, example: -d google.com, -d facebook.com 67 | -org string[] target organization to lookup, example: -org GOOGLE 68 | -f, -file string[] targets to lookup from file 69 | 70 | CONFIGURATIONS: 71 | -config string path to the asnmap configuration file 72 | -r, -resolvers string[] list of resolvers to use 73 | 74 | UPDATE: 75 | -up, -update update asnmap to latest version 76 | -duc, -disable-update-check disable automatic asnmap update check 77 | 78 | OUTPUT: 79 | -o, -output string file to write output to 80 | -j, -json display json format output 81 | -c, -csv display csv format output 82 | -v6 display ipv6 cidr ranges in cli output 83 | -v, -verbose display verbose output 84 | -silent display silent output 85 | -version show version of the project 86 | ``` 87 | 88 | ## Configuring ASNMap CLI 89 | 90 | ASNMap CLI is built on top of the ASNMap API that requires API Token from [ProjectDiscovery Cloud Platform](https://cloud.projectdiscovery.io/?ref=api_key) that can be configured using environment variable or using interactive `-auth` option as shown below. 91 | 92 | ### Using environment variable 93 | 94 | ```console 95 | export PDCP_API_KEY=************* 96 | ``` 97 | 98 | ### Using auth option 99 | 100 | ```console 101 | asnmap -auth 102 | 103 | 104 | ___ _____ __ 105 | / _ | / __/ |/ /_ _ ___ ____ 106 | / __ |_\ \/ / ' \/ _ / _ \ 107 | /_/ |_/___/_/|_/_/_/_/\_,_/ .__/ 108 | /_/ 109 | 110 | projectdiscovery.io 111 | 112 | [INF] Get your free api key by signing up at https://cloud.projectdiscovery.io 113 | [*] Enter PDCP API Key (exit to abort): ************* 114 | [INF] Successfully logged in as (@user) 115 | ``` 116 | 117 | ## Running asnmap 118 | 119 | **asnmap** support multiple inputs including **ASN**, **IP**, **DNS** and **ORG** name to query ASN/CIDR information. 120 | 121 | 122 | | Input | ASN | DNS | IP | ORG | 123 | | ------- | --------- | ------------- | --------------- | -------- | 124 | | Example | `AS14421` | `example.com` | `93.184.216.34` | `GOOGLE` | 125 | 126 | 127 | 128 | Input can be provided either using specific options or STDIN which accepts all the supported formats. Single, multiple (comma-separated) and file input is supported for all the options. 129 | 130 | ```console 131 | echo GOOGLE | ./asnmap -silent 132 | ``` 133 | 134 | Example input for asnmap: 135 | 136 | ```console 137 | asnmap -a AS45596 -silent 138 | asnmap -i 100.19.12.21 -silent 139 | asnmap -d hackerone.com -silent 140 | asnmap -org GOOGLE -silent 141 | ``` 142 | 143 | ### Default Run 144 | 145 | **asnmap** by default returns the CIDR range for given input. 146 | 147 | ```console 148 | echo GOOGLE | ./asnmap 149 | 150 | ___ _____ __ 151 | / _ | / __/ |/ /_ _ ___ ____ 152 | / __ |_\ \/ / ' \/ _ / _ \ 153 | /_/ |_/___/_/|_/_/_/_/\_,_/ .__/ 154 | /_/ v0.0.1 155 | projectdiscovery.io 156 | 157 | Use with caution. You are responsible for your actions 158 | Developers assume no liability and are not responsible for any misuse or damage. 159 | 160 | 8.8.4.0/24 161 | 8.8.8.0/24 162 | 8.35.200.0/21 163 | 34.3.3.0/24 164 | 34.4.4.0/24 165 | 34.96.0.0/20 166 | 34.96.32.0/19 167 | 34.96.64.0/18 168 | 34.98.64.0/18 169 | 34.98.136.0/21 170 | 34.98.144.0/21 171 | ``` 172 | ### JSON Output 173 | 174 | **asnmap** by default displays CIDR range, and all the information is always available in JSON format, for automation and post processing using `-json` output is most convenient option to use. 175 | 176 | ```console 177 | echo hackerone.com | ./asnmap -json -silent | jq 178 | ``` 179 | 180 | ```json 181 | { 182 | "timestamp": "2022-09-19 12:14:33.267339314 +0530 IST", 183 | "input": "hackerone.com", 184 | "as_number": "AS13335", 185 | "as_name": "CLOUDFLARENET", 186 | "as_country": "US", 187 | "as_range": [ 188 | "104.16.0.0/14", 189 | "104.20.0.0/16", 190 | "104.21.0.0/17" 191 | ] 192 | } 193 | { 194 | "timestamp": "2022-09-19 12:14:33.457401266 +0530 IST", 195 | "input": "hackerone.com", 196 | "as_number": "AS13335", 197 | "as_name": "CLOUDFLARENET", 198 | "as_country": "US", 199 | "as_range": [ 200 | "2606:4700:8390::/44" 201 | ] 202 | } 203 | ``` 204 | ### CSV Output 205 | 206 | **asnmap** also support csv format output which has all the information just like JSON output 207 | 208 | ```console 209 | echo hackerone.com | ./asnmap -csv -silent 210 | ``` 211 | 212 | ```console 213 | timestamp|input|as_number|as_name|as_country|as_range 214 | 2022-09-19 12:15:04.906664007 +0530 IST|hackerone.com|AS13335|CLOUDFLARENET|US|104.16.0.0/14,104.20.0.0/16,104.21.0.0/17 215 | 2022-09-19 12:15:05.201328136 +0530 IST|hackerone.com|AS13335|CLOUDFLARENET|US|2606:4700:9760::/44 216 | ``` 217 | 218 | ### Using with other PD projects 219 | 220 | Output of asnmap can be directly piped into other projects in workflow accepting stdin as input, for example: 221 | 222 | - `echo AS54115 | asnmap | tlsx` 223 | - `echo AS54115 | asnmap | dnsx -ptr` 224 | - `echo AS54115 | asnmap | naabu -p 443` 225 | - `echo AS54115 | asnmap | naabu -p 443 | httpx` 226 | - `echo AS54115 | asnmap | naabu -p 443 | httpx | nuclei -id tech-detect` 227 | 228 | ## Use asnmap as a library 229 | 230 | Examples of using asnmap from Go code are provided in the [examples](examples/) folder. 231 | 232 | ## Acknowledgements 233 | 234 | - [Frank Denis](https://github.com/jedisct1/) for maintaining free IPtoASN database. 235 | 236 | ----- 237 | 238 |
239 | 240 | **asnmap** is made with ❤️ by the [projectdiscovery](https://projectdiscovery.io) team and distributed under [MIT License](LICENSE). 241 | 242 | 243 | Join Discord 244 | 245 |
246 | -------------------------------------------------------------------------------- /cmd/asnmap/asnmap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "os/signal" 7 | 8 | asnmap "github.com/projectdiscovery/asnmap/libs" 9 | "github.com/projectdiscovery/asnmap/runner" 10 | "github.com/projectdiscovery/gologger" 11 | "github.com/projectdiscovery/utils/auth/pdcp" 12 | ) 13 | 14 | func main() { 15 | // Parse the command line flags and read config files 16 | options := runner.ParseOptions() 17 | 18 | asnmapRunner, err := runner.New(options) 19 | if err != nil { 20 | gologger.Fatal().Msgf("Could not create runner: %s\n", err) 21 | } 22 | 23 | defer func() { 24 | _ = asnmapRunner.Close() 25 | }() 26 | 27 | // Setup graceful exits 28 | c := make(chan os.Signal, 1) 29 | signal.Notify(c, os.Interrupt) 30 | go func() { 31 | for range c { 32 | gologger.Info().Msgf("CTRL+C pressed: Exiting\n") 33 | // Close should be called explicitly as it doesn't honor 34 | _ = asnmapRunner.Close() 35 | os.Exit(1) 36 | } 37 | }() 38 | 39 | if err := asnmapRunner.Run(); err != nil { 40 | if errors.Is(err, asnmap.ErrUnAuthorized) { 41 | gologger.Info().Msgf("Try again after authenticating with PDCP\n\n") 42 | // trigger auth callback 43 | pdcp.CheckNValidateCredentials("asnmap") 44 | // run again 45 | if err := asnmapRunner.Run(); err != nil { 46 | gologger.Fatal().Msgf("%s\n", err) 47 | } 48 | } 49 | gologger.Fatal().Msgf("%s\n", err) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | 7 | asnmap "github.com/projectdiscovery/asnmap/libs" 8 | ) 9 | 10 | func main() { 11 | client, err := asnmap.NewClient() 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | 16 | items := []string{ 17 | // Query based on ASN 18 | "14421", 19 | // Query based on IP 20 | "210.10.122.10", 21 | // Query based on Organization 22 | "pplinknet", 23 | } 24 | for _, item := range items { 25 | handleInput(client, item) 26 | } 27 | 28 | // Query based on domain 29 | domain := "hackerone.com" 30 | resolvedIps, err := asnmap.ResolveDomain(domain) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | for _, ip := range resolvedIps { 35 | handleInput(client, ip) 36 | } 37 | } 38 | 39 | func handleInput(client *asnmap.Client, item string) { 40 | responses, err := client.GetData(item) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | results, err := asnmap.MapToResults(responses) 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | 49 | output, err := json.Marshal(results) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | 54 | if len(output) > 0 { 55 | log.Printf("%s: %s", item, string(output)) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/projectdiscovery/asnmap 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/projectdiscovery/goflags v0.1.56 7 | github.com/projectdiscovery/gologger v1.1.12 8 | github.com/projectdiscovery/hmap v0.0.47 9 | github.com/projectdiscovery/mapcidr v1.1.34 10 | github.com/projectdiscovery/retryabledns v1.0.63 11 | github.com/stretchr/testify v1.9.0 12 | golang.org/x/net v0.23.0 13 | ) 14 | 15 | require ( 16 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 17 | github.com/aymerick/douceur v0.2.0 // indirect 18 | github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/gorilla/css v1.0.0 // indirect 21 | github.com/json-iterator/go v1.1.12 // indirect 22 | github.com/logrusorgru/aurora v2.0.3+incompatible // indirect 23 | github.com/microcosm-cc/bluemonday v1.0.25 // indirect 24 | github.com/miekg/dns v1.1.56 // indirect 25 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 26 | github.com/modern-go/reflect2 v1.0.2 // indirect 27 | github.com/pkg/errors v0.9.1 // indirect 28 | github.com/pmezard/go-difflib v1.0.0 // indirect 29 | github.com/projectdiscovery/blackrock v0.0.1 // indirect 30 | github.com/projectdiscovery/utils v0.1.4 31 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect 32 | go.uber.org/multierr v1.11.0 // indirect 33 | golang.org/x/mod v0.12.0 // indirect 34 | golang.org/x/sys v0.18.0 // indirect 35 | golang.org/x/tools v0.13.0 // indirect 36 | gopkg.in/yaml.v3 v3.0.1 // indirect 37 | ) 38 | 39 | require ( 40 | aead.dev/minisign v0.2.0 // indirect 41 | github.com/Masterminds/semver/v3 v3.2.1 // indirect 42 | github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057 // indirect 43 | github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 // indirect 44 | github.com/VividCortex/ewma v1.2.0 // indirect 45 | github.com/akrylysov/pogreb v0.10.1 // indirect 46 | github.com/alecthomas/chroma v0.10.0 // indirect 47 | github.com/andybalholm/brotli v1.0.6 // indirect 48 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 49 | github.com/bits-and-blooms/bitset v1.13.0 // indirect 50 | github.com/charmbracelet/glamour v0.6.0 // indirect 51 | github.com/cheggaaa/pb/v3 v3.1.4 // indirect 52 | github.com/cloudflare/circl v1.3.7 // indirect 53 | github.com/dimchansky/utfbom v1.1.1 // indirect 54 | github.com/dlclark/regexp2 v1.8.1 // indirect 55 | github.com/docker/go-units v0.5.0 // indirect 56 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect 57 | github.com/fatih/color v1.15.0 // indirect 58 | github.com/gaissmai/bart v0.9.5 // indirect 59 | github.com/gaukas/godicttls v0.0.4 // indirect 60 | github.com/go-ole/go-ole v1.2.6 // indirect 61 | github.com/golang/protobuf v1.5.3 // indirect 62 | github.com/golang/snappy v0.0.4 // indirect 63 | github.com/google/go-github/v30 v30.1.0 // indirect 64 | github.com/google/go-querystring v1.1.0 // indirect 65 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect 66 | github.com/google/uuid v1.3.1 // indirect 67 | github.com/klauspost/compress v1.16.7 // indirect 68 | github.com/klauspost/pgzip v1.2.5 // indirect 69 | github.com/kr/pretty v0.2.1 // indirect 70 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 71 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect 72 | github.com/mattn/go-colorable v0.1.13 // indirect 73 | github.com/mattn/go-isatty v0.0.19 // indirect 74 | github.com/mattn/go-runewidth v0.0.14 // indirect 75 | github.com/mholt/archiver/v3 v3.5.1 // indirect 76 | github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 // indirect 77 | github.com/muesli/reflow v0.3.0 // indirect 78 | github.com/muesli/termenv v0.15.1 // indirect 79 | github.com/nwaples/rardecode v1.1.3 // indirect 80 | github.com/olekukonko/tablewriter v0.0.5 // indirect 81 | github.com/pierrec/lz4/v4 v4.1.2 // indirect 82 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect 83 | github.com/projectdiscovery/fastdialer v0.1.3 // indirect 84 | github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect 85 | github.com/projectdiscovery/networkpolicy v0.0.9 // indirect 86 | github.com/projectdiscovery/retryablehttp-go v1.0.65 // indirect 87 | github.com/quic-go/quic-go v0.42.0 // indirect 88 | github.com/refraction-networking/utls v1.5.4 // indirect 89 | github.com/rivo/uniseg v0.4.4 // indirect 90 | github.com/shirou/gopsutil/v3 v3.23.7 // indirect 91 | github.com/shoenig/go-m1cpu v0.1.6 // indirect 92 | github.com/syndtr/goleveldb v1.0.0 // indirect 93 | github.com/tidwall/btree v1.6.0 // indirect 94 | github.com/tidwall/buntdb v1.3.0 // indirect 95 | github.com/tidwall/gjson v1.14.4 // indirect 96 | github.com/tidwall/grect v0.1.4 // indirect 97 | github.com/tidwall/match v1.1.1 // indirect 98 | github.com/tidwall/pretty v1.2.1 // indirect 99 | github.com/tidwall/rtred v0.1.2 // indirect 100 | github.com/tidwall/tinyqueue v0.1.1 // indirect 101 | github.com/tklauser/go-sysconf v0.3.12 // indirect 102 | github.com/tklauser/numcpus v0.6.1 // indirect 103 | github.com/ulikunitz/xz v0.5.11 // indirect 104 | github.com/weppos/publicsuffix-go v0.30.1-0.20230422193905-8fecedd899db // indirect 105 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect 106 | github.com/yuin/goldmark v1.5.4 // indirect 107 | github.com/yuin/goldmark-emoji v1.0.1 // indirect 108 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 109 | github.com/zcalusic/sysinfo v1.0.2 // indirect 110 | github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 // indirect 111 | github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968 // indirect 112 | go.etcd.io/bbolt v1.3.7 // indirect 113 | golang.org/x/crypto v0.21.0 // indirect 114 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect 115 | golang.org/x/oauth2 v0.11.0 // indirect 116 | golang.org/x/term v0.18.0 // indirect 117 | golang.org/x/text v0.14.0 // indirect 118 | google.golang.org/appengine v1.6.7 // indirect 119 | google.golang.org/protobuf v1.33.0 // indirect 120 | gopkg.in/djherbis/times.v1 v1.3.0 // indirect 121 | ) 122 | -------------------------------------------------------------------------------- /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/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= 11 | github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= 12 | github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w= 13 | github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= 14 | github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= 15 | github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= 16 | github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= 17 | github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= 18 | github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= 19 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= 20 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 21 | github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= 22 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= 23 | github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 24 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= 25 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 26 | github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= 27 | github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 28 | github.com/bits-and-blooms/bloom/v3 v3.5.0 h1:AKDvi1V3xJCmSR6QhcBfHbCN4Vf8FfxeWkMNQfmAGhY= 29 | github.com/bits-and-blooms/bloom/v3 v3.5.0/go.mod h1:Y8vrn7nk1tPIlmLtW2ZPV+W7StdVMor6bC1xgpjMZFs= 30 | github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= 31 | github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= 32 | github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= 33 | github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA= 34 | github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= 35 | github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= 36 | github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ= 37 | github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4= 38 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 39 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 40 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 41 | github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= 42 | github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= 43 | github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= 44 | github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0= 45 | github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 46 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 47 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 48 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= 49 | github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= 50 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 51 | github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= 52 | github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= 53 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 54 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 55 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 56 | github.com/gaissmai/bart v0.9.5 h1:vy+r4Px6bjZ+v2QYXAsg63vpz9IfzdW146A8Cn4GPIo= 57 | github.com/gaissmai/bart v0.9.5/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg= 58 | github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= 59 | github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= 60 | github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= 61 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 62 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 63 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 64 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 65 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 66 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 67 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 68 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 69 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 70 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 71 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 72 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 73 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 74 | github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 75 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 76 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 77 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 78 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 79 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 80 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 81 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 82 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 83 | github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo= 84 | github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= 85 | github.com/google/go-github/v50 v50.1.0/go.mod h1:Ev4Tre8QoKiolvbpOSG3FIi4Mlon3S2Nt9W5JYqKiwA= 86 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 87 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 88 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 89 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 90 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= 91 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 92 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= 93 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 94 | github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= 95 | github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 96 | github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= 97 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= 98 | github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM= 99 | github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 100 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 101 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 102 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 103 | github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= 104 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 105 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 106 | github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 107 | github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= 108 | github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 109 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 110 | github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= 111 | github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 112 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 113 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 114 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 115 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 116 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 117 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 118 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 119 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 120 | github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= 121 | github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= 122 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 123 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 124 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= 125 | github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 126 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 127 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 128 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 129 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 130 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 131 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 132 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 133 | github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= 134 | github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 135 | github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= 136 | github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= 137 | github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= 138 | github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= 139 | github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= 140 | github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= 141 | github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= 142 | github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 h1:yRZGarbxsRytL6EGgbqK2mCY+Lk5MWKQYKJT2gEglhc= 143 | github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM= 144 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 145 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 146 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 147 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 148 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 149 | github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= 150 | github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= 151 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= 152 | github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= 153 | github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= 154 | github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs= 155 | github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ= 156 | github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 157 | github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= 158 | github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= 159 | github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= 160 | github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= 161 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 162 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 163 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 164 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 165 | github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= 166 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 167 | github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= 168 | github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= 169 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 170 | github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= 171 | github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= 172 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 173 | github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= 174 | github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 175 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 176 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 177 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 178 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 179 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 180 | github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 181 | github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k0VbGJyft6LQ= 182 | github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss= 183 | github.com/projectdiscovery/fastdialer v0.1.3 h1:DuXMFFDnhHP2pXWYZXqZHS76vSvkMeRYD4NifI/DlXI= 184 | github.com/projectdiscovery/fastdialer v0.1.3/go.mod h1:V5fkUburMLylbtBqbei5aAXTt1foDMeX517iM3uPkao= 185 | github.com/projectdiscovery/goflags v0.1.56 h1:tJYiZN7s9Jk9DxfYOUiqOoybaIDlXyX4ZgT4B/06SyU= 186 | github.com/projectdiscovery/goflags v0.1.56/go.mod h1:DsGF0NPpM5hGg75N3MTSvWJ4MIT7HFEAOEeWZ074+Fg= 187 | github.com/projectdiscovery/gologger v1.1.12 h1:uX/QkQdip4PubJjjG0+uk5DtyAi1ANPJUvpmimXqv4A= 188 | github.com/projectdiscovery/gologger v1.1.12/go.mod h1:DI8nywPLERS5mo8QEA9E7gd5HZ3Je14SjJBH3F5/kLw= 189 | github.com/projectdiscovery/hmap v0.0.47 h1:NdakfvlFruevnOvehdllofg/hc0CQn2WQaLHaRcsGAk= 190 | github.com/projectdiscovery/hmap v0.0.47/go.mod h1:/9V6EnyTY33hAy71Quox6Ggu9ZkKp36tgepBh/NzNSg= 191 | github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 h1:ZScLodGSezQVwsQDtBSMFp72WDq0nNN+KE/5DHKY5QE= 192 | github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI= 193 | github.com/projectdiscovery/mapcidr v1.1.34 h1:udr83vQ7oz3kEOwlsU6NC6o08leJzSDQtls1wmXN/kM= 194 | github.com/projectdiscovery/mapcidr v1.1.34/go.mod h1:1+1R6OkKSAKtWDXE9RvxXtXPoajXTYX0eiEdkqlhQqQ= 195 | github.com/projectdiscovery/networkpolicy v0.0.9 h1:IrlDoYZagNNO8y+7iZeHT8k5izE+nek7TdtvEBwCxqk= 196 | github.com/projectdiscovery/networkpolicy v0.0.9/go.mod h1:XFJ2Lnv8BE/ziQCFjBHMsH1w6VmkPiQtk+NlBpdMU7M= 197 | github.com/projectdiscovery/retryabledns v1.0.63 h1:Ijt47ybwf+iIwul606NlPKPN+ouhOYPeA9sLHVgqLG4= 198 | github.com/projectdiscovery/retryabledns v1.0.63/go.mod h1:lTs48OYJnMFuuBzT+3z3PrZ58K0OUBgP7Y4o3ttBwb0= 199 | github.com/projectdiscovery/retryablehttp-go v1.0.65 h1:GCZI9CUbYkldy9Iup4C7w6aVTxZuAHpSZTVy92qHPo4= 200 | github.com/projectdiscovery/retryablehttp-go v1.0.65/go.mod h1:qF0UuglJiYJcezEoMIyYVYhHGwaOBy8bh3gRGz7CBs8= 201 | github.com/projectdiscovery/utils v0.1.4 h1:VSULN/RBEXbkgWuhrDey84ZDnSXtYMSev9p+QHXXDeI= 202 | github.com/projectdiscovery/utils v0.1.4/go.mod h1:mXs6OOeG9l/dVchjB2PGvQO3+wuMiE14Y/kmHeKogoM= 203 | github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM= 204 | github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= 205 | github.com/refraction-networking/utls v1.5.4 h1:9k6EO2b8TaOGsQ7Pl7p9w6PUhx18/ZCeT0WNTZ7Uw4o= 206 | github.com/refraction-networking/utls v1.5.4/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw= 207 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 208 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 209 | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= 210 | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 211 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= 212 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= 213 | github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= 214 | github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= 215 | github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= 216 | github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= 217 | github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= 218 | github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= 219 | github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 220 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 221 | github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 222 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 223 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 224 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 225 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 226 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 227 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 228 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 229 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 230 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 231 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 232 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 233 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 234 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 235 | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= 236 | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= 237 | github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= 238 | github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= 239 | github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= 240 | github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= 241 | github.com/tidwall/buntdb v1.3.0 h1:gdhWO+/YwoB2qZMeAU9JcWWsHSYU3OvcieYgFRS0zwA= 242 | github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU= 243 | github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 244 | github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= 245 | github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 246 | github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= 247 | github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= 248 | github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= 249 | github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= 250 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 251 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 252 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 253 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 254 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 255 | github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= 256 | github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= 257 | github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= 258 | github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= 259 | github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= 260 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 261 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 262 | github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= 263 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 264 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 265 | github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 266 | github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 267 | github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= 268 | github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 269 | github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= 270 | github.com/weppos/publicsuffix-go v0.30.1-0.20230422193905-8fecedd899db h1:/WcxBne+5CbtbgWd/sV2wbravmr4sT7y52ifQaCgoLs= 271 | github.com/weppos/publicsuffix-go v0.30.1-0.20230422193905-8fecedd899db/go.mod h1:aiQaH1XpzIfgrJq3S1iw7w+3EDbRP7mF5fmwUhWyRUs= 272 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= 273 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= 274 | github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU= 275 | github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= 276 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 277 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 278 | github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 279 | github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= 280 | github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 281 | github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= 282 | github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= 283 | github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 284 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= 285 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 286 | github.com/zcalusic/sysinfo v1.0.2 h1:nwTTo2a+WQ0NXwo0BGRojOJvJ/5XKvQih+2RrtWqfxc= 287 | github.com/zcalusic/sysinfo v1.0.2/go.mod h1:kluzTYflRWo6/tXVMJPdEjShsbPpsFRyy+p1mBQPC30= 288 | github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= 289 | github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 h1:Nzukz5fNOBIHOsnP+6I79kPx3QhLv8nBy2mfFhBRq30= 290 | github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= 291 | github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= 292 | github.com/zmap/zcertificate v0.0.1/go.mod h1:q0dlN54Jm4NVSSuzisusQY0hqDWvu92C+TWveAxiVWk= 293 | github.com/zmap/zcrypto v0.0.0-20201128221613-3719af1573cf/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= 294 | github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= 295 | github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968 h1:YOQ1vXEwE4Rnj+uQ/3oCuJk5wgVsvUyW+glsndwYuyA= 296 | github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968/go.mod h1:xIuOvYCZX21S5Z9bK1BMrertTGX/F8hgAPw7ERJRNS0= 297 | github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8= 298 | go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= 299 | go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= 300 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 301 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 302 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 303 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 304 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 305 | golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 306 | golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 307 | golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 308 | golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 309 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 310 | golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 311 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 312 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 313 | golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= 314 | golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= 315 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= 316 | golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 317 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 318 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 319 | golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= 320 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 321 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 322 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 323 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 324 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 325 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 326 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 327 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 328 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 329 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 330 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 331 | golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 332 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 333 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 334 | golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= 335 | golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 336 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 337 | golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= 338 | golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= 339 | golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= 340 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 341 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 342 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 343 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 344 | golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= 345 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 346 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 347 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 348 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 349 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 350 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 351 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 352 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 353 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 354 | golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 355 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 356 | golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 357 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 358 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 359 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 360 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 361 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 362 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 363 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 364 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 365 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 366 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 367 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 368 | golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 369 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 370 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 371 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 372 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 373 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 374 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 375 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 376 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 377 | golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= 378 | golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= 379 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 380 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 381 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 382 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 383 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 384 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 385 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 386 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 387 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 388 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 389 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 390 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 391 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 392 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 393 | golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= 394 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 395 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 396 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 397 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 398 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 399 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 400 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 401 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 402 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 403 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 404 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 405 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 406 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 407 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 408 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 409 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 410 | gopkg.in/djherbis/times.v1 v1.3.0 h1:uxMS4iMtH6Pwsxog094W0FYldiNnfY/xba00vq6C2+o= 411 | gopkg.in/djherbis/times.v1 v1.3.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv6mUY0P8= 412 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 413 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 414 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 415 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 416 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 417 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 418 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 419 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 420 | -------------------------------------------------------------------------------- /libs/cidr.go: -------------------------------------------------------------------------------- 1 | package asnmap 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/projectdiscovery/mapcidr" 7 | ) 8 | 9 | func GetCIDR(output []*Response) ([]*net.IPNet, error) { 10 | var cidrs []*net.IPNet 11 | for _, res := range output { 12 | cidr, err := mapcidr.GetCIDRFromIPRange(net.ParseIP(res.FirstIp), net.ParseIP(res.LastIp)) 13 | if err != nil { 14 | return nil, err 15 | } 16 | cidrs = append(cidrs, cidr...) 17 | } 18 | return cidrs, nil 19 | } 20 | -------------------------------------------------------------------------------- /libs/client.go: -------------------------------------------------------------------------------- 1 | package asnmap 2 | 3 | import ( 4 | "bufio" 5 | "crypto/tls" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "net" 11 | "net/http" 12 | url "net/url" 13 | "os" 14 | "strings" 15 | "time" 16 | 17 | "github.com/projectdiscovery/gologger" 18 | "github.com/projectdiscovery/utils/auth/pdcp" 19 | "github.com/projectdiscovery/utils/env" 20 | fileutil "github.com/projectdiscovery/utils/file" 21 | stringsutil "github.com/projectdiscovery/utils/strings" 22 | updateutils "github.com/projectdiscovery/utils/update" 23 | urlutil "github.com/projectdiscovery/utils/url" 24 | "golang.org/x/net/proxy" 25 | ) 26 | 27 | const serverURL = "https://asn.projectdiscovery.io/" 28 | 29 | var ( 30 | PDCPApiKey = env.GetEnvOrDefault("PDCP_API_KEY", "") 31 | ErrUnAuthorized = errors.New("unauthorized: 401 (get free api key to configure from https://cloud.projectdiscovery.io/?ref=api_key)") 32 | ) 33 | 34 | func init() { 35 | if PDCPApiKey == "" { 36 | pch := pdcp.PDCPCredHandler{} 37 | if creds, err := pch.GetCreds(); err == nil { 38 | PDCPApiKey = creds.APIKey 39 | } 40 | } 41 | } 42 | 43 | type Client struct { 44 | url *url.URL 45 | http *http.Client 46 | } 47 | 48 | // generatefullURL creates the complete URL with path, scheme, and host 49 | func generateFullURL(host string) (*url.URL, error) { 50 | rawURL, err := url.Parse(host) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | if !stringsutil.EqualFoldAny(rawURL.Scheme, "http", "https") { 56 | return nil, errors.New("host should start with http or https") 57 | } 58 | 59 | rawURL.Path = "api/v1/asnmap" 60 | return rawURL, nil 61 | } 62 | 63 | // If SERVER_URL env provider use that else use serverURL constant 64 | func getURL() (*url.URL, error) { 65 | url := os.Getenv("SERVER_URL") 66 | if url == "" { 67 | url = serverURL 68 | } 69 | return generateFullURL(url) 70 | } 71 | 72 | func NewClient() (*Client, error) { 73 | URL, err := getURL() 74 | if err != nil { 75 | return nil, err 76 | } 77 | 78 | // ignore expired SSL certificates 79 | transCfg := &http.Transport{ 80 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 81 | } 82 | 83 | client := Client{ 84 | url: URL, 85 | http: &http.Client{Transport: transCfg}, 86 | } 87 | return &client, nil 88 | } 89 | 90 | // SetProxy adds a proxy to the client 91 | func (c *Client) SetProxy(proxyList []string) (*url.URL, error) { 92 | var ( 93 | proxyUrl *url.URL 94 | err error 95 | ) 96 | for _, p := range proxyList { 97 | switch { 98 | case fileutil.FileExists(p): 99 | proxyUrl, err = c.setProxyFromFile(p) 100 | default: 101 | proxyUrl, err = c.setProxy(p) 102 | } 103 | if err == nil && proxyUrl != nil { 104 | return proxyUrl, nil 105 | } 106 | } 107 | return nil, errors.New("no valid proxy found") 108 | } 109 | 110 | // setProxyFromFile reads the file contents and tries to set the proxy 111 | func (c *Client) setProxyFromFile(fileName string) (*url.URL, error) { 112 | file, err := os.Open(fileName) 113 | if err != nil { 114 | return nil, err 115 | } 116 | defer file.Close() 117 | scanner := bufio.NewScanner(file) 118 | for scanner.Scan() { 119 | proxy := strings.TrimSpace(scanner.Text()) 120 | if proxy == "" { 121 | continue 122 | } 123 | proxyUrl, err := c.setProxy(proxy) 124 | if err == nil && proxyUrl != nil { 125 | return proxyUrl, nil 126 | } 127 | } 128 | return nil, fmt.Errorf("no valid proxy found in file '%s'", fileName) 129 | } 130 | 131 | // setProxy sets a proxy to the client 132 | func (c *Client) setProxy(proxyString string) (*url.URL, error) { 133 | // parse the proxy url string 134 | proxyurl, err := url.Parse(proxyString) 135 | if err != nil { 136 | return nil, err 137 | } 138 | 139 | // try to connect to the proxy 140 | _, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%s", proxyurl.Hostname(), proxyurl.Port()), time.Second*5) 141 | if err != nil { 142 | return nil, err 143 | } 144 | 145 | switch proxyurl.Scheme { 146 | case "http", "https": 147 | c.http.Transport = &http.Transport{ 148 | Proxy: http.ProxyURL(proxyurl), 149 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 150 | } 151 | return proxyurl, nil 152 | case "socks5": 153 | dialer, err := proxy.SOCKS5("tcp", proxyurl.Host, nil, proxy.Direct) 154 | if err != nil { 155 | return nil, err 156 | } 157 | c.http.Transport = &http.Transport{ 158 | Dial: dialer.Dial, 159 | } 160 | return proxyurl, nil 161 | default: 162 | return nil, fmt.Errorf("invalid proxy scheme: %s", proxyurl.Scheme) 163 | } 164 | } 165 | 166 | func (c Client) makeRequest() ([]byte, error) { 167 | if c.http == nil { 168 | return nil, errors.New("http client is not initialized") 169 | } 170 | 171 | req, err := http.NewRequest(http.MethodGet, c.url.String(), nil) 172 | if err != nil { 173 | return nil, err 174 | } 175 | if PDCPApiKey == "" { 176 | gologger.Error().Label("asnmap-api").Msgf("missing or invalid api key (get free api key & configure it from https://cloud.projectdiscovery.io/?ref=api_key)") 177 | return nil, ErrUnAuthorized 178 | } 179 | req.Header.Set("X-PDCP-Key", PDCPApiKey) 180 | res, err := c.http.Do(req) 181 | if err != nil { 182 | return nil, err 183 | } 184 | defer res.Body.Close() 185 | if res.StatusCode == http.StatusUnauthorized { 186 | gologger.Error().Msgf("missing or invalid api key (get free api key & configure it from https://cloud.projectdiscovery.io/?ref=api_key)") 187 | return nil, ErrUnAuthorized 188 | } 189 | 190 | if res.StatusCode == http.StatusBadRequest { 191 | body, _ := io.ReadAll(res.Body) 192 | bodyStr := string(body) 193 | errMsg := fmt.Sprintf("bad request: %s", bodyStr) 194 | 195 | gologger.Error().Msg(errMsg) 196 | return nil, errors.New(errMsg) 197 | } 198 | 199 | resBody, err := io.ReadAll(res.Body) 200 | if err != nil { 201 | return nil, err 202 | } 203 | return resBody, nil 204 | } 205 | 206 | func (c Client) GetDataWithCustomInput(inputToQuery, inputToUseInResponse string) ([]*Response, error) { 207 | results, err := c.GetData(inputToQuery) 208 | for _, result := range results { 209 | result.Input = inputToUseInResponse 210 | } 211 | return results, err 212 | } 213 | 214 | func (c Client) GetData(input string, medatadas ...string) ([]*Response, error) { 215 | inputToStore := input 216 | params := urlutil.NewOrderedParams() 217 | switch IdentifyInput(input) { 218 | case ASN: 219 | inputToStore = strings.TrimPrefix(strings.ToLower(input), "as") 220 | params.Add("asn", inputToStore) 221 | case ASNID: 222 | params.Add("asn", input) 223 | case IP: 224 | params.Add("ip", input) 225 | case Org: 226 | params.Add("org", input) 227 | case Unknown: 228 | return nil, errors.New("unknown type") 229 | } 230 | 231 | params.Decode(updateutils.GetpdtmParams(Version)) 232 | 233 | c.url.RawQuery = params.Encode() 234 | 235 | resp, err := c.makeRequest() 236 | if err != nil { 237 | return nil, err 238 | } 239 | 240 | results := []*Response{} 241 | err = json.Unmarshal(resp, &results) 242 | if err != nil { 243 | return nil, err 244 | } 245 | 246 | // insert original input in all responses 247 | for _, result := range results { 248 | result.Input = inputToStore 249 | } 250 | 251 | return results, nil 252 | } 253 | -------------------------------------------------------------------------------- /libs/client_test.go: -------------------------------------------------------------------------------- 1 | package asnmap 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | 6 | "testing" 7 | ) 8 | 9 | func TestGetASNFromIP(t *testing.T) { 10 | client, err := NewClient() 11 | require.Nil(t, err) 12 | 13 | tt := []struct { 14 | name string 15 | ip string 16 | result []*Response 17 | }{ 18 | {"found", "100.19.12.21", []*Response{{FirstIp: "", LastIp: "", Input: "100.19.12.21", ASN: 701, Country: "US", Org: "uunet"}}}, 19 | {"not found", "255.100.100.100", []*Response{}}, 20 | } 21 | 22 | for _, tc := range tt { 23 | t.Run(tc.name, func(t *testing.T) { 24 | i, err := client.GetData(tc.ip) 25 | require.Nil(t, err) 26 | // // Expecting true from comparision 27 | for _, result := range tc.result { 28 | x := compareResponse(i, result) 29 | require.True(t, x) 30 | } 31 | }) 32 | } 33 | } 34 | 35 | func TestGetIPFromASN(t *testing.T) { 36 | client, err := NewClient() 37 | require.Nil(t, err) 38 | 39 | tt := []struct { 40 | name string 41 | asn string 42 | result []*Response 43 | }{ 44 | { 45 | "zero match", "1123", []*Response{}, 46 | }, 47 | { 48 | "single match", "14421", []*Response{{ 49 | FirstIp: "216.101.17.0", 50 | LastIp: "216.101.17.255", 51 | Input: "14421", 52 | ASN: 14421, 53 | Country: "US", 54 | Org: "theravance", 55 | }}, 56 | }, 57 | { 58 | "multi match", "7712", []*Response{ 59 | { 60 | FirstIp: "118.67.200.0", 61 | LastIp: "118.67.202.255", 62 | Input: "7712", 63 | ASN: 7712, 64 | Country: "KH", 65 | Org: "sabay sabay digital cambodia", 66 | }, 67 | { 68 | FirstIp: "118.67.203.0", 69 | LastIp: "118.67.207.255", 70 | Input: "7712", 71 | ASN: 7712, 72 | Country: "KH", 73 | Org: "sabay sabay digital cambodia", 74 | }, 75 | { 76 | FirstIp: "2405:aa00::", 77 | LastIp: "2405:aa00:ffff:ffff:ffff:ffff:ffff:ffff", 78 | Input: "7712", 79 | ASN: 7712, 80 | Country: "KH", 81 | Org: "sabay sabay digital cambodia", 82 | }}, 83 | }, 84 | } 85 | 86 | for _, tc := range tt { 87 | t.Run(tc.name, func(t *testing.T) { 88 | i, err := client.GetData(tc.asn) 89 | require.Nil(t, err) 90 | for _, result := range tc.result { 91 | x := compareResponse(i, result) 92 | require.True(t, x) 93 | } 94 | }) 95 | } 96 | } 97 | 98 | func TestGetASNFromOrg(t *testing.T) { 99 | t.Skip("asnmap-server returns null for this query, skipping") 100 | client, err := NewClient() 101 | require.Nil(t, err) 102 | 103 | tt := []struct { 104 | name string 105 | org string 106 | err bool 107 | expected []*Response 108 | }{ 109 | {"not found", "RANDOM_TEXT", true, []*Response{}}, 110 | // Todo: excluding - ref: https://github.com/projectdiscovery/asnmap-server/issues/43 111 | // {"regex match", "PPLINKNET*", false, []*Response{ 112 | // { 113 | // FirstIp: "45.239.52.0", 114 | // LastIp: "45.239.55.255", 115 | // Input: "PPLINKNET", 116 | // ASN: 268353, 117 | // Country: "BR", 118 | // Org: "PPLINKNET SERVICOS DE COMUNICACAO LTDA - ME"}, 119 | // { 120 | // FirstIp: "2804:4fd8::", 121 | // LastIp: "2804:4fd8:ffff:ffff:ffff:ffff:ffff:ffff", 122 | // Input: "PPLINKNET", 123 | // ASN: 268353, 124 | // Country: "BR", 125 | // Org: "PPLINKNET SERVICOS DE COMUNICACAO LTDA - ME"}, 126 | // }}, 127 | {"exact match", "PPLINKNET", false, []*Response{}}, 128 | } 129 | 130 | for _, tc := range tt { 131 | t.Run(tc.name, func(t *testing.T) { 132 | i, err := client.GetData(tc.org) 133 | if tc.err { 134 | require.NotNil(t, err) 135 | return 136 | } else { 137 | require.Nil(t, err) 138 | } 139 | // // Expecting true from comparision 140 | for _, result := range tc.expected { 141 | x := compareResponse(i, result) 142 | require.True(t, x) 143 | } 144 | }) 145 | } 146 | } 147 | 148 | // compareResponse compares ASN & ORG against given domain with expected output's ASN & ORG 149 | // Have excluded IPs for now as they might change in future. 150 | func compareResponse(respA []*Response, respB *Response) bool { 151 | for _, r := range respA { 152 | if r.Equal(*respB) { 153 | return true 154 | } 155 | } 156 | return false 157 | } 158 | -------------------------------------------------------------------------------- /libs/config.go: -------------------------------------------------------------------------------- 1 | package asnmap 2 | 3 | // Version is the current Version of asnmap 4 | const Version = `v1.1.1` 5 | -------------------------------------------------------------------------------- /libs/default_client.go: -------------------------------------------------------------------------------- 1 | package asnmap 2 | 3 | var DefaultClient *Client 4 | 5 | func init() { 6 | var err error 7 | DefaultClient, err = NewClient() 8 | if err != nil { 9 | // if we can't create the default client it makes sense to panic, as any other attempt will fail 10 | panic(err) 11 | } 12 | } 13 | 14 | func GetData(input string) ([]*Response, error) { 15 | return DefaultClient.GetData(input) 16 | } 17 | -------------------------------------------------------------------------------- /libs/resolver.go: -------------------------------------------------------------------------------- 1 | package asnmap 2 | 3 | import ( 4 | "github.com/projectdiscovery/retryabledns" 5 | ) 6 | 7 | var resolvers = []string{"8.8.8.8:53", "8.8.4.4:53"} 8 | var max_retries = 2 9 | 10 | func ResolveDomain(domain string, customresolvers ...string) ([]string, error) { 11 | // it requires a list of resolvers 12 | if len(customresolvers) == 0 { 13 | customresolvers = resolvers 14 | } 15 | dnsClient, _ := retryabledns.New(customresolvers, max_retries) 16 | var list []string 17 | 18 | ips, err := dnsClient.A(domain) 19 | if err != nil { 20 | return nil, err 21 | } 22 | list = append(list, ips.A...) 23 | 24 | ipA4, err := dnsClient.AAAA(domain) 25 | if err != nil { 26 | return nil, err 27 | } 28 | list = append(list, ipA4.AAAA...) 29 | return list, nil 30 | } 31 | -------------------------------------------------------------------------------- /libs/resolver_test.go: -------------------------------------------------------------------------------- 1 | package asnmap 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/projectdiscovery/gologger" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestResolveDomain(t *testing.T) { 11 | tt := []struct { 12 | name string 13 | domain string 14 | customresolvers []string 15 | expectedOutput []string 16 | }{ 17 | {"Resolve google.com using default resolvers", "google.com", []string{}, []string{"142.250.183.110"}}, 18 | {"Resolve google.com using custom resolvers", "google.com", []string{"8.8.8.8"}, []string{"142.250.199.142"}}, 19 | {"Resolve random domain name using custom resolvers", "somerandomdomainnamethatisfake.com", []string{"8.8.8.8"}, []string{}}, 20 | } 21 | for _, tc := range tt { 22 | t.Run(tc.name, func(t *testing.T) { 23 | i, err := ResolveDomain(tc.domain, tc.customresolvers...) 24 | require.Nil(t, err) 25 | gologger.Info().Msgf("%v resolve to %v", tc.domain, i) 26 | 27 | // If we are unable to resolve the domain, then ResolveDomain() returns an empty list 28 | // So for some unregistered domain, we will get an empty list. 29 | // Here we are not comparing the exact response for domain as IPs might get change in future. 30 | // Instead we are checking whether we are able to resolve domain to some IP or not. 31 | require.Falsef(t, len(i) == 0 && tc.domain != "somerandomdomainnamethatisfake.com", "Failed to resolve domain for test case: %v", tc.name) 32 | }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /libs/types.go: -------------------------------------------------------------------------------- 1 | package asnmap 2 | 3 | import ( 4 | "net" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | "time" 9 | 10 | "github.com/asaskevich/govalidator" 11 | iputil "github.com/projectdiscovery/utils/ip" 12 | stringsutil "github.com/projectdiscovery/utils/strings" 13 | ) 14 | 15 | type Result struct { 16 | Timestamp string `json:"timestamp,omitempty" csv:"timestamp"` 17 | Input string `json:"input" csv:"input"` 18 | ASN string `json:"as_number" csv:"as_number"` 19 | ASN_org string `json:"as_name" csv:"as_name"` 20 | AS_country string `json:"as_country" csv:"as_country"` 21 | AS_range []string `json:"as_range" csv:"as_range"` 22 | } 23 | 24 | // To model http response from server 25 | type Response struct { 26 | FirstIp string `json:"first_ip,omitempty"` 27 | LastIp string `json:"last_ip,omitempty"` 28 | Input string `json:"-"` // added by client 29 | ASN int `json:"asn,omitempty"` 30 | Country string `json:"country,omitempty"` 31 | Org string `json:"org,omitempty"` 32 | } 33 | 34 | func (r Response) Equal(r2 Response) bool { 35 | return r.ASN == r2.ASN && strings.EqualFold(r.Org, r2.Org) 36 | } 37 | 38 | type InputType uint8 39 | 40 | const ( 41 | ASN InputType = iota 42 | ASNID 43 | IP 44 | Org 45 | Domain 46 | Unknown 47 | ) 48 | 49 | var domainRegex = regexp.MustCompile(`^(?i)[a-z0-9-_]+(\.[a-z0-9-]+)+\.?$`) 50 | 51 | func MapToResults(output []*Response) ([]*Result, error) { 52 | results := make([]*Result, 0, len(output)) 53 | for _, res := range output { 54 | result, err := mapToResult(res) 55 | if err != nil { 56 | return nil, err 57 | } 58 | results = append(results, result) 59 | } 60 | return results, nil 61 | } 62 | 63 | func mapToResult(resp *Response) (*Result, error) { 64 | result := &Result{} 65 | result.Timestamp = time.Now().Local().String() 66 | result.Input = attachPrefix(resp.Input) 67 | result.ASN = attachPrefix(strconv.Itoa(resp.ASN)) 68 | result.ASN_org = resp.Org 69 | result.AS_country = resp.Country 70 | cidrs, err := GetCIDR([]*Response{resp}) 71 | if err != nil { 72 | return nil, err 73 | } 74 | result.AS_range = convertIPsToStringSlice(cidrs) 75 | return result, nil 76 | } 77 | 78 | // attachPrefix func attaches 'AS' prefix to ASN numbers 79 | func attachPrefix(input string) string { 80 | inp := input 81 | if _, err := strconv.Atoi(input); err == nil { 82 | inp = "AS" + input 83 | } 84 | return inp 85 | } 86 | 87 | func convertIPsToStringSlice(ips []*net.IPNet) []string { 88 | var res []string 89 | for _, ip := range ips { 90 | res = append(res, ip.String()) 91 | } 92 | return res 93 | } 94 | 95 | // checkIfASN checks if the given input is ASN or not, 96 | // its possible to have an org name starting with AS/as prefix. 97 | func checkIfASN(input string) bool { 98 | if len(input) == 0 { 99 | return false 100 | } 101 | hasASNPrefix := stringsutil.HasPrefixI(input, "AS") 102 | if hasASNPrefix { 103 | input = input[2:] 104 | } 105 | return hasASNPrefix && checkIfASNId(input) 106 | } 107 | 108 | func checkIfASNId(input string) bool { 109 | if len(input) == 0 { 110 | return false 111 | } 112 | hasNumericId := input != "" && govalidator.IsNumeric(input) 113 | return hasNumericId 114 | } 115 | 116 | func IdentifyInput(input string) InputType { 117 | switch { 118 | case iputil.IsIP(input): 119 | return IP 120 | case checkIfASN(input): 121 | return ASN 122 | case checkIfASNId(input): 123 | return ASNID 124 | case domainRegex.MatchString(input): 125 | return Domain 126 | default: 127 | return Org 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /libs/types_test.go: -------------------------------------------------------------------------------- 1 | package asnmap 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestIdentifyInput(t *testing.T) { 10 | tt := []struct { 11 | name string 12 | input string 13 | expectedOutput InputType 14 | }{ 15 | {"IP", "10.101.101.10", IP}, 16 | {"ASN", "AS14421", ASN}, 17 | {"Org", "PPLINKNET", Org}, 18 | {"Org", "AS", Org}, 19 | {"Org", "AS-CHOOPA", Org}, 20 | {"Top level domain", "google.com", Domain}, 21 | {"Country level domain", "bbc.co.uk", Domain}, 22 | {"Second level domain", "cornell.edu", Domain}, 23 | {"Third level domain", "bigstuff.cornell.edu", Domain}, 24 | {"Fourth level domain", "www.bass.blm.gov", Domain}, 25 | {"Domain with number", "www.99acres.com", Domain}, 26 | } 27 | 28 | for _, tc := range tt { 29 | t.Run(tc.name, func(t *testing.T) { 30 | res := IdentifyInput(tc.input) 31 | require.Equal(t, res, tc.expectedOutput) 32 | }) 33 | } 34 | } 35 | 36 | func TestMapToResults(t *testing.T) { 37 | tt := []struct { 38 | name string 39 | inputResponse []*Response 40 | expectedOutput *Result 41 | }{ 42 | { 43 | name: "ASN", 44 | inputResponse: []*Response{ 45 | { 46 | FirstIp: "216.101.17.0", 47 | LastIp: "216.101.17.255", 48 | Input: "AS14421", 49 | ASN: 14421, 50 | Country: "US", 51 | Org: "THERAVANCE"}, 52 | }, 53 | expectedOutput: &Result{ 54 | Timestamp: "", 55 | Input: "AS14421", 56 | ASN: "AS14421", 57 | ASN_org: "THERAVANCE", 58 | AS_country: "US", 59 | AS_range: []string{"216.101.17.0/24"}, 60 | }, 61 | }, 62 | } 63 | for _, tc := range tt { 64 | t.Run(tc.name, func(t *testing.T) { 65 | output, err := MapToResults(tc.inputResponse) 66 | require.Nil(t, err) 67 | 68 | if len(output) == 0 { 69 | t.Fatalf("Expected at least one result, got none.") 70 | } 71 | 72 | // Ignoring timestamp from acutal output 73 | output[0].Timestamp = "" 74 | require.Equal(t, tc.expectedOutput, output[0]) 75 | }) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /runner/banner.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | asnmap "github.com/projectdiscovery/asnmap/libs" 5 | "github.com/projectdiscovery/gologger" 6 | "github.com/projectdiscovery/utils/auth/pdcp" 7 | updateutils "github.com/projectdiscovery/utils/update" 8 | ) 9 | 10 | const banner = ` 11 | ___ _____ __ 12 | / _ | / __/ |/ /_ _ ___ ____ 13 | / __ |_\ \/ / ' \/ _ / _ \ 14 | /_/ |_/___/_/|_/_/_/_/\_,_/ .__/ 15 | /_/ 16 | ` 17 | 18 | // showBanner is used to show the banner to the user 19 | func showBanner() { 20 | gologger.Print().Msgf("%s\n", banner) 21 | gologger.Print().Msgf("\t\tprojectdiscovery.io\n\n") 22 | } 23 | 24 | // GetUpdateCallback returns a callback function that updates asnmap 25 | func GetUpdateCallback() func() { 26 | return func() { 27 | showBanner() 28 | updateutils.GetUpdateToolCallback("asnmap", asnmap.Version)() 29 | } 30 | } 31 | 32 | // AuthWithPDCP is used to authenticate with PDCP 33 | func AuthWithPDCP() { 34 | showBanner() 35 | pdcp.CheckNValidateCredentials("asnmap") 36 | } 37 | -------------------------------------------------------------------------------- /runner/options.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "os" 7 | "strings" 8 | 9 | asnmap "github.com/projectdiscovery/asnmap/libs" 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 | type OnResultCallback func([]*asnmap.Response) 20 | 21 | var cfgFile string 22 | 23 | type Options struct { 24 | FileInput goflags.StringSlice 25 | Resolvers goflags.StringSlice 26 | Asn goflags.StringSlice 27 | Domain goflags.StringSlice 28 | Ip goflags.StringSlice 29 | Org goflags.StringSlice 30 | Proxy goflags.StringSlice 31 | OutputFile string 32 | PdcpAuth string 33 | Output io.Writer 34 | DisplayInJSON bool 35 | DisplayInCSV bool 36 | Silent bool 37 | Verbose bool 38 | Version bool 39 | DisplayIPv6 bool 40 | OnResult OnResultCallback 41 | DisableUpdateCheck bool 42 | } 43 | 44 | // configureOutput configures the output on the screen 45 | func (options *Options) configureOutput() { 46 | if options.Silent { 47 | gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent) 48 | } else if options.Verbose { 49 | gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose) 50 | } 51 | } 52 | 53 | // validateOptions validates different command line option combinations 54 | func (options *Options) validateOptions() error { 55 | if options.Verbose && options.Silent { 56 | return errors.New("verbose and silent can't be used together") 57 | } 58 | 59 | if options.Asn == nil && options.Ip == nil && options.Org == nil && options.Domain == nil && !fileutil.HasStdin() && cfgFile == "" && options.FileInput == nil { 60 | return errors.New("no input defined") 61 | } 62 | 63 | if options.Asn != nil && (options.Ip != nil || options.Org != nil || options.Domain != nil) { 64 | return errors.New("asn and other options like ip, org and domain can't be used together as input to get data") 65 | } else if options.Ip != nil && (options.Asn != nil || options.Org != nil || options.Domain != nil) { 66 | return errors.New("ip and other options like asn, org and domain can't be used together as input to get data") 67 | } else if options.Org != nil && (options.Asn != nil || options.Ip != nil || options.Domain != nil) { 68 | return errors.New("org and other options like asn, ip and domain can't be used together as input to get data") 69 | } else if options.Domain != nil && (options.Asn != nil || options.Ip != nil || options.Org != nil) { 70 | return errors.New("domain and other options like asn, ip and org can't be used together as input to get data") 71 | } 72 | 73 | if options.DisplayInJSON && options.DisplayInCSV { 74 | return errors.New("can either display in json or csv") 75 | } 76 | 77 | // validate asn input 78 | if options.Asn != nil { 79 | for _, asn := range options.Asn { 80 | if !strings.HasPrefix(strings.ToUpper(asn), "AS") { 81 | return errors.New("invalid ASN given. it should start with prefix 'AS', example : AS14421") 82 | } 83 | } 84 | } 85 | return nil 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(`Go CLI and Library for quickly mapping organization network ranges using ASN information.`) 93 | 94 | // Input 95 | flagSet.CreateGroup("input", "Input", 96 | flagSet.StringSliceVarP(&options.Asn, "asn", "a", nil, "target asn to lookup, example: -a AS5650", goflags.FileNormalizedStringSliceOptions), 97 | flagSet.StringSliceVarP(&options.Ip, "ip", "i", nil, "target ip to lookup, example: -i 100.19.12.21, -i 2a10:ad40:: ", goflags.FileNormalizedStringSliceOptions), 98 | flagSet.StringSliceVarP(&options.Domain, "domain", "d", nil, "target domain to lookup, example: -d google.com, -d facebook.com", goflags.FileNormalizedStringSliceOptions), 99 | flagSet.StringSliceVar(&options.Org, "org", nil, "target organization to lookup, example: -org GOOGLE", goflags.StringSliceOptions), 100 | flagSet.StringSliceVarP(&options.FileInput, "file", "f", nil, "targets to lookup from file", goflags.FileCommaSeparatedStringSliceOptions), 101 | ) 102 | 103 | // Configs 104 | flagSet.CreateGroup("configs", "Configurations", 105 | flagSet.DynamicVar(&options.PdcpAuth, "auth", "true", "configure ProjectDiscovery Cloud Platform (PDCP) api key"), 106 | flagSet.StringVar(&cfgFile, "config", "", "path to the asnmap configuration file"), 107 | flagSet.StringSliceVarP(&options.Resolvers, "resolvers", "r", nil, "list of resolvers to use", goflags.FileCommaSeparatedStringSliceOptions), 108 | flagSet.StringSliceVarP(&options.Proxy, "proxy", "p", nil, "list of proxy to use (comma separated or file input)", goflags.FileCommaSeparatedStringSliceOptions), 109 | ) 110 | 111 | // Update 112 | flagSet.CreateGroup("update", "Update", 113 | flagSet.CallbackVarP(GetUpdateCallback(), "update", "up", "update asnmap to latest version"), 114 | flagSet.BoolVarP(&options.DisableUpdateCheck, "disable-update-check", "duc", false, "disable automatic asnmap update check"), 115 | ) 116 | 117 | // Output 118 | flagSet.CreateGroup("output", "Output", 119 | flagSet.StringVarP(&options.OutputFile, "output", "o", "", "file to write output to"), 120 | flagSet.BoolVarP(&options.DisplayInJSON, "json", "j", false, "display json format output"), 121 | flagSet.BoolVarP(&options.DisplayInCSV, "csv", "c", false, "display csv format output"), 122 | flagSet.BoolVar(&options.DisplayIPv6, "v6", false, "display ipv6 cidr ranges in cli output"), 123 | flagSet.BoolVarP(&options.Verbose, "verbose", "v", false, "display verbose output"), 124 | flagSet.BoolVar(&options.Silent, "silent", false, "display silent output"), 125 | flagSet.BoolVar(&options.Version, "version", false, "show version of the project"), 126 | ) 127 | 128 | if err := flagSet.Parse(); err != nil { 129 | gologger.Fatal().Msgf("%s\n", err) 130 | } 131 | 132 | // api key hierarchy: cli flag > env var > .pdcp/credential file 133 | if options.PdcpAuth == "true" { 134 | AuthWithPDCP() 135 | } else if len(options.PdcpAuth) == 36 { 136 | asnmap.PDCPApiKey = options.PdcpAuth 137 | ph := pdcp.PDCPCredHandler{} 138 | if _, err := ph.GetCreds(); err == pdcp.ErrNoCreds { 139 | apiServer := env.GetEnvOrDefault("PDCP_API_SERVER", pdcp.DefaultApiServer) 140 | if validatedCreds, err := ph.ValidateAPIKey(asnmap.PDCPApiKey, apiServer, "asnmap"); err == nil { 141 | _ = ph.SaveCreds(validatedCreds) 142 | } 143 | } 144 | } 145 | 146 | // Read the inputs and configure the logging 147 | options.configureOutput() 148 | 149 | if options.OutputFile == "" { 150 | options.Output = os.Stdout 151 | } 152 | 153 | if cfgFile != "" { 154 | if err := flagSet.MergeConfigFile(cfgFile); err != nil { 155 | gologger.Fatal().Msgf("Could not read config file.") 156 | } 157 | } 158 | 159 | if options.Version { 160 | gologger.Info().Msgf("Current Version: %s\n", asnmap.Version) 161 | os.Exit(0) 162 | } 163 | 164 | showBanner() 165 | 166 | if !options.DisableUpdateCheck { 167 | latestVersion, err := updateutils.GetToolVersionCallback("asnmap", asnmap.Version)() 168 | if err != nil { 169 | if options.Verbose { 170 | gologger.Error().Msgf("asnmap version check failed: %v", err.Error()) 171 | } 172 | } else { 173 | gologger.Info().Msgf("Current asnmap version %v %v", asnmap.Version, updateutils.GetVersionDescription(asnmap.Version, latestVersion)) 174 | } 175 | } 176 | 177 | if err := options.validateOptions(); err != nil { 178 | gologger.Fatal().Msgf("%s\n", err) 179 | } 180 | 181 | return options 182 | } 183 | -------------------------------------------------------------------------------- /runner/output.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "bytes" 5 | "encoding/csv" 6 | "encoding/json" 7 | "fmt" 8 | "io" 9 | "net" 10 | "strings" 11 | 12 | asnmap "github.com/projectdiscovery/asnmap/libs" 13 | iputil "github.com/projectdiscovery/utils/ip" 14 | ) 15 | 16 | var csvHeaders = [][]string{{"timestamp", "input", "as_number", "as_name", "as_country", "as_range"}} 17 | 18 | func (r *Runner) writeToCsv(records [][]string) error { 19 | w := csv.NewWriter(r.options.Output) 20 | w.Comma = '|' 21 | 22 | for _, record := range records { 23 | if err := w.Write(record); err != nil { 24 | return err 25 | } 26 | } 27 | 28 | w.Flush() 29 | 30 | return w.Error() 31 | } 32 | 33 | func (r *Runner) writeToJson(results []*asnmap.Result) error { 34 | for _, result := range results { 35 | record, err := json.Marshal(result) 36 | if err != nil { 37 | return err 38 | } 39 | record = append(record, '\n') 40 | if _, err := io.Copy(r.options.Output, bytes.NewReader(record)); err != nil { 41 | return err 42 | } 43 | } 44 | return nil 45 | } 46 | 47 | // filterIPv6 48 | // - DisplayIPv6==true => returns IPv6 + IPv4 49 | // - DisplayIPv6==false => returns IPv4 50 | func (r *Runner) filterIPv6(ipsnet []*net.IPNet) []*net.IPNet { 51 | if r.options.DisplayIPv6 { 52 | // ipv4 + ipv6 53 | return ipsnet 54 | } 55 | 56 | // only ipv4 57 | var filteredIpsNet []*net.IPNet 58 | for _, ipnet := range ipsnet { 59 | value := ipnet.String() 60 | // trim net suffix 61 | if idx := strings.Index(value, "/"); idx >= 0 { 62 | value = value[:idx] 63 | } 64 | if iputil.IsIPv4(value) { 65 | filteredIpsNet = append(filteredIpsNet, ipnet) 66 | } 67 | } 68 | return filteredIpsNet 69 | } 70 | 71 | // writeOutput either to file or to stdout 72 | func (r *Runner) writeOutput(output []*asnmap.Response) error { 73 | if r.options.OnResult != nil { 74 | r.options.OnResult(output) 75 | } 76 | // empty output is ignored 77 | if r.options.Output == nil { 78 | return nil 79 | } 80 | switch { 81 | case r.options.DisplayInJSON: 82 | results, err := asnmap.MapToResults(output) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | return r.writeToJson(results) 88 | case r.options.DisplayInCSV: 89 | results, err := asnmap.MapToResults(output) 90 | if err != nil { 91 | return err 92 | } 93 | records := [][]string{} 94 | for _, result := range results { 95 | record := []string{result.Timestamp, result.Input, result.ASN, result.ASN_org, result.AS_country, strings.Join(result.AS_range, ",")} 96 | records = append(records, record) 97 | } 98 | return r.writeToCsv(records) 99 | default: 100 | cidrs, err := asnmap.GetCIDR(output) 101 | if err != nil { 102 | return err 103 | } 104 | result := r.filterIPv6(cidrs) 105 | for _, cidr := range result { 106 | _, err := fmt.Fprintf(r.options.Output, "%v\n", cidr) 107 | if err != nil { 108 | return err 109 | } 110 | } 111 | return nil 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /runner/runner.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "bufio" 5 | "encoding/csv" 6 | "fmt" 7 | "io" 8 | "os" 9 | "strings" 10 | 11 | asnmap "github.com/projectdiscovery/asnmap/libs" 12 | "github.com/projectdiscovery/gologger" 13 | "github.com/projectdiscovery/hmap/store/hybrid" 14 | fileutil "github.com/projectdiscovery/utils/file" 15 | sliceutil "github.com/projectdiscovery/utils/slice" 16 | ) 17 | 18 | type Runner struct { 19 | options *Options 20 | hm *hybrid.HybridMap 21 | client *asnmap.Client 22 | } 23 | 24 | func New(options *Options) (*Runner, error) { 25 | client, err := asnmap.NewClient() 26 | if err != nil { 27 | return nil, err 28 | } 29 | return &Runner{options: options, client: client}, nil 30 | } 31 | 32 | func (r *Runner) Close() error { 33 | if r.hm != nil { 34 | err := r.hm.Close() 35 | if err != nil { 36 | return err 37 | } 38 | } 39 | 40 | return nil 41 | } 42 | 43 | func (r *Runner) Run() error { 44 | if len(r.options.Proxy) > 0 { 45 | if proxyURL, err := r.client.SetProxy(r.options.Proxy); err != nil { 46 | return fmt.Errorf("could not set proxy: %s", err) 47 | } else { 48 | gologger.Info().Msgf("Using %s proxy %s", proxyURL.Scheme, proxyURL.String()) 49 | } 50 | } 51 | 52 | var outputWriters []io.Writer 53 | if r.options.OutputFile != "" { 54 | file, err := os.Create(r.options.OutputFile) 55 | if err != nil { 56 | return err 57 | } 58 | outputWriters = append(outputWriters, file) 59 | } 60 | 61 | outputWriters = append(outputWriters, os.Stdout) 62 | r.options.Output = io.MultiWriter(outputWriters...) 63 | 64 | if r.options.DisplayInCSV { 65 | w := csv.NewWriter(r.options.Output) 66 | w.Comma = '|' 67 | 68 | for _, record := range csvHeaders { 69 | if err := w.Write(record); err != nil { 70 | return err 71 | } 72 | } 73 | w.Flush() 74 | } 75 | 76 | if err := r.prepareInput(); err != nil { 77 | return err 78 | } 79 | 80 | return r.process() 81 | } 82 | 83 | // Process Function makes request to client returns response 84 | func (r *Runner) process() error { 85 | var errProcess error 86 | r.hm.Scan(func(key, _ []byte) error { 87 | item := string(key) 88 | switch asnmap.IdentifyInput(item) { 89 | case asnmap.Domain: 90 | resolvedIps, err := asnmap.ResolveDomain(item, r.options.Resolvers...) 91 | if err != nil { 92 | gologger.Verbose().Msgf("could not resolve '%s': %v", item, err) 93 | return nil 94 | } 95 | 96 | if len(resolvedIps) == 0 { 97 | gologger.Verbose().Msgf("No records found for %v", item) 98 | return nil 99 | } 100 | 101 | var responses []asnmap.Response 102 | for _, resolvedIp := range resolvedIps { 103 | ls, err := r.client.GetDataWithCustomInput(resolvedIp, item) 104 | if err != nil { 105 | errProcess = err 106 | break 107 | } 108 | 109 | for _, l := range ls { 110 | if !sliceutil.Contains(responses, *l) { 111 | responses = append(responses, *l) 112 | } 113 | } 114 | } 115 | 116 | for _, response := range responses { 117 | if err := r.writeOutput([]*asnmap.Response{&response}); err != nil { 118 | errProcess = err 119 | return err 120 | } 121 | } 122 | 123 | default: 124 | ls, err := r.client.GetData(item) 125 | if err != nil { 126 | errProcess = err 127 | return err 128 | } 129 | if len(ls) == 0 { 130 | gologger.Verbose().Msgf("No records found for %v", item) 131 | return nil 132 | } 133 | if err := r.writeOutput(ls); err != nil { 134 | errProcess = err 135 | return err 136 | } 137 | } 138 | 139 | return nil 140 | }) 141 | return errProcess 142 | } 143 | 144 | func (r *Runner) setItem(v string) { 145 | item := strings.TrimSpace(v) 146 | if item != "" { 147 | _ = r.hm.Set(item, nil) 148 | } 149 | } 150 | 151 | func (r *Runner) prepareInput() error { 152 | var err error 153 | r.hm, err = hybrid.New(hybrid.DefaultDiskOptions) 154 | if err != nil { 155 | return err 156 | } 157 | if fileutil.HasStdin() { 158 | scanner := bufio.NewScanner(os.Stdin) 159 | for scanner.Scan() { 160 | r.setItem(scanner.Text()) 161 | } 162 | } 163 | 164 | for _, item := range r.options.FileInput { 165 | r.setItem(item) 166 | } 167 | 168 | for _, item := range r.options.Asn { 169 | r.setItem(item) 170 | } 171 | 172 | for _, item := range r.options.Ip { 173 | r.setItem(item) 174 | } 175 | 176 | for _, item := range r.options.Domain { 177 | r.setItem(item) 178 | } 179 | 180 | for _, item := range r.options.Org { 181 | r.setItem(item) 182 | } 183 | 184 | return nil 185 | } 186 | -------------------------------------------------------------------------------- /runner/runner_test.go: -------------------------------------------------------------------------------- 1 | package runner 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | asnmap "github.com/projectdiscovery/asnmap/libs" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestRunner(t *testing.T) { 13 | tests := []struct { 14 | name string 15 | options *Options 16 | expectedOutput []*asnmap.Response 17 | }{ 18 | { 19 | name: "IP", 20 | options: &Options{ 21 | Ip: []string{"104.16.99.52"}, 22 | }, 23 | expectedOutput: []*asnmap.Response{ 24 | { 25 | FirstIp: "104.16.0.0", 26 | LastIp: "104.22.79.255", 27 | Input: "104.16.99.52", 28 | ASN: 13335, 29 | Country: "US", 30 | Org: "cloudflarenet"}, 31 | }, 32 | }, 33 | { 34 | name: "ASN", 35 | options: &Options{ 36 | Asn: []string{"AS14421"}, 37 | }, 38 | expectedOutput: []*asnmap.Response{ 39 | { 40 | FirstIp: "216.101.17.0", 41 | LastIp: "216.101.17.255", 42 | Input: "14421", 43 | ASN: 14421, 44 | Country: "US", 45 | Org: "theravance"}, 46 | }, 47 | }, 48 | { 49 | name: "Org", 50 | options: &Options{ 51 | Org: []string{"PPLINK"}, 52 | }, 53 | expectedOutput: []*asnmap.Response{ 54 | { 55 | FirstIp: "45.239.52.0", 56 | LastIp: "45.239.55.255", 57 | Input: "PPLINK", 58 | ASN: 268353, 59 | Country: "BR", 60 | Org: "PPLINKNET SERVICOS DE COMUNICACAO LTDA - ME"}, 61 | { 62 | FirstIp: "2804:4fd8::", 63 | LastIp: "2804:4fd8:ffff:ffff:ffff:ffff:ffff:ffff", 64 | Input: "PPLINK", 65 | ASN: 268353, 66 | Country: "BR", 67 | Org: "PPLINKNET SERVICOS DE COMUNICACAO LTDA - ME"}, 68 | }, 69 | }, 70 | } 71 | for _, tt := range tests { 72 | t.Run(tt.name, func(t *testing.T) { 73 | fmt.Println(tt.name) 74 | tt.options.OnResult = func(o []*asnmap.Response) { 75 | require.Equal(t, tt.expectedOutput, o) 76 | } 77 | r, err := New(tt.options) 78 | require.Nil(t, err) 79 | 80 | err = r.prepareInput() 81 | require.Nil(t, err) 82 | 83 | err = r.process() 84 | require.Nil(t, err) 85 | 86 | err = r.Close() 87 | require.Nil(t, err) 88 | }) 89 | } 90 | } 91 | 92 | func TestProcessForDomainInput(t *testing.T) { 93 | tests := []struct { 94 | name string 95 | inputchan chan interface{} 96 | outputchan chan []*asnmap.Response 97 | options *Options 98 | expectedOutput *asnmap.Response 99 | }{ 100 | { 101 | name: "Domain", 102 | inputchan: make(chan interface{}), 103 | outputchan: make(chan []*asnmap.Response), 104 | options: &Options{ 105 | Domain: []string{"google.com"}, 106 | }, 107 | expectedOutput: &asnmap.Response{ 108 | FirstIp: "142.250.0.0", 109 | LastIp: "142.250.82.255", 110 | Input: "google.com", 111 | ASN: 15169, 112 | Country: "US", 113 | Org: "google", 114 | }, 115 | }, 116 | } 117 | 118 | for _, tt := range tests { 119 | t.Run(tt.name, func(t *testing.T) { 120 | tt.options.OnResult = func(o []*asnmap.Response) { 121 | x := compareResponse(o, tt.expectedOutput) 122 | // // Expecting true from comparision 123 | require.True(t, x) 124 | } 125 | 126 | r, err := New(tt.options) 127 | require.Nil(t, err) 128 | 129 | err = r.prepareInput() 130 | require.Nil(t, err) 131 | 132 | err = r.process() 133 | require.Nil(t, err) 134 | 135 | err = r.Close() 136 | require.Nil(t, err) 137 | }) 138 | } 139 | } 140 | 141 | // compareResponse compares ASN & ORG against given domain with expected output's ASN & ORG 142 | // Have excluded IPs for now as they might change in future. 143 | func compareResponse(respA []*asnmap.Response, respB *asnmap.Response) bool { 144 | for _, r := range respA { 145 | if r.Equal(*respB) { 146 | return true 147 | } 148 | } 149 | 150 | return false 151 | } 152 | --------------------------------------------------------------------------------