├── .all-contributorsrc
├── .github
├── dependabot.yml
└── workflows
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .goreleaser.yaml
├── .octocov.yml
├── LICENSE
├── README.md
├── cmd
└── root.go
├── go.mod
├── go.sum
├── logo.png
├── main.go
└── whris
├── whris.go
└── whris_test.go
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "dim13",
10 | "name": "Dimitri Sokolyuk",
11 | "avatar_url": "https://avatars.githubusercontent.com/u/4006042?v=4",
12 | "profile": "https://www.dim13.org",
13 | "contributions": [
14 | "ideas"
15 | ]
16 | },
17 | {
18 | "login": "mattn",
19 | "name": "mattn",
20 | "avatar_url": "https://avatars.githubusercontent.com/u/10111?v=4",
21 | "profile": "https://mattn.kaoriya.net/",
22 | "contributions": [
23 | "ideas"
24 | ]
25 | },
26 | {
27 | "login": "Code-Hex",
28 | "name": "Kei Kamikawa",
29 | "avatar_url": "https://avatars.githubusercontent.com/u/6500104?v=4",
30 | "profile": "https://codehex.dev",
31 | "contributions": [
32 | "code"
33 | ]
34 | },
35 | {
36 | "login": "koyashiro",
37 | "name": "koyashiro",
38 | "avatar_url": "https://avatars.githubusercontent.com/u/6698252?v=4",
39 | "profile": "https://koyashi.ro",
40 | "contributions": [
41 | "doc"
42 | ]
43 | },
44 | {
45 | "login": "noborus",
46 | "name": "Noboru Saito",
47 | "avatar_url": "https://avatars.githubusercontent.com/u/2296563?v=4",
48 | "profile": "https://noborus.github.io/",
49 | "contributions": [
50 | "code"
51 | ]
52 | },
53 | {
54 | "login": "Ben131-Go",
55 | "name": "Ben131-Go",
56 | "avatar_url": "https://avatars.githubusercontent.com/u/123849733?v=4",
57 | "profile": "https://github.com/Ben131-Go",
58 | "contributions": [
59 | "bug"
60 | ]
61 | }
62 | ],
63 | "contributorsPerLine": 7,
64 | "projectName": "whris",
65 | "projectOwner": "harakeishi",
66 | "repoType": "github",
67 | "repoHost": "https://github.com",
68 | "skipCi": true,
69 | "commitConvention": "angular"
70 | }
71 |
--------------------------------------------------------------------------------
/.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://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "gomod" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: goreleaser
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | goreleaser:
10 | runs-on: ubuntu-latest
11 | steps:
12 | -
13 | name: Checkout
14 | uses: actions/checkout@v2
15 | with:
16 | fetch-depth: 0
17 | -
18 | name: Set up Go
19 | uses: actions/setup-go@v2
20 | with:
21 | go-version: 1.16
22 | -
23 | name: Create tag
24 | id: create-tag
25 | run: |
26 | echo 'deb [trusted=yes] https://apt.fury.io/caarlos0/ /' | sudo tee /etc/apt/sources.list.d/caarlos0.list
27 | sudo apt update
28 | sudo apt install svu
29 | echo "::set-output name=Version::$(svu p)"
30 | git tag "$(svu p)"
31 | git push --tags
32 | -
33 | name: Run GoReleaser
34 | uses: goreleaser/goreleaser-action@v3
35 | with:
36 | distribution: goreleaser
37 | version: latest
38 | args: release --rm-dist
39 | env:
40 | GITHUB_TOKEN: ${{ secrets.TOKEN }}
41 | Version: ${{ steps.create-tag.outputs.Version }}
42 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | jobs:
9 | golang-test:
10 | strategy:
11 | matrix:
12 | go-version: [1.17.x]
13 | os: [ubuntu-latest]
14 | name: lint
15 | runs-on: ${{ matrix.os }}
16 | steps:
17 | - uses: actions/checkout@v2
18 | - name: golangci-lint
19 | uses: golangci/golangci-lint-action@v2
20 | with:
21 | version: latest
22 | - name: testing
23 | run: go test ./... -coverprofile=coverage.out
24 | - name: create report
25 | uses: k1LoW/octocov-action@v0
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | dist/
3 |
--------------------------------------------------------------------------------
/.goreleaser.yaml:
--------------------------------------------------------------------------------
1 | # This is an example .goreleaser.yml file with some sensible defaults.
2 | # Make sure to check the documentation at https://goreleaser.com
3 | before:
4 | hooks:
5 | # You may remove this if you don't use go modules.
6 | - go mod tidy
7 | builds:
8 | - env:
9 | - CGO_ENABLED=0
10 | goos:
11 | - linux
12 | - windows
13 | - darwin
14 | ignore:
15 | - goos: windows
16 | goarch: arm64
17 | ldflags:
18 | - -s -w -X github.com/harakeishi/curver.Version={{.Version}}
19 | archives:
20 | - replacements:
21 | darwin: Darwin
22 | linux: Linux
23 | windows: Windows
24 | 386: i386
25 | amd64: x86_64
26 | changelog:
27 | sort: asc
28 | filters:
29 | exclude:
30 | - '^docs:'
31 | - '^test:'
32 |
--------------------------------------------------------------------------------
/.octocov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | paths:
3 | - coverage.out
4 | codeToTestRatio:
5 | code:
6 | - '**/*.go'
7 | - '!**/*_test.go'
8 | test:
9 | - '**/*_test.go'
10 | testExecutionTime:
11 | if: true
12 | comment:
13 | if: is_pull_request
14 | report:
15 | if: is_default_branch
16 | datastores:
17 | - artifact://${GITHUB_REPOSITORY}
18 | diff:
19 | datastores:
20 | - artifact://${GITHUB_REPOSITORY}
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2022 harakeishi.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](#contributors-)
4 |
5 | [](https://github.com/harakeishi/whris/releases) [](https://goreportcard.com/report/github.com/harakeishi/whris) [](https://github.com/harakeishi/whris/actions/workflows/test.yml)
6 |
7 | "whris" is Displays management information for IPs associated with the domain.
8 |
9 |
10 | # Table of Contents
11 | - [Table of Contents](#table-of-contents)
12 | - [DEMO](#demo)
13 | - [Installation](#installation)
14 | - [Usage](#usage)
15 | - [License](#license)
16 | - [Contributing](#contributing)
17 | # DEMO
18 |
19 | ```bash
20 | $ whris example.com
21 | Target domain:example.com
22 | Target ip :93.184.216.34
23 |
24 | Network Admin:NETBLK-03-EU-93-184-216-0-24
25 | Network name :EDGECAST-NETBLK-03
26 | ip range :93.184.216.0 - 93.184.216.255
27 | country :EU
28 | ```
29 |
30 | ```bash
31 | $ whris yaserarenai.com
32 | Target domain:yaserarenai.com
33 | Target ip :163.44.185.212
34 |
35 | Network Admin:GMO Pepabo, Inc.
36 | Network name :LOLIPOP
37 | ip range :163.44.185.0 - 163.44.185.255
38 | country :JP
39 | ```
40 | # Installation
41 |
42 | ```bash
43 | $ go install github.com/harakeishi/whris@latest
44 | ```
45 |
46 | # Usage
47 |
48 | If you want to know the administrator of the IP associated with the domain, type `whris [target]`.
49 |
50 | You will then see the target domain and IP, and the administrator information for that IP (administrator name, network name, range of IPs to be managed, and country).
51 |
52 | ```bash
53 | $ whris yaserarenai.com
54 | Target domain:yaserarenai.com
55 | Target ip :163.44.185.212
56 |
57 | Network Admin:GMO Pepabo, Inc.
58 | Network name :LOLIPOP
59 | ip range :163.44.185.0 - 163.44.185.255
60 | country :JP
61 | ```
62 |
63 | If you want to see more details, use the `-v` option.
64 |
65 | Then you will be able to see the higher level network administrator information.
66 |
67 | ```bash
68 | $ whris yaserarenai.com -v
69 | Target domain:yaserarenai.com
70 | Target ip :163.44.185.212
71 |
72 | Network Admin:GMO Pepabo, Inc.
73 | Network name :LOLIPOP
74 | ip range :163.44.185.0 - 163.44.185.255
75 | country :JP
76 |
77 | =========Network Details=========
78 | 0:
79 | Network Admin:Administered by APNIC
80 | Network name :
81 | ip range :163.0.0.0 - 163.255.255.255
82 | country :
83 | 1:
84 | Network Admin:GMO Internet, Inc.
85 | Network name :interQ
86 | ip range :163.44.64.0 - 163.44.191.255
87 | country :JP
88 | 2:
89 | Network Admin:GMO Pepabo, Inc.
90 | Network name :LOLIPOP
91 | ip range :163.44.185.0 - 163.44.185.255
92 | country :JP
93 | ```
94 | # License
95 | [MIT](LICENSE)
96 |
97 | This software includes the work that is distributed in the Apache License 2.0.
98 |
99 | このソフトウェアは、 Apache 2.0ライセンスで配布されている製作物が含まれています。
100 |
101 | ## Contributors ✨
102 |
103 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
104 |
105 |
106 |
107 |
108 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
127 |
--------------------------------------------------------------------------------
/cmd/root.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | curver "github.com/harakeishi/curver"
8 | whris "github.com/harakeishi/whris/whris"
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | var rootCmd = &cobra.Command{
13 | Use: "whris",
14 | Args: cobra.MinimumNArgs(1),
15 | Short: "`whris` is Displays management information for IPs associated with the domain.",
16 | Long: `"whris" outputs the target domain and IP from the input domain, as well as the administrator information for that IP (administrator name, network name, range of IPs to be managed, and country name).`,
17 | RunE: func(cmd *cobra.Command, args []string) error {
18 | version, err := cmd.Flags().GetBool("version")
19 | if err != nil {
20 | return err
21 | }
22 | if version {
23 | curver.EchoVersion()
24 | return nil
25 | }
26 |
27 | v, err := cmd.Flags().GetBool("verbose")
28 | if err != nil {
29 | return err
30 | }
31 |
32 | for n, domain := range args {
33 | if n > 0 {
34 | fmt.Println()
35 | }
36 | if _, err := whris.Resolve(domain, v); err != nil {
37 | return err
38 | }
39 | }
40 | return nil
41 | },
42 | }
43 |
44 | func Execute() {
45 | err := rootCmd.Execute()
46 | if err != nil {
47 | os.Exit(1)
48 | }
49 | }
50 |
51 | func init() {
52 | rootCmd.Flags().BoolP("verbose", "v", false, "verbose output")
53 | rootCmd.Flags().BoolP("version", "V", false, "echo version")
54 | }
55 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/harakeishi/whris
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/harakeishi/curver v0.1.2
7 | github.com/likexian/whois v1.15.0
8 | github.com/spf13/cobra v1.7.0
9 | )
10 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
2 | github.com/harakeishi/curver v0.1.2 h1:jyPmANcNfpZIau+MJZ+cXAm8E5Hutl5EvItugaBuQmA=
3 | github.com/harakeishi/curver v0.1.2/go.mod h1:G8ZUue/D5LVD1MFMv8bcsrn1SdwL/LbnqC+BFoFuVJw=
4 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
5 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
6 | github.com/likexian/gokit v0.25.13 h1:p2Uw3+6fGG53CwdU2Dz0T6bOycdb2+bAFAa3ymwWVkM=
7 | github.com/likexian/gokit v0.25.13/go.mod h1:qQhEWFBEfqLCO3/vOEo2EDKd+EycekVtUK4tex+l2H4=
8 | github.com/likexian/whois v1.15.0 h1:AYYJ5bNUo8Qy2T1Z5GgMp1oIcIlCcTDfg1buYz6TdAE=
9 | github.com/likexian/whois v1.15.0/go.mod h1:456fUTkh+O8F8v09bGdVl7XxBjRaQ4LvYHyVWX5Bxyg=
10 | github.com/likexian/whois-parser v1.24.8/go.mod h1:b6STMHHDaSKbd4PzGrP50wWE5NzeBUETa/hT9gI0G9I=
11 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
12 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
13 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
14 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
15 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
16 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
17 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
18 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
19 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
20 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
21 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
22 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
23 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
24 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
25 | golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
26 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
27 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
28 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
29 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
30 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
31 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
32 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
33 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
34 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
35 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
36 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
37 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
38 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
39 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
40 | golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
41 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
42 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
43 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
44 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
45 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
46 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
47 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
48 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
49 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
50 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
51 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
52 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
53 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harakeishi/whris/9d53bc72c13c2f9043329bb652d858578eea875d/logo.png
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright © 2022 NAME HERE
3 |
4 | */
5 | package main
6 |
7 | import (
8 | "github.com/harakeishi/whris/cmd"
9 | )
10 |
11 | func main() {
12 | cmd.Execute()
13 | }
14 |
--------------------------------------------------------------------------------
/whris/whris.go:
--------------------------------------------------------------------------------
1 | package whris
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "strings"
7 |
8 | "github.com/likexian/whois"
9 | )
10 |
11 | type NetworkAdmin struct {
12 | IpRange string
13 | NetName string
14 | Country string
15 | Admin string
16 | }
17 |
18 | type Summary struct {
19 | TargetDomain string
20 | TargetIp string
21 | WhoisResponseServer string
22 | WhoisResponse string
23 | ParseResult []NetworkAdmin
24 | }
25 |
26 | func Resolve(domain string, verbose bool) (Summary, error) {
27 | var summary Summary
28 | summary.WhoisResponseServer = "whois.apnic.net"
29 | summary.TargetDomain = domain
30 | addr, err := net.ResolveIPAddr("ip", domain)
31 | if err != nil {
32 | return Summary{}, err
33 | }
34 | summary.TargetIp = addr.String()
35 | summary.WhoisResponse, err = whois.Whois(summary.TargetIp, "whois.iana.org")
36 | if err != nil {
37 | return Summary{}, err
38 | }
39 | summary.SetWhoisResponseServerFromWhoisResponse()
40 | summary.ParseWhoisResponse()
41 | if !summary.ParseCheck() {
42 | summary.ParseResult = summary.ParseResult[1:]
43 | summary.WhoisResponse, err = whois.Whois(summary.TargetIp, summary.WhoisResponseServer)
44 | if err != nil {
45 | return Summary{}, err
46 | }
47 | summary.ParseWhoisResponse()
48 | }
49 | summary.EchoResult(verbose)
50 | return summary, nil
51 | }
52 |
53 | func (s *Summary) ParseWhoisResponse() {
54 | paragraph := s.BreakDownWhoisResponseIntoParagraphs()
55 | for _, v := range paragraph {
56 | tmp := NetworkAdmin{}
57 | row := strings.Split(v, "\n")
58 | for _, val := range row {
59 | col := strings.Split(val, ":")
60 | switch col[0] {
61 | case "inetnum", "NetRange":
62 | tmp.IpRange = strings.TrimSpace(col[1])
63 | case "netname", "NetName":
64 | tmp.NetName = strings.TrimSpace(col[1])
65 | case "country", "Country":
66 | tmp.Country = strings.TrimSpace(col[1])
67 | case "descr", "Organization", "organisation", "owner":
68 | if tmp.Admin == "" {
69 | tmp.Admin = strings.TrimSpace(col[1])
70 | }
71 | }
72 | }
73 | if tmp.IpRange != "" {
74 | s.ParseResult = append(s.ParseResult, tmp)
75 | }
76 | }
77 | }
78 |
79 | func (s *Summary) EchoResult(verbose bool) {
80 | fmt.Printf("Target domain:%s\n", s.TargetDomain)
81 | fmt.Printf("Target ip :%s\n\n", s.TargetIp)
82 | fmt.Printf("Network Admin:%s\n", s.ParseResult[len(s.ParseResult)-1].Admin)
83 | fmt.Printf("Network name :%s\n", s.ParseResult[len(s.ParseResult)-1].NetName)
84 | fmt.Printf("ip range :%s\n", s.ParseResult[len(s.ParseResult)-1].IpRange)
85 | fmt.Printf("country :%s\n", s.ParseResult[len(s.ParseResult)-1].Country)
86 | if verbose {
87 | fmt.Printf("\n=========Network Details=========\n")
88 | for i, v := range s.ParseResult {
89 | fmt.Printf("%d:\n", i)
90 | fmt.Printf(" Network Admin:%s\n", v.Admin)
91 | fmt.Printf(" Network name :%s\n", v.NetName)
92 | fmt.Printf(" ip range :%s\n", v.IpRange)
93 | fmt.Printf(" country :%s\n", v.Country)
94 | }
95 | }
96 | }
97 |
98 | func (s *Summary) BreakDownWhoisResponseIntoParagraphs() []string {
99 | switch s.WhoisResponseServer {
100 | case "whois.arin.net":
101 | return strings.Split(s.WhoisResponse, "#")
102 | case "whois.apnic.net", "whois.ripe.net", "whois.lacnic.net":
103 | return strings.Split(s.WhoisResponse, "%")
104 | default:
105 | return strings.Split(s.WhoisResponse, "\n\n")
106 | }
107 | }
108 |
109 | func (s *Summary) SetWhoisResponseServerFromWhoisResponse() {
110 | tmp := strings.Split(s.WhoisResponse, "\n")
111 | for _, v := range tmp {
112 | col := strings.Split(v, ":")
113 | if col[0] == "refer" {
114 | s.WhoisResponseServer = strings.TrimSpace(col[1])
115 | break
116 | }
117 | }
118 | }
119 |
120 | func (s *Summary) ParseCheck() bool {
121 | // 転送されていないかチェックする
122 | list := []string{"apnic", "arin", "ripe", "lacnic"}
123 | for _, v := range list {
124 | if strings.Contains(strings.ToLower(s.ParseResult[1].NetName), v) {
125 | s.WhoisResponseServer = fmt.Sprintf("whois.%s.net", v)
126 | return false
127 | }
128 | }
129 | return true
130 | }
131 |
--------------------------------------------------------------------------------
/whris/whris_test.go:
--------------------------------------------------------------------------------
1 | package whris
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 | )
7 |
8 | const TestRipeWhoisResponse = `% IANA WHOIS server
9 | % for more information on IANA, visit http://www.iana.org
10 | % This query returned 1 object
11 |
12 | refer: whois.ripe.net
13 |
14 | inetnum: 93.0.0.0 - 93.255.255.255
15 | organisation: RIPE NCC
16 | status: ALLOCATED
17 |
18 | whois: whois.ripe.net
19 |
20 | changed: 2007-03
21 | source: IANA
22 |
23 | % This is the RIPE Database query service.
24 | % The objects are in RPSL format.
25 | %
26 | % The RIPE Database is subject to Terms and Conditions.
27 | % See http://www.ripe.net/db/support/db-terms-conditions.pdf
28 |
29 | % Note: this output has been filtered.
30 | % To receive output for a database update, use the "-B" flag.
31 |
32 | % Information related to '93.184.216.0 - 93.184.216.255'
33 |
34 | % Abuse contact for '93.184.216.0 - 93.184.216.255' is 'abuse@verizondigitalmedia.com'
35 |
36 | inetnum: 93.184.216.0 - 93.184.216.255
37 | netname: EDGECAST-NETBLK-03
38 | descr: NETBLK-03-EU-93-184-216-0-24
39 | country: EU
40 | admin-c: DS7892-RIPE
41 | tech-c: DS7892-RIPE
42 | status: ASSIGNED PA
43 | mnt-by: MNT-EDGECAST
44 | created: 2012-06-22T21:48:41Z
45 | last-modified: 2012-06-22T21:48:41Z
46 | source: RIPE # Filtered
47 |
48 | person: Derrick Sawyer
49 | address: 13031 W Jefferson Blvd #900, Los Angeles, CA 90094
50 | phone: +18773343236
51 | nic-hdl: DS7892-RIPE
52 | created: 2010-08-25T18:44:19Z
53 | last-modified: 2017-03-03T09:06:18Z
54 | source: RIPE
55 | mnt-by: MNT-EDGECAST
56 |
57 | % This query was served by the RIPE Database Query Service version 1.102.2 (WAGYU)
58 |
59 | ;; Query time: 1194 msec
60 | ;; WHEN: Tue Feb 01 21:12:31 JST 2022`
61 |
62 | func TestResolve(t *testing.T) {
63 | type fields struct {
64 | TargetDomain string
65 | TargetIp string
66 | WhoisResponseServer string
67 | WhoisResponse string
68 | ParseResult []NetworkAdmin
69 | }
70 | type args struct {
71 | domain string
72 | verbose bool
73 | }
74 | tests := []struct {
75 | name string
76 | args args
77 | want fields
78 | }{
79 | {
80 | name: "the_result_of_example.com_must_be_returned",
81 | args: args{
82 | domain: "example.com",
83 | verbose: false,
84 | },
85 | want: fields{
86 | ParseResult: []NetworkAdmin{
87 | {
88 | IpRange: "93.0.0.0 - 93.255.255.255",
89 | Admin: "RIPE NCC",
90 | },
91 | {
92 | IpRange: "93.184.216.0 - 93.184.216.255",
93 | Admin: "NETBLK-03-EU-93-184-216-0-24",
94 | Country: "EU",
95 | NetName: "EDGECAST-NETBLK-03",
96 | },
97 | },
98 | },
99 | },
100 | }
101 | for _, tt := range tests {
102 | t.Run(tt.name, func(t *testing.T) {
103 | got, err := Resolve(tt.args.domain, tt.args.verbose)
104 | if err != nil {
105 | t.Errorf("Resolve() error: %v", err)
106 | }
107 | if !reflect.DeepEqual(got.ParseResult, tt.want.ParseResult) {
108 | t.Fatalf("Summary.ParseWhoisResponse() = %v, want %v", got.ParseResult, tt.want.ParseResult)
109 | }
110 | })
111 | }
112 | }
113 |
114 | func TestSummary_ParseWhoisResponse(t *testing.T) {
115 | type fields struct {
116 | WhoisResponseServer string
117 | WhoisResponse string
118 | ParseResult []NetworkAdmin
119 | }
120 | tests := []struct {
121 | name string
122 | fields fields
123 | want fields
124 | }{
125 | {
126 | name: "be_able_to_correctly_parse_the_response_from_ripe",
127 | fields: fields{
128 | WhoisResponseServer: "whois.ripe.net",
129 | WhoisResponse: TestRipeWhoisResponse,
130 | },
131 | want: fields{
132 | WhoisResponseServer: "whois.ripe.net",
133 | WhoisResponse: TestRipeWhoisResponse,
134 | ParseResult: []NetworkAdmin{
135 | {
136 | IpRange: "93.0.0.0 - 93.255.255.255",
137 | Admin: "RIPE NCC",
138 | },
139 | {
140 | IpRange: "93.184.216.0 - 93.184.216.255",
141 | Admin: "NETBLK-03-EU-93-184-216-0-24",
142 | Country: "EU",
143 | NetName: "EDGECAST-NETBLK-03",
144 | },
145 | },
146 | },
147 | },
148 | }
149 | for _, tt := range tests {
150 | t.Run(tt.name, func(t *testing.T) {
151 | s := &Summary{
152 | WhoisResponseServer: tt.fields.WhoisResponseServer,
153 | WhoisResponse: tt.fields.WhoisResponse,
154 | ParseResult: tt.fields.ParseResult,
155 | }
156 | s.ParseWhoisResponse()
157 | if !reflect.DeepEqual(s.ParseResult, tt.want.ParseResult) {
158 | t.Fatalf("Summary.ParseWhoisResponse() = %v, want %v", s.ParseResult, tt.want.ParseResult)
159 | }
160 | })
161 | }
162 | }
163 |
164 | func TestSummary_SetWhoisResponseServerFromWhoisResponse(t *testing.T) {
165 | type fields struct {
166 | WhoisResponseServer string
167 | WhoisResponse string
168 | }
169 | tests := []struct {
170 | name string
171 | fields fields
172 | want fields
173 | }{
174 | {
175 | name: "the_response_server_can_be_set_correctly_from_the_response_of_ripe",
176 | fields: fields{
177 | WhoisResponseServer: "whois.apnic.net",
178 | WhoisResponse: TestRipeWhoisResponse,
179 | },
180 | want: fields{
181 | WhoisResponseServer: "whois.ripe.net",
182 | WhoisResponse: TestRipeWhoisResponse,
183 | },
184 | },
185 | }
186 | for _, tt := range tests {
187 | t.Run(tt.name, func(t *testing.T) {
188 | s := &Summary{
189 | WhoisResponseServer: tt.fields.WhoisResponseServer,
190 | WhoisResponse: tt.fields.WhoisResponse,
191 | }
192 | s.SetWhoisResponseServerFromWhoisResponse()
193 | if s.WhoisResponseServer != tt.want.WhoisResponseServer {
194 | t.Errorf("Summary.SetWhoisResponseServerFromWhoisResponse() = %v, want %v", s.WhoisResponseServer, tt.want.WhoisResponseServer)
195 | }
196 | })
197 | }
198 | }
199 |
200 | func TestSummary_ParseCheck(t *testing.T) {
201 | type fields struct {
202 | TargetDomain string
203 | TargetIp string
204 | WhoisResponseServer string
205 | WhoisResponse string
206 | ParseResult []NetworkAdmin
207 | }
208 | type wants struct {
209 | WhoisResponseServer string
210 | Result bool
211 | }
212 | tests := []struct {
213 | name string
214 | fields fields
215 | want wants
216 | }{
217 | {
218 | name: "return_true_in_non-redirected_response",
219 | fields: fields{
220 | WhoisResponseServer: "whois.ripe.net",
221 | ParseResult: []NetworkAdmin{
222 | {
223 | IpRange: "93.0.0.0 - 93.255.255.255",
224 | Admin: "RIPE NCC",
225 | },
226 | {
227 | IpRange: "93.184.216.0 - 93.184.216.255",
228 | Admin: "NETBLK-03-EU-93-184-216-0-24",
229 | Country: "EU",
230 | NetName: "EDGECAST-NETBLK-03",
231 | },
232 | },
233 | },
234 | want: wants{
235 | WhoisResponseServer: "whois.ripe.net",
236 | Result: true,
237 | },
238 | },
239 | {
240 | name: "return_false_in_redirected_response",
241 | fields: fields{
242 | WhoisResponseServer: "whois.apnic.net",
243 | ParseResult: []NetworkAdmin{
244 | {
245 | IpRange: "157.1.0.0 - 157.14.255.255",
246 | Admin: "APNIC",
247 | Country: "",
248 | NetName: "",
249 | },
250 | {
251 | IpRange: "93.0.0.0 - 93.255.255.255",
252 | Admin: "DS7892-RIPE",
253 | Country: "",
254 | NetName: "RIPE NCC",
255 | },
256 | },
257 | },
258 | want: wants{
259 | WhoisResponseServer: "whois.ripe.net",
260 | Result: false,
261 | },
262 | },
263 | }
264 | for _, tt := range tests {
265 | t.Run(tt.name, func(t *testing.T) {
266 | s := &Summary{
267 | TargetDomain: tt.fields.TargetDomain,
268 | TargetIp: tt.fields.TargetIp,
269 | WhoisResponseServer: tt.fields.WhoisResponseServer,
270 | WhoisResponse: tt.fields.WhoisResponse,
271 | ParseResult: tt.fields.ParseResult,
272 | }
273 | got := s.ParseCheck()
274 | if got != tt.want.Result {
275 | t.Errorf("Summary.ParseCheck() Return = %v, want %v", got, tt.want.Result)
276 | }
277 | if s.WhoisResponseServer != tt.want.WhoisResponseServer {
278 | t.Errorf("Summary.ParseCheck() WhoisResponseServer = %v, want %v", s.WhoisResponseServer, tt.want.WhoisResponseServer)
279 | }
280 | })
281 | }
282 | }
283 |
--------------------------------------------------------------------------------