hats-domains/main.go

193 lines
5 KiB
Go
Raw Normal View History

2021-10-18 17:42:25 -05:00
package main
// https://pkg.go.dev/github.com/tebeka/selenium#example-package
// https://github.com/domainr/whois
// https://golangexample.com/dns-lookup-using-go/
/*
chandler@xenon ~/projects/hats-domains % for i in {1..50}; do [21:17:08]
dig ${i}hats.com | grep -i nxdomain >/dev/null && echo ${i}hats.com is available
done
5hats.com is available
16hats.com is available
26hats.com is available
28hats.com is available
30hats.com is available
35hats.com is available
36hats.com is available
37hats.com is available
41hats.com is available
43hats.com is available
44hats.com is available
46hats.com is available
48hats.com is available
49hats.com is available
[1] chandler@xenon ~/projects/hats-domains %
*/
import (
2021-10-18 19:32:10 -05:00
_ "embed"
2021-10-18 17:56:43 -05:00
"encoding/base64"
2021-10-18 17:42:25 -05:00
"flag"
"fmt"
"html/template"
"io"
"log"
"net"
2021-10-18 17:42:25 -05:00
"net/http"
"os"
"time"
2021-10-18 19:18:32 -05:00
"github.com/likexian/whois"
whoisparser "github.com/likexian/whois-parser"
2021-10-18 17:42:25 -05:00
"github.com/tebeka/selenium"
)
type HatsSite struct {
DomainName string
2021-10-18 19:18:32 -05:00
Available bool
FetchTime time.Time
DomainInfo *whoisparser.Domain
Registrar *whoisparser.Contact
Registrant *whoisparser.Contact
2021-10-18 17:56:43 -05:00
ScreenshotURL template.URL
2021-10-18 17:42:25 -05:00
Title string
Notes template.HTML
Error template.HTML
2021-10-18 17:42:25 -05:00
}
func getSite(domainName string, wd selenium.WebDriver) (hatsSite HatsSite) {
2021-10-18 23:02:02 -05:00
log.Printf("Retrieving info for %v\n", domainName)
2021-10-18 17:42:25 -05:00
hatsSite.DomainName = domainName
hatsSite.FetchTime = time.Now()
2021-10-18 17:42:25 -05:00
// Check if domain is registered
query_result, err := whois.Whois(hatsSite.DomainName)
if err != nil {
hatsSite.Error = template.HTML(err.Error())
return
}
result, err := whoisparser.Parse(query_result)
if err == whoisparser.ErrNotFoundDomain {
hatsSite.Available = true
hatsSite.Notes = template.HTML(fmt.Sprintf("Register at <a href='https://www.namecheap.com/domains/registration/results/?domain=%v'>NameCheap</a>", hatsSite.DomainName))
return
} else if err != nil {
hatsSite.Error = template.HTML(err.Error())
return
}
hatsSite.Available = false
hatsSite.DomainInfo = result.Domain
hatsSite.Registrar = result.Registrar
hatsSite.Registrant = result.Registrant
_, err = net.LookupHost(hatsSite.DomainName)
if err != nil {
hatsSite.Notes = template.HTML(fmt.Sprintf("DNS Error: <code>%v</code>", err.Error()))
return
}
2021-10-18 19:18:32 -05:00
// Get web page, take screenshot
err = wd.Get(fmt.Sprintf("http://%v/", hatsSite.DomainName))
if err != nil {
hatsSite.Error = template.HTML(err.Error())
return
}
2021-10-18 17:42:25 -05:00
hatsSite.Title, err = wd.Title()
if err != nil {
hatsSite.Error = template.HTML(err.Error())
return
}
screenshot, err := wd.Screenshot()
if err != nil {
hatsSite.Error = template.HTML(err.Error())
return
}
hatsSite.ScreenshotURL = template.URL(fmt.Sprintf("data:image/png;base64,%v", base64.StdEncoding.EncodeToString(screenshot)))
return
}
func getSites(largest int, wd selenium.WebDriver) (sites []HatsSite) {
domains := []string{"0hats.com", "1hat.com"} // Special cases
//domains = append(domains, "hats.com", "onehat.com", "twohats.com", "nhats.com") // Possibly more special cases?
for i := 2; i <= largest; i++ {
domains = append(domains, fmt.Sprintf("%dhats.com", i))
}
2021-10-18 17:42:25 -05:00
for _, domain := range domains {
sites = append(sites, getSite(domain, wd))
2021-10-18 17:42:25 -05:00
}
return sites
2021-10-18 17:42:25 -05:00
}
2021-10-18 19:32:10 -05:00
//go:embed template.html
var rawtmpl string
2021-10-18 17:42:25 -05:00
func generateHTML(sites []HatsSite, w io.Writer) error {
2021-10-18 19:18:32 -05:00
funcs := template.FuncMap{
"parseTime": func(s string) time.Time { time, _ := time.Parse(time.RFC3339, s); return time },
}
2021-10-18 19:32:10 -05:00
tmpl, err := template.New("main").Funcs(funcs).Parse(rawtmpl)
2021-10-18 17:42:25 -05:00
if err != nil {
return err
}
return tmpl.Execute(w, sites)
}
func main() {
serve := flag.Bool("serve", false, "Serve HTTP rather than writing a file")
filename := flag.String("path", "index.html", "Output filename (if -serve=false, default)")
port := flag.Int("port", 8080, "Port to serve on")
2021-10-18 18:55:36 -05:00
debug := flag.Bool("debug", false, "Enable debug logging for the selenium package")
2021-10-18 17:42:25 -05:00
largest := flag.Int("largest", 50, "largest n for {n}hats.com")
flag.Parse()
const (
geckoDriverPath = "deps/geckodriver"
2021-10-19 00:36:02 -05:00
geckoDriverPort = 8081
2021-10-18 17:42:25 -05:00
)
2021-10-18 17:42:25 -05:00
opts := []selenium.ServiceOption{
selenium.StartFrameBuffer(),
2021-10-18 17:42:25 -05:00
selenium.Output(nil),
}
2021-10-18 18:55:36 -05:00
selenium.SetDebug(*debug)
service, err := selenium.NewGeckoDriverService(geckoDriverPath, geckoDriverPort, opts...)
2021-10-18 17:42:25 -05:00
if err != nil {
panic(err)
2021-10-18 17:42:25 -05:00
}
defer service.Stop()
2022-04-02 00:25:20 -05:00
wd, err := selenium.NewRemote(selenium.Capabilities{
"acceptInsecureCerts": true,
}, fmt.Sprintf("http://localhost:%d", geckoDriverPort))
2021-10-18 17:42:25 -05:00
if err != nil {
panic(err)
}
defer wd.Quit()
if *serve {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
2021-10-19 00:36:02 -05:00
err = generateHTML(getSites(*largest, wd), w)
if err != nil {
w.Write([]byte(err.Error()))
}
2021-10-18 17:42:25 -05:00
})
2021-10-19 00:36:02 -05:00
fmt.Printf("Serving on %v\n", *port)
2021-10-18 17:42:25 -05:00
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *port), nil))
} else {
file, err := os.Create(*filename)
if err != nil {
panic(err)
}
err = generateHTML(getSites(*largest, wd), file)
2021-10-18 17:42:25 -05:00
if err != nil {
2021-10-18 23:31:17 -05:00
log.Fatal(err)
2021-10-18 17:42:25 -05:00
}
}
}