From bb11b15972a541cd5b1bc4798d4a5445a33697a2 Mon Sep 17 00:00:00 2001 From: mrprofessor Date: Tue, 22 Aug 2023 00:19:28 +0530 Subject: [PATCH] Added whois, ssh check and dns commands --- .gitignore | 4 ++ README.md | 1 - cmd/dns.go | 16 +++++ cmd/root.go | 26 ++++++++ cmd/ssl.go | 16 +++++ cmd/who.go | 16 +++++ go.mod | 34 ++++++++++ go.sum | 78 +++++++++++++++++++++++ internal/pkg/dns/dns.go | 122 ++++++++++++++++++++++++++++++++++++ internal/pkg/ssl/ssl.go | 73 +++++++++++++++++++++ internal/pkg/whois/whois.go | 83 ++++++++++++++++++++++++ main.go | 9 +++ readme.org | 35 +++++++++++ 13 files changed, 512 insertions(+), 1 deletion(-) delete mode 100644 README.md create mode 100644 cmd/dns.go create mode 100644 cmd/root.go create mode 100644 cmd/ssl.go create mode 100644 cmd/who.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/pkg/dns/dns.go create mode 100644 internal/pkg/ssl/ssl.go create mode 100644 internal/pkg/whois/whois.go create mode 100644 main.go create mode 100644 readme.org diff --git a/.gitignore b/.gitignore index 3b735ec..ab1f09d 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,7 @@ # Go workspace file go.work + +.DS_Store + +hound diff --git a/README.md b/README.md deleted file mode 100644 index 0cf2c4e..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# hound \ No newline at end of file diff --git a/cmd/dns.go b/cmd/dns.go new file mode 100644 index 0000000..071813b --- /dev/null +++ b/cmd/dns.go @@ -0,0 +1,16 @@ +package cmd + +import ( + "github.com/mrprofessor/hound/internal/pkg/dns" + "github.com/spf13/cobra" +) + +var DnsCmd = &cobra.Command{ + Use: "dns", + Aliases: []string{"dns"}, + Short: "Domain Name Server details for a URL", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + dns.LookUpDnsRecords(args[0]) + }, +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..b48315a --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,26 @@ +package cmd + +import ( + "fmt" + "github.com/spf13/cobra" + "os" +) + +var rootCmd = &cobra.Command{ + Use: "hound", + Short: "Hound is a web information gathering tool", + Long: `Hound aspires to be the only tool you need to get all the information for a website.`, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func init() { + rootCmd.AddCommand(WhoCmd) + rootCmd.AddCommand(SslCmd) + rootCmd.AddCommand(DnsCmd) +} diff --git a/cmd/ssl.go b/cmd/ssl.go new file mode 100644 index 0000000..1f9dcb7 --- /dev/null +++ b/cmd/ssl.go @@ -0,0 +1,16 @@ +package cmd + +import ( + "github.com/mrprofessor/hound/internal/pkg/ssl" + "github.com/spf13/cobra" +) + +var SslCmd = &cobra.Command{ + Use: "ssl", + Aliases: []string{"ssl"}, + Short: "SSL data for a site", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ssl.RunSSL(args[0]) + }, +} diff --git a/cmd/who.go b/cmd/who.go new file mode 100644 index 0000000..8f6d449 --- /dev/null +++ b/cmd/who.go @@ -0,0 +1,16 @@ +package cmd + +import ( + "github.com/mrprofessor/hound/internal/pkg/whois" + "github.com/spf13/cobra" +) + +var WhoCmd = &cobra.Command{ + Use: "who", + Aliases: []string{"whois"}, + Short: "Whois data about a domain", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + whois.RunWhoIs(args[0]) + }, +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8f51e70 --- /dev/null +++ b/go.mod @@ -0,0 +1,34 @@ +module github.com/mrprofessor/hound + +go 1.21 + +require ( + github.com/charmbracelet/glamour v0.6.0 + github.com/likexian/whois v1.9.0 + github.com/likexian/whois-parser v1.20.2 + github.com/spf13/cobra v1.7.0 +) + +require ( + github.com/alecthomas/chroma v0.10.0 // indirect + github.com/aymanbagabas/go-osc52 v1.0.3 // indirect + github.com/aymerick/douceur v0.2.0 // indirect + github.com/dlclark/regexp2 v1.4.0 // indirect + github.com/gorilla/css v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/likexian/gokit v0.24.7 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/microcosm-cc/bluemonday v1.0.21 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.13.0 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/yuin/goldmark v1.5.2 // indirect + github.com/yuin/goldmark-emoji v1.0.1 // indirect + golang.org/x/net v0.0.0-20221002022538-bcab6841153b // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/text v0.3.7 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..092edb4 --- /dev/null +++ b/go.sum @@ -0,0 +1,78 @@ +github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= +github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= +github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= +github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= +github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/likexian/gokit v0.24.7 h1:jGb8rZTKFnk0tAY9Knd4FwQVLRVBaAEM1gY8C/y1sBQ= +github.com/likexian/gokit v0.24.7/go.mod h1:NCv1RDZK5kR0T2SfAl/vjIO6rsjszt2C/25TKxJalhs= +github.com/likexian/whois v1.9.0 h1:s8CtcrWTwfoqbvgBcu9MLLmCovfQXWZOymBAANsBEkI= +github.com/likexian/whois v1.9.0/go.mod h1:wcTZLs5xGhwVsKZ8yE8VbOKiIIkMGssUV+Z6QgPWCqU= +github.com/likexian/whois-parser v1.20.2 h1:JiJKgQwKSEhR9hcPYDFidIQPCWrf4g5es4aHwtYJsJk= +github.com/likexian/whois-parser v1.20.2/go.mod h1:nswyr25OlatRYZH3A9kK4Bobp7QtsPWDPX+46bgQaT8= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg= +github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= +github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU= +github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= +github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20221002022538-bcab6841153b h1:6e93nYa3hNqAvLr0pD4PN1fFS+gKzp2zAXqrnTCstqU= +golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/pkg/dns/dns.go b/internal/pkg/dns/dns.go new file mode 100644 index 0000000..8915f68 --- /dev/null +++ b/internal/pkg/dns/dns.go @@ -0,0 +1,122 @@ +package dns + +import ( + "fmt" + "github.com/charmbracelet/glamour" + "net" +) + +func LookUpDnsRecords(url string) { + + var dnsInfo string + // Write functions to lookup all the possible records. + + dnsInfo = "## A & AAAA record \n" + + lookUpIpRecords(url) + + + "\n ## CNAME record \n" + + lookUpCnameRecords(url) + + + "\n ## MX records \n" + + lookUpMxRecords(url) + + + "\n ## TXT records \n" + + lookUpTxtRecords(url) + + + "\n ## Name servers \n" + + lookUpNameServers(url) + + + // PTR records are only for IPs(opposite of A records) + "\n ## PTR records \n" + + lookUpPtrRecords(url) + + + "\n ## SRV records \n" + + lookUpSrvRecords(url) + + Render(dnsInfo) +} + +func Render(str string) { + out, _ := glamour.Render(str, "auto") + fmt.Print(out) +} + +func lookUpIpRecords(url string) string { + ipRecords, err := net.LookupIP(url) + if err != nil { + return "- " + err.Error() + } + str := "" + for _, ip := range ipRecords { + str += fmt.Sprintln("- ", ip) + } + return str +} + +func lookUpCnameRecords(url string) string { + CNAME, err := net.LookupCNAME(url) + if err != nil { + return "- " + err.Error() + } + return fmt.Sprintln("- ", CNAME) +} + +func lookUpMxRecords(url string) string { + mxRecords, err := net.LookupMX(url) + if err != nil { + return "- " + err.Error() + } + str := "" + for _, mx := range mxRecords { + str += fmt.Sprintln("- ", mx.Host, mx.Pref) + } + return str +} + +func lookUpTxtRecords(url string) string { + txtRecords, err := net.LookupTXT(url) + if err != nil { + return "- " + err.Error() + } + str := "" + for _, txt := range txtRecords { + str += fmt.Sprintf("- %s\n", txt) + } + return str +} + +func lookUpNameServers(url string) string { + nameServers, err := net.LookupNS(url) + if err != nil { + return "- " + err.Error() + } + str := "" + for _, ns := range nameServers { + str += fmt.Sprintf("- %s \n", ns) + } + return str +} + +func lookUpPtrRecords(url string) string { + ptrServers, err := net.LookupAddr(url) + if err != nil { + return "- " + err.Error() + } + str := "" + for _, ptr := range ptrServers { + str += fmt.Sprintf("- %s\n", ptr) + } + return str +} + +func lookUpSrvRecords(url string) string { + _, xmppSrvRecords, err := net.LookupSRV("xmpp-server", "tcp", url) + if err != nil { + return "- " + err.Error() + } + str := "" + for _, srv := range xmppSrvRecords { + str += fmt.Sprintf("- %v:%v:%d:%d\n", srv.Target, srv.Port, srv.Priority, srv.Weight) + } + return str +} diff --git a/internal/pkg/ssl/ssl.go b/internal/pkg/ssl/ssl.go new file mode 100644 index 0000000..89a3b76 --- /dev/null +++ b/internal/pkg/ssl/ssl.go @@ -0,0 +1,73 @@ +package ssl + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "net" + + "github.com/charmbracelet/glamour" +) + +// TODO +// Error handling needs to improve +func RunSSL(url string) { + cert, err := SSLCertData(url) + if err != nil { + fmt.Errorf("Error getting SSL data, Error: ", err) + return + } + Render(cert) +} + +// NOTE +// crypto/tls doesn't return certificates when it has some errors. For example +// with CertificateInvalidError, it returns the certificates, but not for other +// types of errors. +// e.g certificate signed by unknown authority +// test with (self-signed.badssl.com) from badssl.com +func SSLCertData(url string) (*x509.Certificate, error) { + // By default HTTPS connections use port 443 + conn, err := tls.Dial("tcp", url+":443", nil) + if err != nil { + + var certErr x509.CertificateInvalidError + if errors.As(err, &certErr) && certErr.Reason == x509.Expired { + fmt.Errorf("Certificate has expired or is not yet valid") + // Certificate exists even though it's expired. + return certErr.Cert, nil + } else if errors.As(err, new(*net.DNSError)) { + fmt.Errorf("Host not Found") + } else { + fmt.Errorf("%+v\n", err) + } + } + + // Malformed certificates can be present even after an error + // Only return the first certificate. + if conn != nil { + return conn.ConnectionState().PeerCertificates[0], nil + } else { + return nil, err + } +} + +func Render(cert *x509.Certificate) { + + sslInfo := "## Certificate Details \n" + + fmt.Sprintf("- %-20s: %v\n", "Issuer", cert.Issuer.String()) + + fmt.Sprintf("- %-20s: %v\n", "Subject", cert.Subject.String()) + + fmt.Sprintf("- %-20s: %v\n", "Version", cert.Version) + + fmt.Sprintf("- %-20s: %v\n", "Serial Number", cert.SerialNumber) + + fmt.Sprintf("- %-20s: %v\n", "Start Date", cert.NotBefore) + + fmt.Sprintf("- %-20s: %v\n", "Expiration Date", cert.NotAfter) + + fmt.Sprintf("- %-20s: %v\n", "DNS Names", cert.DNSNames) + + fmt.Sprintf("- %-20s: %v\n", "Email Addresses", cert.EmailAddresses) + + fmt.Sprintf("- %-20s: %v\n", "IP Addresses", cert.IPAddresses) + + fmt.Sprintf("- %-20s: %v\n", "URIs", cert.URIs) + + fmt.Sprintf("- %-20s: %v\n", "Permitted Domains", cert.PermittedDNSDomains) + + out, _ := glamour.Render(sslInfo, "auto") + fmt.Print(out) +} diff --git a/internal/pkg/whois/whois.go b/internal/pkg/whois/whois.go new file mode 100644 index 0000000..5c3670e --- /dev/null +++ b/internal/pkg/whois/whois.go @@ -0,0 +1,83 @@ +package whois + +import ( + "fmt" + + "github.com/charmbracelet/glamour" + "github.com/likexian/whois" + "github.com/likexian/whois-parser" +) + +func RunWhoIs(url string) { + whoIsParsedData, err := WhoIsData(url) + if err != nil { + fmt.Println("Error Getting and parsing Whois data, Error: ", err) + return + } + Render(*whoIsParsedData) +} + +// WhoIsData accepts the URL and returns a whoisparser.WhoisInfo struct. +func WhoIsData(url string) (*whoisparser.WhoisInfo, error) { + whoIsRaw, err := whois.Whois(url) + if err != nil { + return nil, err + } + + parsedData, err := whoisparser.Parse(whoIsRaw) + if err != nil { + return nil, err + } + + return &parsedData, nil +} + +func Render(resp whoisparser.WhoisInfo) { + + // Create a map of similar types in order to iterate easily. + whoIsAddrMap := map[string]*whoisparser.Contact{ + "Registrar": resp.Registrar, + "Registrant": resp.Registrant, + "Administrative": resp.Administrative, + "Technical": resp.Technical, + "Billing": resp.Billing, + } + + domainInfo := "## Domain Info \n" + + fmt.Sprintf("- %-20s: %s\n", "Name", resp.Domain.Name) + + fmt.Sprintf("- %-20s: %s\n", "Whois Server", resp.Domain.WhoisServer) + + fmt.Sprintf("- %-20s: %s\n", "Registered On", resp.Domain.CreatedDate) + + fmt.Sprintf("- %-20s: %s\n", "Expires On", resp.Domain.ExpirationDate) + + fmt.Sprintf("- %-20s: %s\n", "Domain Status", resp.Domain.Status) + + fmt.Sprintf("- %-20s: %s\n", "Name Servers", resp.Domain.NameServers) + + // Print WhoIs contact info + for k, v := range whoIsAddrMap { + if v != nil { + + str := fmt.Sprintf("## %s \n", k) + + fmt.Sprintf("- %-20s: %s\n", "Name", v.Name) + + fmt.Sprintf("- %-20s: %s\n", "Organization", v.Organization) + + fmt.Sprintf("- %-20s: %s\n", "Street", v.Street) + + fmt.Sprintf("- %-20s: %s\n", "City", v.City) + + fmt.Sprintf("- %-20s: %s\n", "State", v.Province) + + fmt.Sprintf("- %-20s: %s\n", "Country", v.Country) + + fmt.Sprintf("- %-20s: %s\n", "Phone", v.Phone) + + fmt.Sprintf("- %-20s: %s\n", "Phone Extension", v.PhoneExt) + + fmt.Sprintf("- %-20s: %s\n", "Email", v.Email) + + fmt.Sprintf("- %-20s: %s\n", "Referral URL", v.ReferralURL) + + domainInfo += str + } + } + + customRenderer, _ := glamour.NewTermRenderer( + // detect background color and pick either the default dark or light theme + glamour.WithAutoStyle(), + // wrap output at specific width (default is 80) + glamour.WithWordWrap(100), + ) + + out, _ := customRenderer.Render(domainInfo) + fmt.Print(out) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..a0bf35b --- /dev/null +++ b/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/mrprofessor/hound/cmd" +) + +func main() { + cmd.Execute() +} diff --git a/readme.org b/readme.org new file mode 100644 index 0000000..f638a02 --- /dev/null +++ b/readme.org @@ -0,0 +1,35 @@ +* Hound +:PROPERTIES: +:CUSTOM_ID: hound +:END: + +Hound aspires to be the only tool you need to get all the information for a website. + +** Initial basic usage +:PROPERTIES: +:CUSTOM_ID: initial-basic-usage +:END: + +- =hound who -url github.com= + + Provides the basic whois data + +- =hound ssl -url github.com= + + Provides the ssl certs data, such as expiration etc + +- =hound dns-lookup -url github.com= + + Shows basic DNS records + + +** TODOs +:PROPERTIES: +:CUSTOM_ID: todos +:END: + +- Fix SSL +- Add support for more dns records(CAA, SPF, etc.) +- Add support for more SRV server(Currently only support xmpp) +- Add a fast sub domain finder +- Tests maybe?