1 parent
952587df4f
commit
efcb19aa2f
19 files changed
+276
-189
No files matched your search
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,35 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"dns_service/settings"
|
||||
"dns_service/utils"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Settings(w http.ResponseWriter, req *http.Request) {
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
respSettings, err := settings.GetSettings()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
utils.RespondWithJSON(w, respSettings, http.StatusOK)
|
||||
case "POST":
|
||||
var newSettings map[string]any
|
||||
if err := json.NewDecoder(req.Body).Decode(&newSettings); err != nil {
|
||||
http.Error(w, "invalid JSON format", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
respSettings, err := settings.SetSettings(newSettings)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
utils.RespondWithJSON(w, respSettings, http.StatusOK)
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"dns_service/utils"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func SettingsFile(w http.ResponseWriter, req *http.Request) {
|
||||
configPath := os.Getenv("DNSMASQ_CONFIG")
|
||||
if configPath == "" {
|
||||
http.Error(w, "DNSMASQ_CONFIG environment variable not set", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to read dnsmasq config file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write(data)
|
||||
case "POST":
|
||||
bodyBytes, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to read request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := os.WriteFile(configPath, bodyBytes, 0644); err != nil {
|
||||
http.Error(w, "failed to write dnsmasq config file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := utils.RestartDnsmasq(); err != nil {
|
||||
http.Error(w, "failed to restart dnsmasq", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write(bodyBytes)
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
//go:embed frontend/*
|
||||
var Frontend embed.FS
|
||||
|
||||
func SpaHandler(fs http.FileSystem) http.Handler {
|
||||
fileServer := http.FileServer(fs)
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("/frontend" + r.URL.Path)
|
||||
r.URL.Path = "/frontend" + path.Clean(r.URL.Path)
|
||||
if _, err := fs.Open(path.Clean(r.URL.Path)); err != nil {
|
||||
r.URL.Path = "/"
|
||||
}
|
||||
fileServer.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -1,111 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"dns_service/settings"
|
||||
"dns_service/utils"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"dns_service/api"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func Settings(w http.ResponseWriter, req *http.Request) {
|
||||
if req.Method == "GET" {
|
||||
resp_settings, err := settings.GetSettings()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
jsondata, err := json.Marshal(resp_settings)
|
||||
if err != nil {
|
||||
http.Error(w, "Error json marshal settings", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, "%s", string(jsondata))
|
||||
} else if req.Method == "POST" {
|
||||
new_settings := make(map[string]any)
|
||||
decoder := json.NewDecoder(req.Body)
|
||||
decoder.Decode(&new_settings)
|
||||
old_settings, err := settings.GetSettings()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
resp_settings, err := settings.SetSettings(new_settings, old_settings)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
jsondata, err := json.Marshal(resp_settings)
|
||||
if err != nil {
|
||||
http.Error(w, "Error json marshal settings", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Println(new_settings, old_settings, resp_settings)
|
||||
fmt.Fprintf(w, "%s", string(jsondata))
|
||||
}
|
||||
}
|
||||
|
||||
func SettingsFile(w http.ResponseWriter, req *http.Request) {
|
||||
if req.Method == "GET" {
|
||||
data, e := os.ReadFile(os.Getenv("DNSMASQ_CONFIG"))
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
fmt.Fprintf(w, "%s", string(data))
|
||||
} else if req.Method == "POST" {
|
||||
bodyBytes, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "Error reading request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
err = os.WriteFile(os.Getenv("DNSMASQ_CONFIG"), bodyBytes, 0644)
|
||||
if err != nil {
|
||||
http.Error(w, "Error writing dnsmasq config file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(bodyBytes))
|
||||
utils.RestartDnsmasq()
|
||||
exec.Command("rc", "restart", "dnsmasq").Run()
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
fmt.Fprintf(w, "%s", string(bodyBytes))
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed frontend/*
|
||||
var frontend embed.FS
|
||||
|
||||
func spaHandler(fs http.FileSystem) http.Handler {
|
||||
fileServer := http.FileServer(fs)
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("/frontend" + r.URL.Path)
|
||||
r.URL.Path = "/frontend" + path.Clean(r.URL.Path)
|
||||
if _, err := fs.Open(path.Clean(r.URL.Path)); err != nil {
|
||||
r.URL.Path = "/"
|
||||
}
|
||||
fileServer.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load("/etc/dns_service/dns_service.conf")
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
|
||||
http.Handle("/", spaHandler(http.FS(frontend)))
|
||||
http.HandleFunc("/api/settings", Settings)
|
||||
http.HandleFunc("/api/settings/file", SettingsFile)
|
||||
http.Handle("/", api.SpaHandler(http.FS(api.Frontend)))
|
||||
http.HandleFunc("/api/settings", api.Settings)
|
||||
http.HandleFunc("/api/settings/file", api.SettingsFile)
|
||||
|
||||
if os.Getenv("ENABLE_TLS") == "true" {
|
||||
http.ListenAndServeTLS(
|
||||
|
||||
+36
-17
@@ -2,32 +2,51 @@ package settings
|
||||
|
||||
import (
|
||||
"dns_service/utils"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func GetSettings() (map[string]any, error) {
|
||||
settings := make(map[string]any)
|
||||
data, e := os.ReadFile(os.Getenv("DNSMASQ_CONFIG"))
|
||||
if e != nil {
|
||||
return settings, e
|
||||
configPath := os.Getenv("DNSMASQ_CONFIG")
|
||||
if configPath == "" {
|
||||
return nil, fmt.Errorf("DNSMASQ_CONFIG environment variable not set")
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read config file at %s: %w", configPath, err)
|
||||
}
|
||||
|
||||
dnsmasqSettings := utils.DecodeSettings(string(data), "\n", "=")
|
||||
if dnsmasqSettings["cache-size"] != "" {
|
||||
cache_size, _ := strconv.ParseInt(dnsmasqSettings["cache-size"], 10, 64)
|
||||
settings["cache_size"] = cache_size
|
||||
settings := make(map[string]any)
|
||||
|
||||
// Parse cache size
|
||||
if cacheSizeStr, ok := dnsmasqSettings["cache-size"]; ok && cacheSizeStr != "" {
|
||||
cacheSize, err := strconv.ParseInt(cacheSizeStr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse cache-size '%s': %w", cacheSizeStr, err)
|
||||
}
|
||||
settings["cache_size"] = cacheSize
|
||||
}
|
||||
data, e = os.ReadFile(dnsmasqSettings["resolv-file"])
|
||||
if e != nil {
|
||||
return settings, e
|
||||
|
||||
// Read and parse resolv file
|
||||
if resolvFile, ok := dnsmasqSettings["resolv-file"]; ok {
|
||||
data, err := os.ReadFile(resolvFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read resolv-file at %s: %w", resolvFile, err)
|
||||
}
|
||||
settings["resolv"] = utils.DecodeResolvs(string(data))
|
||||
}
|
||||
resolvSettings := utils.DecodeResolvs(string(data))
|
||||
settings["resolv"] = resolvSettings
|
||||
data, e = os.ReadFile(dnsmasqSettings["addn-hosts"])
|
||||
if e != nil {
|
||||
return settings, e
|
||||
|
||||
// Read and parse hosts file
|
||||
if hostsFile, ok := dnsmasqSettings["addn-hosts"]; ok {
|
||||
data, err := os.ReadFile(hostsFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read addn-hosts file at %s: %w", hostsFile, err)
|
||||
}
|
||||
settings["hosts"] = utils.DecodeHosts(string(data))
|
||||
}
|
||||
hosts := utils.DecodeHosts(string(data))
|
||||
settings["hosts"] = hosts
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
+74
-39
@@ -7,51 +7,86 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func SetSettings(new_settings map[string]any, old_settings map[string]any) (map[string]any, error) {
|
||||
data, err := os.ReadFile(os.Getenv("DNSMASQ_CONFIG"))
|
||||
// Helper to write file content
|
||||
func writeFile(path, content string) error {
|
||||
return os.WriteFile(path, []byte(content), 0644)
|
||||
}
|
||||
|
||||
func SetSettings(new_settings map[string]any) (map[string]any, error) {
|
||||
// 1. Get config path and read the main dnsmasq configuration
|
||||
configPath := os.Getenv("DNSMASQ_CONFIG")
|
||||
if configPath == "" {
|
||||
return nil, fmt.Errorf("DNSMASQ_CONFIG environment variable not set")
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return old_settings, err
|
||||
return nil, fmt.Errorf("failed to read config file at %s: %w", configPath, err)
|
||||
}
|
||||
new_data := strings.Replace(
|
||||
string(data),
|
||||
"cache-size="+fmt.Sprintf("%v", old_settings["cache_size"]),
|
||||
"cache-size="+fmt.Sprintf("%v", new_settings["cache_size"]),
|
||||
1,
|
||||
)
|
||||
err = os.WriteFile(os.Getenv("DNSMASQ_CONFIG"), []byte(new_data), 0644)
|
||||
if err != nil {
|
||||
return old_settings, err
|
||||
}
|
||||
var resolvs string
|
||||
new_resolvs := new_settings["resolv"].([]any)
|
||||
for i := range new_resolvs {
|
||||
resolvs += "nameserver " + new_resolvs[i].(string) + "\n"
|
||||
}
|
||||
var hosts string
|
||||
new_hosts := new_settings["hosts"].(map[string]any)
|
||||
for key, value := range new_hosts {
|
||||
hosts += key
|
||||
switch value.(type) {
|
||||
case []any:
|
||||
arrvalue := value.([]any)
|
||||
for i := range arrvalue {
|
||||
hosts += " " + arrvalue[i].(string)
|
||||
dnsmasqSettings := utils.DecodeSettings(string(data), "\n", "=")
|
||||
|
||||
// 2. Update cache-size in the main config file
|
||||
if newCacheSize, ok := new_settings["cache_size"]; ok {
|
||||
oldCacheSize, oldOk := dnsmasqSettings["cache-size"]
|
||||
// Replace the line if it exists, otherwise dnsmasq uses a default
|
||||
if oldOk {
|
||||
newConfigContent := strings.Replace(
|
||||
string(data),
|
||||
"cache-size="+oldCacheSize,
|
||||
"cache-size="+fmt.Sprintf("%v", newCacheSize),
|
||||
1,
|
||||
)
|
||||
if err := writeFile(configPath, newConfigContent); err != nil {
|
||||
return nil, fmt.Errorf("failed to write updated config to %s: %w", configPath, err)
|
||||
}
|
||||
hosts += "\n"
|
||||
break
|
||||
case string:
|
||||
hosts += " " + value.(string) + "\n"
|
||||
}
|
||||
}
|
||||
fmt.Println(resolvs, hosts)
|
||||
err = os.WriteFile("/etc/dnsmasq.conf.d/resolv.conf", []byte(resolvs), 0644)
|
||||
if err != nil {
|
||||
return old_settings, err
|
||||
|
||||
// 3. Update the resolv file
|
||||
if newResolvs, ok := new_settings["resolv"].([]any); ok {
|
||||
resolvFile, ok := dnsmasqSettings["resolv-file"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("resolv-file path not found in dnsmasq config")
|
||||
}
|
||||
var b strings.Builder
|
||||
for _, v := range newResolvs {
|
||||
if s, ok := v.(string); ok {
|
||||
fmt.Fprintf(&b, "nameserver %s\n", s)
|
||||
}
|
||||
}
|
||||
if err := writeFile(resolvFile, b.String()); err != nil {
|
||||
return nil, fmt.Errorf("failed to write to resolv file %s: %w", resolvFile, err)
|
||||
}
|
||||
}
|
||||
err = os.WriteFile("/etc/dnsmasq.conf.d/hosts", []byte(hosts), 0644)
|
||||
if err != nil {
|
||||
return old_settings, err
|
||||
|
||||
// 4. Update the hosts file
|
||||
if newHosts, ok := new_settings["hosts"].(map[string]any); ok {
|
||||
hostsFile, ok := dnsmasqSettings["addn-hosts"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("addn-hosts path not found in dnsmasq config")
|
||||
}
|
||||
var b strings.Builder
|
||||
for key, value := range newHosts {
|
||||
b.WriteString(key)
|
||||
switch v := value.(type) {
|
||||
case []any:
|
||||
for _, ip := range v {
|
||||
fmt.Fprintf(&b, " %s", ip)
|
||||
}
|
||||
b.WriteString("\n")
|
||||
case string:
|
||||
fmt.Fprintf(&b, " %s\n", v)
|
||||
}
|
||||
}
|
||||
if err := writeFile(hostsFile, b.String()); err != nil {
|
||||
return nil, fmt.Errorf("failed to write to hosts file %s: %w", hostsFile, err)
|
||||
}
|
||||
}
|
||||
utils.RestartDnsmasq()
|
||||
|
||||
// 5. Restart dnsmasq to apply changes
|
||||
if err := utils.RestartDnsmasq(); err != nil {
|
||||
return nil, fmt.Errorf("failed to restart dnsmasq: %w", err)
|
||||
}
|
||||
|
||||
return new_settings, nil
|
||||
}
|
||||
+12
-19
@@ -4,28 +4,21 @@ import "strings"
|
||||
|
||||
func DecodeHosts(input string) map[string]any {
|
||||
result := make(map[string]any)
|
||||
pairs := strings.SplitSeq(input, "\n")
|
||||
lines := strings.Split(input, "\n")
|
||||
|
||||
for pair := range pairs {
|
||||
if pair == "" {
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 0 {
|
||||
continue
|
||||
}
|
||||
kv := strings.Split(pair, " ")
|
||||
if len(kv) == 2 {
|
||||
key := strings.TrimSpace(kv[0])
|
||||
value := strings.TrimSpace(kv[1])
|
||||
if key != "" {
|
||||
result[key] = value
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(kv) != 0 {
|
||||
key := strings.TrimSpace(kv[0])
|
||||
var value []string
|
||||
for i := 1; i < len(kv); i++ {
|
||||
value = append(value, kv[i])
|
||||
}
|
||||
result[key] = value
|
||||
|
||||
key := fields[0]
|
||||
if len(fields) == 1 {
|
||||
result[key] = []string{}
|
||||
} else if len(fields) == 2 {
|
||||
result[key] = fields[1]
|
||||
} else {
|
||||
result[key] = fields[1:]
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
+8
-12
@@ -4,19 +4,15 @@ import "strings"
|
||||
|
||||
func DecodeResolvs(input string) []string {
|
||||
var result []string
|
||||
pairs := strings.SplitSeq(input, "\n")
|
||||
lines := strings.Split(input, "\n")
|
||||
|
||||
for pair := range pairs {
|
||||
if pair == "" {
|
||||
continue
|
||||
}
|
||||
kv := strings.SplitN(pair, " ", 2)
|
||||
if len(kv) != 2 {
|
||||
continue
|
||||
}
|
||||
value := strings.TrimSpace(kv[1])
|
||||
if value != "" {
|
||||
result = append(result, value)
|
||||
for _, line := range lines {
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmedLine, "nameserver ") {
|
||||
value := strings.TrimPrefix(trimmedLine, "nameserver ")
|
||||
if value != "" {
|
||||
result = append(result, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -4,20 +4,22 @@ import "strings"
|
||||
|
||||
func DecodeSettings(input string, pairDelim string, kvDelim string) map[string]string {
|
||||
result := make(map[string]string)
|
||||
pairs := strings.SplitSeq(input, pairDelim)
|
||||
pairs := strings.Split(input, pairDelim)
|
||||
|
||||
for pair := range pairs {
|
||||
if pair == "" {
|
||||
for _, pair := range pairs {
|
||||
trimmedPair := strings.TrimSpace(pair)
|
||||
if trimmedPair == "" {
|
||||
continue
|
||||
}
|
||||
kv := strings.SplitN(pair, kvDelim, 2)
|
||||
|
||||
kv := strings.SplitN(trimmedPair, kvDelim, 2)
|
||||
if len(kv) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(kv[0])
|
||||
value := strings.TrimSpace(kv[1])
|
||||
if key != "" {
|
||||
result[key] = value
|
||||
result[key] = strings.TrimSpace(kv[1])
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Helper to write JSON responses
|
||||
func RespondWithJSON(w http.ResponseWriter, data any, statusCode int) {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to marshal JSON", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(statusCode)
|
||||
w.Write(jsonData)
|
||||
}
|
||||
@@ -1,17 +1,26 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func RestartDnsmasq() {
|
||||
func RestartDnsmasq() error {
|
||||
var cmd *exec.Cmd
|
||||
switch os.Getenv("SYSTEM_INITIALIZATION") {
|
||||
case "rc":
|
||||
exec.Command("rc", "restart", "dnsmasq").Run()
|
||||
cmd = exec.Command("rc", "restart", "dnsmasq")
|
||||
case "openrc":
|
||||
exec.Command("rc-service", "dnsmasq", "restart").Run()
|
||||
cmd = exec.Command("rc-service", "dnsmasq", "restart")
|
||||
case "systemd":
|
||||
exec.Command("systemctl", "restart", "dnsmasq").Run()
|
||||
cmd = exec.Command("systemctl", "restart", "dnsmasq")
|
||||
default:
|
||||
return fmt.Errorf("unsupported system initialization: %s", os.Getenv("SYSTEM_INITIALIZATION"))
|
||||
}
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to restart dnsmasq: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user