commit 46ef7e5e7c236c81a2dd63baf0b58efea385cefc
parent 16454d75b6eccf44fd85ca2356de570e16a31964
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Thu, 7 Aug 2025 22:23:29 +0200
implementation draft GNS disseminator
Diffstat:
6 files changed, 225 insertions(+), 100 deletions(-)
diff --git a/cmd/taldir-config/main.go b/cmd/taldir-config/main.go
@@ -57,7 +57,7 @@ func printCfgOptions(sec *ini.Section, option *string, onlyValue *bool) {
}
return
}
- if ! sec.HasKey(*option) {
+ if !sec.HasKey(*option) {
fmt.Printf("Section `%s' does not have option `%s'!\n", sec.Name(), *option)
os.Exit(1)
}
@@ -71,7 +71,6 @@ func printCfgSections(f *ini.File) {
}
}
-
func main() {
var cfg *ini.File
var err error
@@ -95,7 +94,7 @@ func main() {
}
cfgfile := "taldir.conf"
if len(*cfgFlag) != 0 {
- cfg, err = ini.LooseLoad(*cfgFlag)
+ cfg, err = ini.Load(*cfgFlag)
if err != nil {
fmt.Printf("Failed to read config: %v\n", err)
os.Exit(1)
diff --git a/pkg/rest/config.go b/pkg/rest/config.go
@@ -0,0 +1,61 @@
+// This file is part of tdir, the Taler Directory implementation.
+// Copyright (C) 2022 Martin Schanzenbach
+//
+// Taldir is free software: you can redistribute it and/or modify it
+// under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// Taldir is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package taldir
+
+import (
+ "github.com/schanzen/taler-go/pkg/merchant"
+ "gopkg.in/ini.v1"
+ "gorm.io/gorm"
+)
+
+type TaldirConfig struct {
+ // The config to use
+ Ini *ini.File
+
+ // The Taldir Version
+ Version string
+
+ // The Taldir data home location (usually $datadir/taldir)
+ Datahome string
+
+ // The database connection to use
+ Db gorm.Dialector
+
+ // The merchant connection to use
+ Merchant merchant.Merchant
+
+ // The loglevel to use
+ Loglevel LogLevel
+}
+
+type LogLevel int
+
+const (
+ LogError LogLevel = iota
+ LogWarning
+ LogInfo
+ LogDebug
+)
+
+var LoglevelStringMap = map[LogLevel]string{
+ LogDebug: "DEBUG",
+ LogError: "ERROR",
+ LogWarning: "WARN",
+ LogInfo: "INFO",
+}
diff --git a/pkg/rest/db.go b/pkg/rest/db.go
@@ -0,0 +1,42 @@
+// This file is part of tdir, the Taler Directory implementation.
+// Copyright (C) 2025 Martin Schanzenbach
+//
+// Taldir is free software: you can redistribute it and/or modify it
+// under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// Taldir is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package taldir
+
+import (
+ "gorm.io/gorm"
+ "time"
+)
+
+// Entry is a mapping from the identity key hash to a wallet key
+// The identity key hash is sha512(sha512(address)|salt) where identity is
+// one of the identity key types supported (e.g. an email address)
+type Entry struct {
+
+ // ORM
+ gorm.Model `json:"-"`
+
+ // The salted hash (SHA512) of the hashed address (h_address)
+ HsAddress string `json:"-"`
+
+ // Target URI to associate with this address
+ TargetUri string `json:"target_uri"`
+
+ // How long the registration lasts in microseconds
+ Duration time.Duration `json:"-"`
+}
diff --git a/pkg/rest/disseminator.go b/pkg/rest/disseminator.go
@@ -0,0 +1,37 @@
+// This file is part of tdir, the Taler Directory implementation.
+// Copyright (C) 2025 Martin Schanzenbach
+//
+// Taldir is free software: you can redistribute it and/or modify it
+// under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// Taldir is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package taldir
+
+type Disseminator struct {
+
+ // Disseminator name
+ Name string
+
+ // Config disseminator uses
+ Config *TaldirConfig
+
+ // Disseminator function
+ DisseminateStart func(d *Disseminator, e *Entry) error
+
+ // Disseminator function
+ DisseminateStop func(d *Disseminator, e *Entry) error
+
+ // Disseminator active status
+ Enabled bool
+}
diff --git a/pkg/rest/disseminator_gns.go b/pkg/rest/disseminator_gns.go
@@ -0,0 +1,62 @@
+// This file is part of tdir, the Taler Directory implementation.
+// Copyright (C) 2025 Martin Schanzenbach
+//
+// Taldir is free software: you can redistribute it and/or modify it
+// under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License,
+// or (at your option) any later version.
+//
+// Taldir is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// SPDX-License-Identifier: AGPL3.0-or-later
+
+package taldir
+
+import (
+ "fmt"
+ "os/exec"
+)
+
+func gns_disseminate_stop(d *Disseminator, e *Entry) error {
+ path, err := exec.LookPath("gnunet-namestore")
+ if err != nil {
+ return fmt.Errorf("path of command not found: %w", err)
+ }
+ zone := d.Config.Ini.Section("taldir-disseminator-gns").Key("zone").MustString("taldir")
+ out, err := exec.Command(path, "--delete", "--public", "--zone", zone, "--type", "TXT", "--name", e.HsAddress, "--value", e.TargetUri).Output()
+ if err != nil {
+ return fmt.Errorf("failed to execute disseminator command: `%s', %w", out, err)
+ }
+ return nil
+}
+
+func gns_disseminate_start(d *Disseminator, e *Entry) error {
+ path, err := exec.LookPath("gnunet-namestore")
+ if err != nil {
+ return fmt.Errorf("path of command not found: %w", err)
+ }
+ expiration := d.Config.Ini.Section("taldir-disseminator-gns").Key("expiration").MustString("1d")
+ zone := d.Config.Ini.Section("taldir-disseminator-gns").Key("zone").MustString("taldir")
+ out, err := exec.Command(path, "--add", "--public", "--expiration", expiration, "--zone", zone, "--type", "TXT", "--name", e.HsAddress, "--value", e.TargetUri).Output()
+ if err != nil {
+ return fmt.Errorf("failed to execute disseminator command: `%s', %w", out, err)
+ }
+ return nil
+}
+
+func make_gns_disseminator(cfg *TaldirConfig) Disseminator {
+ d := Disseminator{
+ Name: "gns",
+ Config: cfg,
+ DisseminateStart: gns_disseminate_start,
+ DisseminateStop: gns_disseminate_stop,
+ Enabled: cfg.Ini.Section("taldir-disseminator-gns").Key("enabled").MustBool(false),
+ }
+ return d
+}
diff --git a/pkg/rest/taldir.go b/pkg/rest/taldir.go
@@ -46,33 +46,12 @@ import (
tos "github.com/schanzen/taler-go/pkg/rest"
talerutil "github.com/schanzen/taler-go/pkg/util"
"github.com/skip2/go-qrcode"
- "gopkg.in/ini.v1"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"taler.net/taldir/internal/gana"
"taler.net/taldir/internal/util"
)
-type TaldirConfig struct {
- // The config to use
- Ini *ini.File
-
- // The Taldir Version
- Version string
-
- // The Taldir data home location (usually $datadir/taldir)
- Datahome string
-
- // The database connection to use
- Db gorm.Dialector
-
- // The merchant connection to use
- Merchant merchant.Merchant
-
- // The loglevel to use
- Loglevel LogLevel
-}
-
// Taldir is the primary object of the Taldir service
type Taldir struct {
@@ -177,16 +156,6 @@ type Validator struct {
LandingPageTpl *template.Template
}
-type Disseminator struct {
-
- // Disseminator name
- Name string
-
- // The command to call for dissemination
- Command string
-
-}
-
// VersionResponse is the JSON response of the /config endpoint
type VersionResponse struct {
// libtool-style representation of the Merchant protocol version, see
@@ -240,24 +209,6 @@ type RegisterMessage struct {
Duration int64 `json:"duration"`
}
-// Entry is a mapping from the identity key hash to a wallet key
-// The identity key hash is sha512(sha512(address)|salt) where identity is
-// one of the identity key types supported (e.g. an email address)
-type Entry struct {
-
- // ORM
- gorm.Model `json:"-"`
-
- // The salted hash (SHA512) of the hashed address (h_address)
- HsAddress string `json:"-"`
-
- // Target URI to associate with this address
- TargetUri string `json:"target_uri"`
-
- // How long the registration lasts in microseconds
- Duration time.Duration `json:"-"`
-}
-
// Validation is the object created when a registration for an entry is initiated.
// The Validation stores the identity key (sha256(identity)) the secret
// Validation reference. The Validation reference is sent to the identity
@@ -389,21 +340,24 @@ func (t *Taldir) getSingleEntry(w http.ResponseWriter, r *http.Request) {
}
// Disseminate entry
-func (t *Taldir) disseminateEntry(e Entry) error {
+func (t *Taldir) disseminateStop(e Entry) error {
for _, d := range t.Disseminators {
- path, err := exec.LookPath(d.Command)
+ err := d.DisseminateStop(&d, &e)
if err != nil {
- t.Logf(LogError, err.Error()+"\n")
- return fmt.Errorf("path of command not found: %w", err)
+ t.Logf(LogWarning, "Dissemination stop failed for disseminator `%s' and entry `%s'", d.Name, e.HsAddress)
}
- t.Logf(LogDebug, "Found `%s' in path as `%s'\n", d.Command, path)
- out, err := exec.Command(path, e.HsAddress, e.TargetUri).Output()
- t.Logf(LogDebug, "Executing `%s %s %s `\n", path, e.HsAddress, e.TargetUri)
+ }
+ return nil
+}
+
+// Disseminate entry
+func (t *Taldir) disseminateStart(e Entry) {
+ for _, d := range t.Disseminators {
+ err := d.DisseminateStart(&d, &e)
if err != nil {
- return fmt.Errorf("failed to execute disseminator command: `%s', %w", out, err)
+ t.Logf(LogWarning, "Dissemination start failed for disseminator `%s' and entry `%s': %v", d.Name, e.HsAddress, err)
}
}
- return nil
}
// Disseminate all entries
@@ -411,10 +365,7 @@ func (t *Taldir) disseminateEntries() error {
var entries []Entry
t.Db.Where("1 = 1").Find(&entries)
for _, e := range entries {
- err := t.disseminateEntry(e)
- if err != nil {
- return fmt.Errorf("failed to disseminate `%s': %w", e.HsAddress, err)
- }
+ t.disseminateStart(e)
}
return nil
}
@@ -491,9 +442,10 @@ func (t *Taldir) validationRequest(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
return
}
+ t.disseminateStop(entry)
} else {
t.Db.Save(&entry)
- t.disseminateEntry(entry)
+ t.disseminateStart(entry)
}
} else {
if validation.TargetUri == "" {
@@ -992,24 +944,8 @@ func (t *Taldir) getFileName(relativeFileName string) string {
return relativeFileName
}
-type LogLevel int
-
-const (
- LogError LogLevel = iota
- LogWarning
- LogInfo
- LogDebug
-)
-
-var LoglevelStringMap = map[LogLevel]string{
- LogDebug: "DEBUG",
- LogError: "ERROR",
- LogWarning: "WARN",
- LogInfo: "INFO",
-}
-
func (t *Taldir) Logf(loglevel LogLevel, fmt string, args ...any) {
- if loglevel < t.Cfg.Loglevel {
+ if loglevel > t.Cfg.Loglevel {
return
}
t.Logger.SetPrefix("taler-directory - " + LoglevelStringMap[loglevel] + " ")
@@ -1070,23 +1006,10 @@ func (t *Taldir) Initialize(cfg TaldirConfig) {
}
}
t.Disseminators = make(map[string]Disseminator)
- for _, sec := range cfg.Ini.Sections() {
- if !strings.HasPrefix(sec.Name(), "taldir-disseminator-") {
- continue
- }
- dname := strings.TrimPrefix(sec.Name(), "taldir-disseminator-")
- if !sec.HasKey("enabled") {
- t.Logger.Printf("`enabled` key in section `[%s]` not found, disabling disseminator.\n", sec.Name())
- continue
- }
- if !sec.Key("enabled").MustBool(false) {
- t.Logger.Printf("`%s` disseminator disabled.\n", dname)
- continue
- }
- t.Disseminators[dname] = Disseminator{
- Name: dname,
- Command: sec.Key("command").MustString(""),
- }
+ gnsdisseminator := make_gns_disseminator(&cfg)
+ if gnsdisseminator.Enabled {
+ t.Disseminators[gnsdisseminator.Name] = gnsdisseminator
+ t.Logger.Printf("Disseminator `%s' enabled.\n", gnsdisseminator.Name)
}
t.ChallengeBytes = cfg.Ini.Section("taldir").Key("challenge_bytes").MustInt(16)
t.ValidationInitiationMax = cfg.Ini.Section("taldir").Key("validation_initiation_max").MustInt64(3)
@@ -1189,4 +1112,5 @@ func (t *Taldir) Initialize(cfg TaldirConfig) {
}
t.CurrencySpec = currencySpec
t.setupHandlers()
+ t.disseminateEntries()
}