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
|
||||
[](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:
|
||||
|
||||
|
@ -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
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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
|
||||
|
@ -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 {
|
||||
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 {
|
||||
|
@ -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
|
@ -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