Opinionated changes to burble.dn42'ify

This commit is contained in:
Simon Marsh 2020-05-18 13:51:14 +01:00
parent 3715e7af5a
commit d56d19a90b
No known key found for this signature in database
GPG Key ID: 30B29A716A54DBB3
13 changed files with 88 additions and 301 deletions

View File

@ -1,7 +0,0 @@
language: go
go:
- 1.3
- "1.11"
- tip
script: true

View File

@ -1,4 +0,0 @@
FROM scratch
USER 99
ADD whois42d /
CMD ["/whois42d", "-registry", "/registry", "-port", "4343"]

View File

@ -1,10 +0,0 @@
VERSION?=1.0
container:
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o whois42d .
strip whois42d
docker build -t mic92/whois42d:$(VERSION) .
upload:
docker tag -f mic92/whois42d:$(VERSION) mic92/whois42d:latest
docker push mic92/whois42d

View File

@ -1,12 +1,17 @@
# whois42d
[![Build Status](https://travis-ci.org/Mic92/whois42d.svg?branch=master)](https://travis-ci.org/Mic92/whois42d)
Whois server for the dn42 registry.
burble.dn42 whois server for the dn42 registry.
Based original on whoisd from the dn42 monotone registry written by
[welterde](https://github.com/Mic92/whois42d), and fork by
[zgiles](https://github.com/zgiles/whois42d).
Based original on whoisd from the dn42 monotone registry written by welterde.
## Installation
**- Outdated instructions below ....**
1. Install a go compiler (like apt-get install go)
2. Setup Go Workspace:

View File

@ -1,8 +0,0 @@
{
"registry": "data",
"port": 43,
"httpport": 80,
"header": "This is the dn42 whois query service.",
"dnstoplevel": "dn42",
"registrytoplevel": "DN42"
}

32
info.go
View File

@ -1,32 +0,0 @@
package main
import (
"fmt"
"net/http"
)
func HandleHTTPHelp() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request){
content := `<html><body><pre>
Whois42d JSON API:
Paths:
/ This message, help.
/api/1/text Query with text response
/api/1/json Query with json response
/api/1/version Server version query
/api/1/types Server types query ( list all schema types )
Query parameter:
The URL variable 'q' should be filled in with your query
Ex:
/api/1/json?q=10.0.0.0/8
/api/1/json?q=SOMEONE-MNT
</pre></body></html>
`
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, content)
})
}

145
server.go
View File

@ -1,10 +1,11 @@
package main
//////////////////////////////////////////////////////////////////////////
import (
"flag"
"fmt"
"net"
"net/http"
"os"
"os/signal"
"path"
@ -13,12 +14,11 @@ import (
"sync/atomic"
"syscall"
"time"
"encoding/json"
"io/ioutil"
"github.com/zgiles/whois42d/whois"
"whois42d/whois"
)
//////////////////////////////////////////////////////////////////////////
type Server struct {
registry whois.Registry
LastConnection time.Time
@ -72,79 +72,45 @@ func (s *Server) handleConn(conn *net.TCPConn) {
s.registry.HandleQuery(conn)
}
//////////////////////////////////////////////////////////////////////////
type options struct {
configfile string
Port uint `json:port`
HttpPort uint `json:httpport`
Address string `json:address`
Registry string `json:registry`
Datapath string
SocketTimeout float64 `json:sockettimeout`
Header string `json:header`
DNSTopLevel string `json:dnstoplevel`
RegistryTopLevel string `json:registrytoplevel`
Port uint `json:port`
Address string `json:address`
Registry string `json:registry`
Datapath string
Header string `json:header`
DNSTopLevel string `json:dnstoplevel`
RegistryTopLevel string `json:registrytoplevel`
}
func parseFlags() options {
var o options
flag.StringVar(&o.configfile, "config", "config.json", "config file")
// default options
flag.UintVar(&o.Port, "port", 43, "port to listen")
flag.UintVar(&o.HttpPort, "httpport", 80, "port to listen on for http")
flag.StringVar(&o.Address, "address", "*", "address to listen")
flag.StringVar(&o.Registry, "registry", ".", "path to dn42 registry")
flag.Float64Var(&o.SocketTimeout, "timeout", 10, "timeout in seconds before suspending the service when using socket activation")
flag.StringVar(&o.Header, "header", "This is the dn42 whois query service.", "announcement header")
flag.StringVar(&o.Header,
"header",
"This is the dn42 whois query service.",
"announcement header")
flag.StringVar(&o.DNSTopLevel, "dnstoplevel", "dn42", "DNS TLD")
flag.StringVar(&o.RegistryTopLevel, "registrytoplevel", "DN42", "Registry Top Level identifier")
flag.StringVar(&o.RegistryTopLevel,
"registrytoplevel",
"DN42",
"Registry Top Level identifier")
flag.Parse()
if o.Address == "*" {
o.Address = ""
}
// config
if _, err := os.Stat(o.configfile); err == nil {
jsonfile, jsonfileerr := ioutil.ReadFile(o.configfile)
if jsonfileerr != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", jsonfileerr)
os.Exit(1)
}
err := json.Unmarshal(jsonfile, &o)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}
return o
}
func Listeners() []*net.TCPListener {
defer unsetenv("LISTEN_PID")
defer unsetenv("LISTEN_FDS")
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
if err != nil || pid != os.Getpid() {
return nil
}
nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
if err != nil || nfds == 0 {
return nil
}
listeners := make([]*net.TCPListener, 0)
for fd := 3; fd < 3+nfds; fd++ {
syscall.CloseOnExec(fd)
file := os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd))
if listener, err := net.FileListener(file); err == nil {
if l, ok := listener.(*net.TCPListener); ok {
listeners = append(listeners, l)
}
}
}
return listeners
}
//////////////////////////////////////////////////////////////////////////
func checkDataPath(registry string) (string, error) {
dataPath := path.Join(registry, "data")
@ -165,23 +131,18 @@ func createServer(opts options) (*Server, error) {
opts.Datapath = dataPath
server := New(opts)
if listeners := Listeners(); len(listeners) > 0 {
fmt.Printf("socket action detected\n")
server.SocketActivation = true
for _, listener := range listeners {
go server.Run(listener)
}
} else {
address := opts.Address + ":" + strconv.Itoa(int(opts.Port))
listener, err := net.Listen("tcp", address)
if err != nil {
return nil, err
}
go server.Run(listener.(*net.TCPListener))
address := opts.Address + ":" + strconv.Itoa(int(opts.Port))
listener, err := net.Listen("tcp", address)
if err != nil {
return nil, err
}
go server.Run(listener.(*net.TCPListener))
return server, nil
}
//////////////////////////////////////////////////////////////////////////
// server starts here
func main() {
opts := parseFlags()
@ -192,44 +153,18 @@ func main() {
os.Exit(1)
}
// create HTTP server
httpRouter := http.NewServeMux()
httpRouter.Handle("/", HandleHTTPHelp())
httpRouter.Handle("/api/1/text", server.registry.HandleHTTPBoth("text"))
httpRouter.Handle("/api/1/json", server.registry.HandleHTTPBoth("json"))
httpRouter.Handle("/api/1/version", server.registry.HandleHTTPVersion())
httpRouter.Handle("/api/1/types", server.registry.HandleHTTPTypes())
go func() {
address := opts.Address + ":" + strconv.Itoa(int(opts.HttpPort))
if err := http.ListenAndServe(address, httpRouter); err != nil && err != http.ErrServerClosed {
panic(err)
}
}()
// Signals
signals := make(chan os.Signal, 1)
signal.Notify(signals, os.Interrupt)
signal.Notify(signals, syscall.SIGTERM)
signal.Notify(signals, syscall.SIGINT)
// Exit timeout
if server.SocketActivation {
Out:
for {
select {
case <-signals:
break Out
case <-time.After(time.Second * 3):
if time.Since(server.LastConnection).Seconds() >= opts.SocketTimeout {
break Out
}
}
}
} else {
<-signals
}
// wait for signal
<-signals
fmt.Printf("Shutting socket(s) down (takes up to 1s)\n")
server.Shutdown()
}
//////////////////////////////////////////////////////////////////////////
// end of file

View File

@ -1,9 +0,0 @@
// +build go1.4
package main
import "os"
func unsetenv(key string) {
os.Unsetenv(key)
}

View File

@ -1,9 +0,0 @@
// +build !go1.4
package main
import "os"
func unsetenv(key string) {
os.Setenv(key, "")
}

View File

@ -1,100 +0,0 @@
package whois
import (
"net/http"
"fmt"
"bytes"
"strconv"
"encoding/json"
)
func (r *Registry) HandleHTTPBoth(t string) http.Handler {
// t := "json"
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request){
content := []byte("")
var m []map[string]string
q, ok := req.URL.Query()["q"]
if !ok || len(q[0]) < 1 {
http.Error(w, "Bad request", 400)
return
}
o := parseObject(q[0])
paths := r.findObjectPaths(o)
for _, p := range paths {
c, _, err := r.retrieveObject(p.objtype, p.obj)
if err != nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
m = append(m,WhoisToMap(c))
content = append(content[:], c[:]...)
}
switch t {
case "json":
j, jerr := json.Marshal(m)
if jerr != nil {
http.Error(w, "Bad request", 400)
} else {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, string(j))
}
return
case "text":
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, string(content))
return
default:
fmt.Println(t)
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
})
}
func WhoisToMap(b []byte) map[string]string {
r := make(map[string]string)
for _, l := range bytes.Split(b, []byte("\n")) {
i := bytes.Index(l, []byte(":"))
if i > 0 {
r[string(bytes.TrimSpace(l[0:i]))] = string(bytes.TrimSpace(l[i+1:]))
}
}
return r
}
func (r *Registry) HandleHTTPVersion() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request){
m := map[string]string{
"version": strconv.FormatInt(VERSION, 10),
}
j, jerr := json.Marshal(m)
if jerr != nil {
http.Error(w, "Bad request", 400)
} else {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, string(j))
}
return
})
}
func (r *Registry) HandleHTTPTypes() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request){
var m []string
for _, t := range r.whoisTypes {
m = append(m,t.Name)
}
j, jerr := json.Marshal(m)
if jerr != nil {
http.Error(w, "Bad request", 400)
} else {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, string(j))
}
return
})
}

View File

@ -12,21 +12,21 @@ import (
type pathpair struct {
objtype string
obj string
obj string
}
func (r *Registry) handleObject(conn *net.TCPConn, object Object, flags *Flags) bool {
found := false
paths := r.findObjectPaths(object)
for _, p := range paths {
r.printObject(conn, p.objtype, p.obj)
found = true
r.printObject(conn, p.objtype, p.obj)
found = true
}
return found
}
func (r *Registry) HandleQuery(conn *net.TCPConn) {
fmt.Fprint(conn, "% " + r.Header + "\n\n")
fmt.Fprint(conn, "% "+r.Header+"\n\n")
query := parseQuery(conn)
if query == nil {

View File

@ -1,6 +1,40 @@
##########################################################################
# whois42d example service file
##########################################################################
[Unit]
Description=Whois dn42 daemon
After=network.target
[Install]
WantedBy=multi-user.target
[Service]
ExecStart=/usr/local/bin/whois42d -registry /var/lib/whois42d/registry
User=nobody
# running as an unprivileged user requires the whois42d binary
# to be installed with additional capabilities for port 43 binding
#
# setcap 'cap_net_bind_service=+ep' ./whois42d
#
User=whoisd
Group=whoisd
Type=simple
Restart=on-failure
# service hardening
ProtectSystem=strict
ReadOnlyPaths=/home/whoisd/registry
NoNewPrivileges=yes
ProtectControlGroups=yes
PrivateTmp=yes
PrivateDevices=yes
DevicePolicy=closed
MemoryDenyWriteExecute=yes
# exec func
ExecStart=/usr/local/bin/whois42d \
-registry /home/whoisd/registry
##########################################################################
# end of file

View File

@ -1,8 +0,0 @@
[Unit]
Description=Whois dn42 daemon socket
[Socket]
ListenStream=43
[Install]
WantedBy=sockets.target