2021-04-23 14:02:15 +00:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
|
|
|
_ "embed"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"git.cryptic.systems/volker.raschek/dyndns-client/pkg/types"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
//go:embed config.json
|
|
|
|
var defaultConfig string
|
|
|
|
|
|
|
|
// GetDefaultConfiguration returns a default configuration
|
|
|
|
func GetDefaultConfiguration() (*types.Config, error) {
|
|
|
|
cnf := new(types.Config)
|
|
|
|
jsonDecoder := json.NewDecoder(strings.NewReader(defaultConfig))
|
|
|
|
|
|
|
|
err := jsonDecoder.Decode(cnf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to decode default config: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultInterface, err := getDefaultInterfaceByIP()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cnf.Ifaces = []string{defaultInterface.Name}
|
|
|
|
|
|
|
|
return cnf, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read config from a file
|
|
|
|
func Read(cnfFile string) (*types.Config, error) {
|
|
|
|
// Load burned in configuration if config not available
|
|
|
|
if _, err := os.Stat(cnfFile); os.IsNotExist(err) {
|
2022-03-11 10:14:38 +00:00
|
|
|
// #nosec G301
|
2021-04-23 14:02:15 +00:00
|
|
|
if err := os.MkdirAll(filepath.Dir(cnfFile), 0755); err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to create directory: %w", err)
|
|
|
|
}
|
|
|
|
cnf, err := GetDefaultConfiguration()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = cnf.Validate()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Infof("use embedded configuration")
|
|
|
|
|
|
|
|
return cnf, nil
|
|
|
|
}
|
|
|
|
|
2022-03-11 10:14:38 +00:00
|
|
|
// #nosec G304
|
2021-04-23 14:02:15 +00:00
|
|
|
f, err := os.Open(cnfFile)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to open file: %w", err)
|
|
|
|
}
|
2022-03-11 10:14:38 +00:00
|
|
|
defer func() { _ = f.Close() }()
|
2021-04-23 14:02:15 +00:00
|
|
|
|
|
|
|
cnf := new(types.Config)
|
|
|
|
jsonDecoder := json.NewDecoder(f)
|
|
|
|
err = jsonDecoder.Decode(cnf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to decode json: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-04-26 07:56:38 +00:00
|
|
|
if len(cnf.Ifaces) <= 0 {
|
|
|
|
defaultInterface, err := getDefaultInterfaceByIP()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cnf.Ifaces = []string{defaultInterface.Name}
|
|
|
|
}
|
|
|
|
|
2021-04-23 14:02:15 +00:00
|
|
|
for _, iface := range cnf.Ifaces {
|
|
|
|
if _, err := net.InterfaceByName(iface); err != nil {
|
|
|
|
return nil, fmt.Errorf("unknown interface: %v", iface)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = cnf.Validate()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Infof("use configuration from file %v", cnfFile)
|
|
|
|
|
|
|
|
return cnf, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write config into a file
|
|
|
|
func Write(cnf *types.Config, cnfFile string) error {
|
|
|
|
if _, err := os.Stat(filepath.Dir(cnfFile)); os.IsNotExist(err) {
|
2022-03-11 10:14:38 +00:00
|
|
|
// #nosec G301
|
2021-04-23 14:02:15 +00:00
|
|
|
err := os.MkdirAll(filepath.Dir(cnfFile), 0755)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-11 10:14:38 +00:00
|
|
|
// #nosec G304
|
2021-04-23 14:02:15 +00:00
|
|
|
f, err := os.Create(cnfFile)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create file %v: %v", cnfFile, err)
|
|
|
|
}
|
2022-03-11 10:14:38 +00:00
|
|
|
defer func() { _ = f.Close() }()
|
2021-04-23 14:02:15 +00:00
|
|
|
|
|
|
|
jsonEncoder := json.NewEncoder(f)
|
|
|
|
jsonEncoder.SetIndent("", " ")
|
|
|
|
err = jsonEncoder.Encode(cnf)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to encode json: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getDefaultInterfaceByIP() (*net.Interface, error) {
|
|
|
|
ifaces, err := net.Interfaces()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to fet network interfaces from kernel: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultIP := getOutboundIP()
|
|
|
|
|
|
|
|
for _, iface := range ifaces {
|
|
|
|
addrs, err := iface.Addrs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to list ip addresses for interface %v: %w", iface.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, addr := range addrs {
|
|
|
|
addrIP := strings.Split(addr.String(), "/")[0]
|
|
|
|
if addrIP == defaultIP.String() {
|
|
|
|
return &iface, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("no interface found fo ip address %v", defaultIP)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getOutboundIP() net.IP {
|
|
|
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
|
|
|
return localAddr.IP
|
|
|
|
}
|