package updater import ( "bufio" "bytes" "context" "fmt" "os/exec" "strings" "git.cryptic.systems/volker.raschek/dyndns-client/pkg/types" ) type Updater interface { AddRecord(ctx context.Context, name string, ttl uint, recordType string, data string) error DeleteRecord(ctx context.Context, name string, recordType string) error PruneRecords(ctx context.Context, name string) error } type NSUpdate struct { server string tsigKey *types.TSIGKey } func (u *NSUpdate) AddRecord(ctx context.Context, name string, ttl uint, recordType string, data string) error { nsUpdateCmd := "" switch recordType { case "A", "AAAA": nsUpdateCmd = fmt.Sprintf("update add %v %v IN %v %v", name, ttl, recordType, data) case "TXT": nsUpdateCmd = fmt.Sprintf("update add %v %v IN %v \"%v\"", name, ttl, recordType, data) default: return fmt.Errorf("RecordType %v not supported", recordType) } return u.execute(ctx, nsUpdateCmd) } func (u *NSUpdate) DeleteRecord(ctx context.Context, name string, recordType string) error { nsUpdateCmd := fmt.Sprintf("update delete %v IN %v", name, recordType) return u.execute(ctx, nsUpdateCmd) } func (u *NSUpdate) execute(ctx context.Context, nsUpdateCmd string) error { body := fmt.Sprintf("server %v\n%v\nsend\nquit", u.server, nsUpdateCmd) errBuffer := new(bytes.Buffer) // #nosec G204 cmd := exec.CommandContext(ctx, "nsupdate", "-y", fmt.Sprintf("%v:%v:%v", u.tsigKey.Algorithm, u.tsigKey.Name, u.tsigKey.Secret)) // cmd.Stdout = os.Stdout cmd.Stderr = bufio.NewWriter(errBuffer) cmd.Stdin = strings.NewReader(body) err := cmd.Run() if err != nil { return fmt.Errorf("nsupdate error %w: %v", err, errBuffer.String()) } return nil } func (u *NSUpdate) PruneRecords(ctx context.Context, name string) error { nsUpdateCmd := fmt.Sprintf("update delete %v", name) return u.execute(ctx, nsUpdateCmd) } func NewNSUpdate(server string, tsigKey *types.TSIGKey) (Updater, error) { return &NSUpdate{ server: server, tsigKey: tsigKey, }, nil }