commit d40bfca474692b104982402369a71e837f4d43ad Author: Jarkko Toivanen <jt@jakest.us> Date: Fri Jan 31 05:59:20 2025 +0200 Simple FI-domain availability checking diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6a9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/config.json +/nexsis diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6c802db --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +run: + go run main.go +build: + go build -v -ldflags "-s -w" diff --git a/config.json.example b/config.json.example new file mode 100644 index 0000000..c910018 --- /dev/null +++ b/config.json.example @@ -0,0 +1,4 @@ +{ + "port": 6900, + "siteName": "NexSIS" +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0905b39 --- /dev/null +++ b/go.mod @@ -0,0 +1,16 @@ +module nexsis + +go 1.23.0 + +require ( + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + golang.org/x/sys v0.22.0 // indirect + modernc.org/libc v1.55.3 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/sqlite v1.34.5 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2b43db3 --- /dev/null +++ b/go.sum @@ -0,0 +1,21 @@ +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= +modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g= +modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE= diff --git a/index.html b/index.html new file mode 100644 index 0000000..a0d0277 --- /dev/null +++ b/index.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8"> + <link rel="stylesheet" type="text/css" href="/static/style.css"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>{{.Title}}</title> + </head> + <body> + <div class="cntrbox"> + <h1>{{.Title}}</h1> + <p> + Will be able to manage. Or not. + </p> + <footer> + <hr> + <small> + 2015 Jarkko Toivanen + </small> + </footer> + </div> + </body> +</html> diff --git a/main.go b/main.go new file mode 100644 index 0000000..1d540ee --- /dev/null +++ b/main.go @@ -0,0 +1,183 @@ +package main + +import ( + "database/sql" + "encoding/json" + "encoding/xml" + "fmt" + "html/template" + "log" + "net" + "net/http" + "os" + "io" + "strings" + "bytes" + "errors" + _ "modernc.org/sqlite" +) + +type Config struct { + Port uint + SiteName string +} + +type page struct { + Title string +} + +type xmlDomain struct { + authority string + registryType string + entityClass string + entityName string +} + +var C = Config{} +var db *sql.DB + +func main() { + var err error + + domain := "snuff.fi" + av, err := checkDomainAvailability(domain) + if err != nil { + log.Fatal(err) + return + } + fmt.Printf("Domain '%s' available: %t\n", domain, av) + return + + // Load config + fmt.Println("Loading config.json") + cfgfile, err := os.Open("config.json") + if err != nil { + log.Fatal("Error reading config.json!") + return + } + defer cfgfile.Close() + + decoder := json.NewDecoder(cfgfile) + //Config := Config{} + err = decoder.Decode(&C) + if err != nil { + log.Fatal("Can't decode config.json!") + return + } + + + + + db, err = sql.Open("sqlite", "database.db") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Starting listener on :%v", C.Port) + http.Handle("/static/", http.FileServer(http.Dir("./"))) + http.HandleFunc("/", httpRootHandler) + err = http.ListenAndServe(fmt.Sprintf(":%v", C.Port), nil) + if err != nil { + log.Fatal(err) + } + + db.Close() +} + +func httpRootHandler(w http.ResponseWriter, r *http.Request) { + t, _ := template.ParseFiles("index.html") + // var err := do_get_stuff() + // if err != nil { + // log.Fatal(err) + // http.Error(w, http.StatusText(500), 500) + // return + // } + + + + var p page + p.Title = C.SiteName; + t.Execute(w, p) +} + +func checkDomainAvailability(domain string) (bool, error) { + dasHost := "das.domain.fi"; + dasPort := 715 + dasAddr := fmt.Sprintf("%s:%d", dasHost, dasPort) + request := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?> +<iris1:request xmlns:iris1="urn:ietf:params:xml:ns:iris1"> +<iris1:searchSet> +<iris1:lookupEntity registryType="dchk1" entityClass="domain-name" entityName="%s"/> +</iris1:searchSet> +</iris1:request>`, domain) + + conn, err := net.Dial("udp", dasAddr) + if err != nil { + log.Fatal(err) + return false, err + } + defer conn.Close() + + fmt.Fprintf(conn, request) + response := make([]byte, 1024) + _, err = conn.Read(response) + if err != nil { + log.Fatal(err) + } + + response = bytes.Trim(response, "\x00") + + // Response can be: + // - active: already in use + // - available: go ahead and grab it while you can + // - invalid: malformed request: maybe check domain has .fi in the end + isAvailable, err := xmlCheckTag(string(response), "available") + if err != nil { + log.Fatal(err) + return false, err + } else if isAvailable { + // Domain is available + return true, nil + } + + isActive, err := xmlCheckTag(string(response), "active") + if err != nil { + log.Fatal(err) + return false, err + } else if isActive { + // Domain is already registered taken + return false, nil + } + + isInvalid, err := xmlCheckTag(string(response), "invalid") + if err != nil { + log.Fatal(err) + return false, err + } else if isInvalid { + // We got bonked for our request + return false, errors.New("Request or domain invalid") + } + + // Execution should not lead here but hey might as well error it out + return false, errors.New("Error: Status field not found") +} + +func xmlCheckTag(src, tag string) (bool, error) { + // Thanks icza for your stackoverflow + // https://stackoverflow.com/a/51649834 + decoder := xml.NewDecoder(strings.NewReader(src)) + for { + t, err := decoder.Token() + if err != nil { + if err == io.EOF { + return false, nil + } + return false, err + } + if se, ok := t.(xml.StartElement); ok { + if se.Name.Local == tag { + return true, nil + } + } + } +} diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..db4a1f6 --- /dev/null +++ b/static/style.css @@ -0,0 +1,14 @@ +body { + background-color: #000000; + color: #ffffff; + font-family: "sans-serif"; +} + +a, a:visited, a:active { + color: #00ffff; +} + +.cntrbox { + display: grid; + place-content: center; +}