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 17:56:43 -05:00
|
|
|
"encoding/base64"
|
2021-10-18 17:42:25 -05:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"html/template"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/tebeka/selenium"
|
|
|
|
)
|
|
|
|
|
|
|
|
type HatsSite struct {
|
|
|
|
DomainName string
|
|
|
|
Owner string
|
|
|
|
Since time.Time
|
2021-10-18 17:56:43 -05:00
|
|
|
ScreenshotURL template.URL
|
2021-10-18 17:42:25 -05:00
|
|
|
Title string
|
|
|
|
FetchTime time.Time
|
|
|
|
HTTPOpen bool
|
|
|
|
HTTPSOpen bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func getSites(largest int, wd selenium.WebDriver) (sites []HatsSite, err error) {
|
|
|
|
for i := 2; i <= largest; i++ {
|
|
|
|
domainName := fmt.Sprintf("%vhats.com", i)
|
|
|
|
|
|
|
|
err := wd.Get(fmt.Sprintf("http://%v/", domainName))
|
|
|
|
if err != nil {
|
|
|
|
return sites, err
|
|
|
|
}
|
|
|
|
|
|
|
|
title, err := wd.Title()
|
|
|
|
if err != nil {
|
|
|
|
return sites, err
|
|
|
|
}
|
|
|
|
|
|
|
|
screenshot, err := wd.Screenshot()
|
|
|
|
if err != nil {
|
|
|
|
return sites, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sites = append(sites, HatsSite{
|
|
|
|
DomainName: domainName,
|
|
|
|
Owner: "unknown",
|
2021-10-18 17:56:43 -05:00
|
|
|
ScreenshotURL: template.URL(fmt.Sprintf("data:image/png;base64,%v", base64.StdEncoding.EncodeToString(screenshot))),
|
2021-10-18 17:42:25 -05:00
|
|
|
Title: title,
|
|
|
|
FetchTime: time.Now(),
|
|
|
|
HTTPOpen: false, // TODO
|
|
|
|
HTTPSOpen: false, // TODO
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return sites, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateHTML(sites []HatsSite, w io.Writer) error {
|
|
|
|
tmpl, err := template.New("main").Parse(`<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
|
|
|
<head>
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<title>{n}hats.com domains</title>
|
|
|
|
<style>
|
2021-10-18 18:02:43 -05:00
|
|
|
body { max-width: 800px; font-family: sans-serif; margin: auto; }
|
2021-10-18 17:42:25 -05:00
|
|
|
dt { font-weight: bold; }
|
2021-10-18 18:02:43 -05:00
|
|
|
img { max-width: 100%; }
|
2021-10-18 17:42:25 -05:00
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<h1>{n}hats.com domains</h1>
|
|
|
|
{{range .}}
|
|
|
|
<h3>{{.DomainName}}</h3>
|
|
|
|
<dl>
|
|
|
|
<dt>Fetched</dt>
|
|
|
|
<dd>{{.FetchTime}}</dd>
|
|
|
|
<dt>Title</dt>
|
|
|
|
<dd>{{.Title}}</dd>
|
|
|
|
<dt>Owner</dt>
|
|
|
|
<dd>{{.Owner}}</dd>
|
|
|
|
<dt>Since</dt>
|
|
|
|
<dd>{{.Since}}</dd>
|
|
|
|
</dl>
|
|
|
|
<img src="{{.ScreenshotURL}}" alt="screenshot of {{.DomainName}} as of {{.FetchTime}}">
|
|
|
|
{{end}}
|
|
|
|
</body>
|
|
|
|
</html>`)
|
|
|
|
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")
|
|
|
|
largest := flag.Int("largest", 50, "largest n for {n}hats.com")
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
const (
|
|
|
|
geckoDriverPath = "deps/geckodriver"
|
2021-10-18 17:43:46 -05:00
|
|
|
geckoDriverPort = 8080
|
2021-10-18 17:42:25 -05:00
|
|
|
)
|
2021-10-18 17:43:46 -05:00
|
|
|
|
2021-10-18 17:42:25 -05:00
|
|
|
opts := []selenium.ServiceOption{
|
2021-10-18 17:43:46 -05:00
|
|
|
selenium.StartFrameBuffer(),
|
2021-10-18 17:42:25 -05:00
|
|
|
selenium.Output(nil),
|
|
|
|
}
|
|
|
|
selenium.SetDebug(true)
|
2021-10-18 17:43:46 -05:00
|
|
|
|
|
|
|
service, err := selenium.NewGeckoDriverService(geckoDriverPath, geckoDriverPort, opts...)
|
2021-10-18 17:42:25 -05:00
|
|
|
if err != nil {
|
2021-10-18 17:43:46 -05:00
|
|
|
panic(err)
|
2021-10-18 17:42:25 -05:00
|
|
|
}
|
|
|
|
defer service.Stop()
|
2021-10-18 17:43:46 -05:00
|
|
|
|
|
|
|
wd, err := selenium.NewRemote(nil, 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) {
|
|
|
|
// err := generateHTML(w, *largest, wd)
|
|
|
|
// if err != nil {
|
|
|
|
// w.Write([]byte(err.Error()))
|
|
|
|
// }
|
|
|
|
})
|
|
|
|
fmt.Printf("Serving on %v\n", port)
|
|
|
|
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *port), nil))
|
|
|
|
} else {
|
|
|
|
file, err := os.Create(*filename)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
sites, err := getSites(*largest, wd)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
err = generateHTML(sites, file)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|