Simple FI-domain availability checking
This commit is contained in:
commit
d40bfca474
8 changed files with 267 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/config.json
|
||||||
|
/nexsis
|
4
Makefile
Normal file
4
Makefile
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
run:
|
||||||
|
go run main.go
|
||||||
|
build:
|
||||||
|
go build -v -ldflags "-s -w"
|
4
config.json.example
Normal file
4
config.json.example
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"port": 6900,
|
||||||
|
"siteName": "NexSIS"
|
||||||
|
}
|
16
go.mod
Normal file
16
go.mod
Normal 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
21
go.sum
Normal 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
23
index.html
Normal 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
183
main.go
Normal 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
14
static/style.css
Normal 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;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue