intial import
This commit is contained in:
commit
2e90d32ce2
5 changed files with 264 additions and 0 deletions
35
auth.go
Normal file
35
auth.go
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func authenticateUser(db *sql.DB, username, password string) bool {
|
||||||
|
pass, ok := getPasswordForUser(db, username)
|
||||||
|
if ok {
|
||||||
|
return subtle.ConstantTimeCompare([]byte(pass), []byte(password)) == 1
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func basicAuth(db *sql.DB) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, pass, ok := r.BasicAuth()
|
||||||
|
if !ok || !authenticateUser(db, user, pass) {
|
||||||
|
w.Header().Set("WWW-Authenticate", `Basic realm="dyndns"`)
|
||||||
|
w.WriteHeader(401)
|
||||||
|
w.Write([]byte("badauth"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userdata, err := getDataForUser(db, user)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
handleRequest(w, r, userdata)
|
||||||
|
}
|
||||||
|
}
|
||||||
68
backend.go
Normal file
68
backend.go
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// check if net.ParseIP returns nil!!
|
||||||
|
func getIP(r *http.Request) net.IP {
|
||||||
|
addr := r.URL.Query().Get("myip")
|
||||||
|
if len(addr) > 0 {
|
||||||
|
return net.ParseIP(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = r.Header.Get(http.CanonicalHeaderKey("X-Forwarded-For"))
|
||||||
|
if len(addr) > 0 {
|
||||||
|
end := strings.Index(addr, ", ")
|
||||||
|
if end == -1 {
|
||||||
|
end = len(addr)
|
||||||
|
}
|
||||||
|
return net.ParseIP(addr[:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = r.Header.Get(http.CanonicalHeaderKey("X-Real-IP"))
|
||||||
|
if len(addr) > 0 {
|
||||||
|
return net.ParseIP(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return net.ParseIP(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleRequest(w http.ResponseWriter, r *http.Request, data userData) {
|
||||||
|
msg := "good"
|
||||||
|
defer func() {
|
||||||
|
w.Write([]byte(msg))
|
||||||
|
}()
|
||||||
|
|
||||||
|
hostname := r.URL.Query().Get("hostname")
|
||||||
|
if len(hostname) <= 0 || hostname != data.hostname {
|
||||||
|
msg = "nohost"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ipaddr := getIP(r)
|
||||||
|
if ipaddr == nil {
|
||||||
|
//TODO:
|
||||||
|
msg = "error"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg = "Hostname: " + hostname + " , IP: " + ipaddr.String()
|
||||||
|
|
||||||
|
updated, err := updateNameserver(data, ipaddr)
|
||||||
|
if err != nil {
|
||||||
|
msg = "dnserr"
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !updated {
|
||||||
|
msg = "nochg"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
72
data.go
Normal file
72
data.go
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"database/sql"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
//TODO: move into separate config file?
|
||||||
|
const (
|
||||||
|
DBHost = "127.0.0.1"
|
||||||
|
DBPort = 5432
|
||||||
|
DBUser = "username"
|
||||||
|
DBPass = "password"
|
||||||
|
DBName = "database"
|
||||||
|
)
|
||||||
|
|
||||||
|
type userData struct {
|
||||||
|
nameserver string
|
||||||
|
zonename string
|
||||||
|
hostname string
|
||||||
|
tsigkey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareDatabase() (*sql.DB, error) {
|
||||||
|
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s " +
|
||||||
|
"sslmode=disable", DBHost, DBPort, DBUser, DBPass,
|
||||||
|
DBName)
|
||||||
|
db, err := sql.Open("postgres", psqlInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("CREATE TABLE IF NOT EXISTS users " +
|
||||||
|
"(id SERIAL PRIMARY KEY, username VARCHAR(255) NULL, " +
|
||||||
|
"password VARCHAR(255) NULL, salt VARCHAR(255) NULL, " +
|
||||||
|
"nameserver VARCHAR(255) NULL, zone VARCHAR(255) NULL, " +
|
||||||
|
"hostname VARCHAR(255) NULL, tsig VARCHAR(255) NULL)")
|
||||||
|
if err != nil {
|
||||||
|
db.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPasswordForUser(db *sql.DB, username string) (string, bool) {
|
||||||
|
var password string
|
||||||
|
|
||||||
|
row := db.QueryRow("SELECT password FROM users WHERE username=$1", username)
|
||||||
|
err := row.Scan(&password)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return "", false
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return password, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDataForUser(db *sql.DB, username string) (userData, error) {
|
||||||
|
var data userData
|
||||||
|
|
||||||
|
row := db.QueryRow("SELECT nameserver, zone, hostname, tsig " +
|
||||||
|
"FROM users WHERE username=$1", username)
|
||||||
|
err := row.Scan(&data.nameserver, &data.zonename, &data.hostname,
|
||||||
|
&data.tsigkey)
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
19
main.go
Normal file
19
main.go
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
db, err := prepareDatabase()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
http.HandleFunc("/", basicAuth(db))
|
||||||
|
err = http.ListenAndServe(":3002", nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
70
nameserver.go
Normal file
70
nameserver.go
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createQuery(data userData, addr net.IP, deleteRecord bool) string {
|
||||||
|
var query strings.Builder
|
||||||
|
|
||||||
|
query.WriteString(fmt.Sprintf("key %s\n", data.tsigkey))
|
||||||
|
query.WriteString(fmt.Sprintf("server %s\n", data.nameserver))
|
||||||
|
query.WriteString(fmt.Sprintf("zone %s\n", data.zonename))
|
||||||
|
if deleteRecord {
|
||||||
|
query.WriteString(fmt.Sprintf("update delete %s. A\n", data.hostname))
|
||||||
|
}
|
||||||
|
query.WriteString(fmt.Sprintf("update add %s. A %s\n", data.hostname,
|
||||||
|
addr.String()))
|
||||||
|
query.WriteString("show\n")
|
||||||
|
query.WriteString("send\n")
|
||||||
|
|
||||||
|
return query.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryNameserver(query string) error {
|
||||||
|
var (
|
||||||
|
stdout bytes.Buffer
|
||||||
|
stderr bytes.Buffer
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd := exec.Command("knsupdate", "-v")
|
||||||
|
cmd.Stdin = strings.NewReader(query)
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
fmt.Printf(stdout.String())
|
||||||
|
fmt.Printf(stderr.String())
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNameserver(data userData, addr net.IP) (bool, error) {
|
||||||
|
updateRecord := true
|
||||||
|
deleteRecord := true
|
||||||
|
|
||||||
|
addrs, err := net.LookupIP(data.hostname)
|
||||||
|
if err != nil {
|
||||||
|
deleteRecord = false
|
||||||
|
} else {
|
||||||
|
for _, ip := range addrs {
|
||||||
|
if ip.Equal(addr) {
|
||||||
|
updateRecord = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !updateRecord {
|
||||||
|
return false, nil
|
||||||
|
} else {
|
||||||
|
query := createQuery(data, addr, deleteRecord)
|
||||||
|
err = queryNameserver(query)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue