Opinionated changes to burble.dn42'ify
This commit is contained in:
parent
3715e7af5a
commit
d56d19a90b
@ -1,7 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.3
|
|
||||||
- "1.11"
|
|
||||||
- tip
|
|
||||||
script: true
|
|
@ -1,4 +0,0 @@
|
|||||||
FROM scratch
|
|
||||||
USER 99
|
|
||||||
ADD whois42d /
|
|
||||||
CMD ["/whois42d", "-registry", "/registry", "-port", "4343"]
|
|
10
Makefile
10
Makefile
@ -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
|
|
11
README.md
11
README.md
@ -1,12 +1,17 @@
|
|||||||
# whois42d
|
# whois42d
|
||||||
[](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
|
## Installation
|
||||||
|
|
||||||
|
**- Outdated instructions below ....**
|
||||||
|
|
||||||
|
|
||||||
1. Install a go compiler (like apt-get install go)
|
1. Install a go compiler (like apt-get install go)
|
||||||
2. Setup Go Workspace:
|
2. Setup Go Workspace:
|
||||||
|
|
||||||
|
@ -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
32
info.go
@ -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
145
server.go
@ -1,10 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
@ -13,12 +14,11 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
"encoding/json"
|
"whois42d/whois"
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/zgiles/whois42d/whois"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
registry whois.Registry
|
registry whois.Registry
|
||||||
LastConnection time.Time
|
LastConnection time.Time
|
||||||
@ -72,79 +72,45 @@ func (s *Server) handleConn(conn *net.TCPConn) {
|
|||||||
s.registry.HandleQuery(conn)
|
s.registry.HandleQuery(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
configfile string
|
Port uint `json:port`
|
||||||
Port uint `json:port`
|
Address string `json:address`
|
||||||
HttpPort uint `json:httpport`
|
Registry string `json:registry`
|
||||||
Address string `json:address`
|
Datapath string
|
||||||
Registry string `json:registry`
|
Header string `json:header`
|
||||||
Datapath string
|
DNSTopLevel string `json:dnstoplevel`
|
||||||
SocketTimeout float64 `json:sockettimeout`
|
RegistryTopLevel string `json:registrytoplevel`
|
||||||
Header string `json:header`
|
|
||||||
DNSTopLevel string `json:dnstoplevel`
|
|
||||||
RegistryTopLevel string `json:registrytoplevel`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseFlags() options {
|
func parseFlags() options {
|
||||||
|
|
||||||
var o 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.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.Address, "address", "*", "address to listen")
|
||||||
flag.StringVar(&o.Registry, "registry", ".", "path to dn42 registry")
|
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,
|
||||||
flag.StringVar(&o.Header, "header", "This is the dn42 whois query service.", "announcement header")
|
"header",
|
||||||
|
"This is the dn42 whois query service.",
|
||||||
|
"announcement header")
|
||||||
flag.StringVar(&o.DNSTopLevel, "dnstoplevel", "dn42", "DNS TLD")
|
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()
|
flag.Parse()
|
||||||
if o.Address == "*" {
|
if o.Address == "*" {
|
||||||
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
|
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) {
|
func checkDataPath(registry string) (string, error) {
|
||||||
dataPath := path.Join(registry, "data")
|
dataPath := path.Join(registry, "data")
|
||||||
@ -165,23 +131,18 @@ func createServer(opts options) (*Server, error) {
|
|||||||
opts.Datapath = dataPath
|
opts.Datapath = dataPath
|
||||||
server := New(opts)
|
server := New(opts)
|
||||||
|
|
||||||
if listeners := Listeners(); len(listeners) > 0 {
|
address := opts.Address + ":" + strconv.Itoa(int(opts.Port))
|
||||||
fmt.Printf("socket action detected\n")
|
listener, err := net.Listen("tcp", address)
|
||||||
server.SocketActivation = true
|
if err != nil {
|
||||||
for _, listener := range listeners {
|
return nil, err
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
go server.Run(listener.(*net.TCPListener))
|
||||||
return server, nil
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// server starts here
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
opts := parseFlags()
|
opts := parseFlags()
|
||||||
|
|
||||||
@ -192,44 +153,18 @@ func main() {
|
|||||||
os.Exit(1)
|
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
|
||||||
signals := make(chan os.Signal, 1)
|
signals := make(chan os.Signal, 1)
|
||||||
signal.Notify(signals, os.Interrupt)
|
signal.Notify(signals, os.Interrupt)
|
||||||
signal.Notify(signals, syscall.SIGTERM)
|
signal.Notify(signals, syscall.SIGTERM)
|
||||||
signal.Notify(signals, syscall.SIGINT)
|
signal.Notify(signals, syscall.SIGINT)
|
||||||
|
|
||||||
// Exit timeout
|
// wait for signal
|
||||||
if server.SocketActivation {
|
<-signals
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Shutting socket(s) down (takes up to 1s)\n")
|
fmt.Printf("Shutting socket(s) down (takes up to 1s)\n")
|
||||||
server.Shutdown()
|
server.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// end of file
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
// +build go1.4
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
func unsetenv(key string) {
|
|
||||||
os.Unsetenv(key)
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
// +build !go1.4
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
func unsetenv(key string) {
|
|
||||||
os.Setenv(key, "")
|
|
||||||
}
|
|
100
whois/json.go
100
whois/json.go
@ -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
|
|
||||||
})
|
|
||||||
}
|
|
@ -12,21 +12,21 @@ import (
|
|||||||
|
|
||||||
type pathpair struct {
|
type pathpair struct {
|
||||||
objtype string
|
objtype string
|
||||||
obj string
|
obj string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) handleObject(conn *net.TCPConn, object Object, flags *Flags) bool {
|
func (r *Registry) handleObject(conn *net.TCPConn, object Object, flags *Flags) bool {
|
||||||
found := false
|
found := false
|
||||||
paths := r.findObjectPaths(object)
|
paths := r.findObjectPaths(object)
|
||||||
for _, p := range paths {
|
for _, p := range paths {
|
||||||
r.printObject(conn, p.objtype, p.obj)
|
r.printObject(conn, p.objtype, p.obj)
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) HandleQuery(conn *net.TCPConn) {
|
func (r *Registry) HandleQuery(conn *net.TCPConn) {
|
||||||
fmt.Fprint(conn, "% " + r.Header + "\n\n")
|
fmt.Fprint(conn, "% "+r.Header+"\n\n")
|
||||||
|
|
||||||
query := parseQuery(conn)
|
query := parseQuery(conn)
|
||||||
if query == nil {
|
if query == nil {
|
||||||
|
@ -1,6 +1,40 @@
|
|||||||
|
##########################################################################
|
||||||
|
# whois42d example service file
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Whois dn42 daemon
|
Description=Whois dn42 daemon
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
||||||
[Service]
|
[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
|
@ -1,8 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Whois dn42 daemon socket
|
|
||||||
|
|
||||||
[Socket]
|
|
||||||
ListenStream=43
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=sockets.target
|
|
Loading…
x
Reference in New Issue
Block a user