Simple FI-domain availability checking

This commit is contained in:
Jarkko Toivanen 2025-01-31 05:59:20 +02:00
commit d40bfca474
8 changed files with 267 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/config.json
/nexsis

4
Makefile Normal file
View file

@ -0,0 +1,4 @@
run:
go run main.go
build:
go build -v -ldflags "-s -w"

4
config.json.example Normal file
View file

@ -0,0 +1,4 @@
{
"port": 6900,
"siteName": "NexSIS"
}

16
go.mod Normal file
View file

@ -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
)

21
go.sum Normal file
View file

@ -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=

23
index.html Normal file
View file

@ -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>

183
main.go Normal file
View file

@ -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
}
}
}
}

14
static/style.css Normal file
View file

@ -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;
}