itAdd integration example in cmd/tor-dam. - tordam - A library for peer discovery inside the Tor network Err parazyd.org 70 hgit clone https://git.parazyd.org/tordam URL:https://git.parazyd.org/tordam parazyd.org 70 1Log /git/tordam/log.gph parazyd.org 70 1Files /git/tordam/files.gph parazyd.org 70 1Refs /git/tordam/refs.gph parazyd.org 70 1README /git/tordam/file/README.md.gph parazyd.org 70 1LICENSE /git/tordam/file/LICENSE.gph parazyd.org 70 i--- Err parazyd.org 70 1commit 9a3c22f8e25ae7dd29c5c5869191ad8d5ca8d233 /git/tordam/commit/9a3c22f8e25ae7dd29c5c5869191ad8d5ca8d233.gph parazyd.org 70 1parent 2f66ffd8201aa31aba279822a292125485bffe51 /git/tordam/commit/2f66ffd8201aa31aba279822a292125485bffe51.gph parazyd.org 70 hAuthor: parazyd URL:mailto:parazyd@dyne.org parazyd.org 70 iDate: Sun, 7 Mar 2021 21:31:59 +0100 Err parazyd.org 70 i Err parazyd.org 70 iAdd integration example in cmd/tor-dam. Err parazyd.org 70 i Err parazyd.org 70 iDiffstat: Err parazyd.org 70 i M README.md | 3 +++ Err parazyd.org 70 i M announce_test.go | 4 ++-- Err parazyd.org 70 i A cmd/tor-dam/tor-dam.go | 187 +++++++++++++++++++++++++++++++ Err parazyd.org 70 i M config.go | 10 ++++------ Err parazyd.org 70 i M go.mod | 5 +++++ Err parazyd.org 70 i A go.sum | 18 ++++++++++++++++++ Err parazyd.org 70 i M rpc_announce.go | 6 +++--- Err parazyd.org 70 i Err parazyd.org 70 i7 files changed, 222 insertions(+), 11 deletions(-) Err parazyd.org 70 i--- Err parazyd.org 70 1diff --git a/README.md b/README.md /git/tordam/file/README.md.gph parazyd.org 70 it@@ -20,3 +20,6 @@ https://pkg.go.dev/github.com/parazyd/tordam Err parazyd.org 70 i Err parazyd.org 70 i tor-dam is a small library that can be used to facilitate peer to peer Err parazyd.org 70 i services in the Tor network with simple mechanisms. Err parazyd.org 70 i+ Err parazyd.org 70 i+A basic integration example can be found and reviewed in the form of Err parazyd.org 70 i+a single go file: [cmd/tor-dam/tor-dam.go](cmd/tor-dam/tor-dam.go). Err parazyd.org 70 1diff --git a/announce_test.go b/announce_test.go /git/tordam/file/announce_test.go.gph parazyd.org 70 it@@ -37,7 +37,7 @@ func TestAnnounce(t *testing.T) { Err parazyd.org 70 i "12345:54321,666:3521", Err parazyd.org 70 i } Err parazyd.org 70 i Err parazyd.org 70 i- ret, err := ann.Init(ann{}, context.Background(), vals) Err parazyd.org 70 i+ ret, err := Ann.Init(Ann{}, context.Background(), vals) Err parazyd.org 70 i if err != nil { Err parazyd.org 70 i t.Fatal(err) Err parazyd.org 70 i } Err parazyd.org 70 it@@ -52,7 +52,7 @@ func TestAnnounce(t *testing.T) { Err parazyd.org 70 i base64.StdEncoding.EncodeToString(ed25519.Sign(sk, []byte(ret[0]))), Err parazyd.org 70 i } Err parazyd.org 70 i Err parazyd.org 70 i- ret, err = ann.Validate(ann{}, context.Background(), vals) Err parazyd.org 70 i+ ret, err = Ann.Validate(Ann{}, context.Background(), vals) Err parazyd.org 70 i if err != nil { Err parazyd.org 70 i t.Fatal(err) Err parazyd.org 70 i } Err parazyd.org 70 1diff --git a/cmd/tor-dam/tor-dam.go b/cmd/tor-dam/tor-dam.go /git/tordam/file/cmd/tor-dam/tor-dam.go.gph parazyd.org 70 it@@ -0,0 +1,187 @@ Err parazyd.org 70 i+// Copyright (c) 2017-2021 Ivan Jelincic Err parazyd.org 70 i+// Err parazyd.org 70 i+// This file is part of tordam Err parazyd.org 70 i+// Err parazyd.org 70 i+// This program is free software: you can redistribute it and/or modify Err parazyd.org 70 i+// it under the terms of the GNU Affero General Public License as published by Err parazyd.org 70 i+// the Free Software Foundation, either version 3 of the License, or Err parazyd.org 70 i+// (at your option) any later version. Err parazyd.org 70 i+// Err parazyd.org 70 i+// This program is distributed in the hope that it will be useful, Err parazyd.org 70 i+// but WITHOUT ANY WARRANTY; without even the implied warranty of Err parazyd.org 70 i+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Err parazyd.org 70 i+// GNU Affero General Public License for more details. Err parazyd.org 70 i+// Err parazyd.org 70 i+// You should have received a copy of the GNU Affero General Public License Err parazyd.org 70 i+// along with this program. If not, see . Err parazyd.org 70 i+ Err parazyd.org 70 i+package main Err parazyd.org 70 i+ Err parazyd.org 70 i+import ( Err parazyd.org 70 i+ "crypto/ed25519" Err parazyd.org 70 i+ "crypto/rand" Err parazyd.org 70 i+ "encoding/base64" Err parazyd.org 70 i+ "flag" Err parazyd.org 70 i+ "io/ioutil" Err parazyd.org 70 i+ "log" Err parazyd.org 70 i+ "net" Err parazyd.org 70 i+ "os" Err parazyd.org 70 i+ "strings" Err parazyd.org 70 i+ "sync" Err parazyd.org 70 i+ "time" Err parazyd.org 70 i+ Err parazyd.org 70 i+ "github.com/creachadair/jrpc2" Err parazyd.org 70 i+ "github.com/creachadair/jrpc2/handler" Err parazyd.org 70 i+ "github.com/creachadair/jrpc2/server" Err parazyd.org 70 i+ "github.com/parazyd/tordam" Err parazyd.org 70 i+) Err parazyd.org 70 i+ Err parazyd.org 70 i+var ( Err parazyd.org 70 i+ generate = flag.Bool("g", false, "(Re)generate keys and exit") Err parazyd.org 70 i+ portmap = flag.String("m", "13010:13010,13011:13011", "Map of ports forwarded to/from Tor") Err parazyd.org 70 i+ listen = flag.String("l", "127.0.0.1:49371", "Local listen address") Err parazyd.org 70 i+ datadir = flag.String("datadir", os.Getenv("HOME")+"/.dam", "Data directory") Err parazyd.org 70 i+ seeds = flag.String("s", Err parazyd.org 70 i+ "p7qaewjgnvnaeihhyybmoofd5avh665kr3awoxlh5rt6ox743kjdr6qd.onion:49371", Err parazyd.org 70 i+ "List of initial peers (comma-separated)") Err parazyd.org 70 i+ noannounce = flag.Bool("n", false, "Do not announce to peers") Err parazyd.org 70 i+) Err parazyd.org 70 i+ Err parazyd.org 70 i+func generateED25519Keypair(dir string) error { Err parazyd.org 70 i+ _, sk, err := ed25519.GenerateKey(rand.Reader) Err parazyd.org 70 i+ if err != nil { Err parazyd.org 70 i+ return err Err parazyd.org 70 i+ } Err parazyd.org 70 i+ if err := os.MkdirAll(dir, 0700); err != nil { Err parazyd.org 70 i+ return err Err parazyd.org 70 i+ } Err parazyd.org 70 i+ Err parazyd.org 70 i+ seedpath := strings.Join([]string{dir, "ed25519.seed"}, "/") Err parazyd.org 70 i+ log.Println("Writing ed25519 key seed to", seedpath) Err parazyd.org 70 i+ return ioutil.WriteFile(seedpath, Err parazyd.org 70 i+ []byte(base64.StdEncoding.EncodeToString(sk.Seed())), 0600) Err parazyd.org 70 i+} Err parazyd.org 70 i+ Err parazyd.org 70 i+func loadED25519Seed(file string) (ed25519.PrivateKey, error) { Err parazyd.org 70 i+ log.Println("Reading ed25519 seed from", file) Err parazyd.org 70 i+ Err parazyd.org 70 i+ data, err := ioutil.ReadFile(file) Err parazyd.org 70 i+ if err != nil { Err parazyd.org 70 i+ return nil, err Err parazyd.org 70 i+ } Err parazyd.org 70 i+ dec, err := base64.StdEncoding.DecodeString(string(data)) Err parazyd.org 70 i+ if err != nil { Err parazyd.org 70 i+ return nil, err Err parazyd.org 70 i+ } Err parazyd.org 70 i+ return ed25519.NewKeyFromSeed(dec), nil Err parazyd.org 70 i+} Err parazyd.org 70 i+ Err parazyd.org 70 i+func main() { Err parazyd.org 70 i+ flag.Parse() Err parazyd.org 70 i+ var wg sync.WaitGroup Err parazyd.org 70 i+ var err error Err parazyd.org 70 i+ Err parazyd.org 70 i+ if *generate { Err parazyd.org 70 i+ if err := generateED25519Keypair(*datadir); err != nil { Err parazyd.org 70 i+ log.Fatal(err) Err parazyd.org 70 i+ } Err parazyd.org 70 i+ os.Exit(0) Err parazyd.org 70 i+ } Err parazyd.org 70 i+ Err parazyd.org 70 i+ // Validate given seeds Err parazyd.org 70 i+ for _, i := range strings.Split(*seeds, ",") { Err parazyd.org 70 i+ if err := tordam.ValidateOnionInternal(i); err != nil { Err parazyd.org 70 i+ log.Fatalf("invalid seed %s (%v)", i, err) Err parazyd.org 70 i+ } Err parazyd.org 70 i+ } Err parazyd.org 70 i+ Err parazyd.org 70 i+ // Assign portmap to tordam Cfg global and validate it Err parazyd.org 70 i+ tordam.Cfg.Portmap = strings.Split(*portmap, ",") Err parazyd.org 70 i+ if err := tordam.ValidatePortmap(tordam.Cfg.Portmap); err != nil { Err parazyd.org 70 i+ log.Fatal(err) Err parazyd.org 70 i+ } Err parazyd.org 70 i+ Err parazyd.org 70 i+ // Validate and assign the local listening address Err parazyd.org 70 i+ tordam.Cfg.Listen, err = net.ResolveTCPAddr("tcp", *listen) Err parazyd.org 70 i+ if err != nil { Err parazyd.org 70 i+ log.Fatal("invalid listen address: %s (%v)", *listen, err) Err parazyd.org 70 i+ } Err parazyd.org 70 i+ Err parazyd.org 70 i+ // Assign the global tordam data directory Err parazyd.org 70 i+ tordam.Cfg.Datadir = *datadir Err parazyd.org 70 i+ Err parazyd.org 70 i+ // Load the ed25519 signing key into the tordam global Err parazyd.org 70 i+ tordam.SignKey, err = loadED25519Seed(strings.Join( Err parazyd.org 70 i+ []string{*datadir, "ed25519.seed"}, "/")) Err parazyd.org 70 i+ if err != nil { Err parazyd.org 70 i+ log.Fatal(err) Err parazyd.org 70 i+ } Err parazyd.org 70 i+ Err parazyd.org 70 i+ // Spawn Tor daemon and let it settle Err parazyd.org 70 i+ tor, err := tordam.SpawnTor(tordam.Cfg.Listen, tordam.Cfg.Portmap, Err parazyd.org 70 i+ tordam.Cfg.Datadir) Err parazyd.org 70 i+ defer tor.Process.Kill() Err parazyd.org 70 i+ if err != nil { Err parazyd.org 70 i+ log.Fatal(err) Err parazyd.org 70 i+ } Err parazyd.org 70 i+ time.Sleep(2 * time.Second) Err parazyd.org 70 i+ log.Println("Started Tor daemon on", tordam.Cfg.TorAddr.String()) Err parazyd.org 70 i+ Err parazyd.org 70 i+ // Read the onion hostname from the datadir and map it into the Err parazyd.org 70 i+ // global tordam.Onion variable Err parazyd.org 70 i+ onionaddr, err := ioutil.ReadFile(strings.Join([]string{ Err parazyd.org 70 i+ tordam.Cfg.Datadir, "hs", "hostname"}, "/")) Err parazyd.org 70 i+ if err != nil { Err parazyd.org 70 i+ log.Fatal(err) Err parazyd.org 70 i+ } Err parazyd.org 70 i+ onionaddr = []byte(strings.TrimSuffix(string(onionaddr), "\n")) Err parazyd.org 70 i+ tordam.Onion = strings.Join([]string{ Err parazyd.org 70 i+ string(onionaddr), string(tordam.Cfg.Listen.Port)}, ":") Err parazyd.org 70 i+ log.Println("Our onion address is:", tordam.Onion) Err parazyd.org 70 i+ Err parazyd.org 70 i+ // Start the JSON-RPC server with announce endpoints. Err parazyd.org 70 i+ // This is done in the library user rather than internally in the library Err parazyd.org 70 i+ // because it is more useful and easier to add additional JSON-RPC Err parazyd.org 70 i+ // endpoints to the same server. Err parazyd.org 70 i+ l, err := net.Listen(jrpc2.Network(tordam.Cfg.Listen.String()), Err parazyd.org 70 i+ tordam.Cfg.Listen.String()) Err parazyd.org 70 i+ if err != nil { Err parazyd.org 70 i+ log.Fatal(err) Err parazyd.org 70 i+ } Err parazyd.org 70 i+ defer l.Close() Err parazyd.org 70 i+ // Endpoints are assigned here Err parazyd.org 70 i+ assigner := handler.ServiceMap{ Err parazyd.org 70 i+ // "ann" is the JSON-RPC endpoint for peer discovery/announcement Err parazyd.org 70 i+ "ann": handler.NewService(tordam.Ann{}), Err parazyd.org 70 i+ } Err parazyd.org 70 i+ go server.Loop(l, server.NewStatic(assigner), nil) Err parazyd.org 70 i+ log.Println("Started JSON-RPC server on", tordam.Cfg.Listen.String()) Err parazyd.org 70 i+ Err parazyd.org 70 i+ // If decided to not announce to anyone Err parazyd.org 70 i+ if *noannounce { Err parazyd.org 70 i+ // We shall sit here and wait Err parazyd.org 70 i+ wg.Add(1) Err parazyd.org 70 i+ wg.Wait() Err parazyd.org 70 i+ } Err parazyd.org 70 i+ Err parazyd.org 70 i+ // Announce to initial seeds Err parazyd.org 70 i+ var succ int = 0 // Track of successful announces Err parazyd.org 70 i+ for _, i := range strings.Split(*seeds, ",") { Err parazyd.org 70 i+ wg.Add(1) Err parazyd.org 70 i+ go func(x string) { Err parazyd.org 70 i+ if err := tordam.Announce(i); err != nil { Err parazyd.org 70 i+ log.Println("error in announce:", err) Err parazyd.org 70 i+ } else { Err parazyd.org 70 i+ succ++ Err parazyd.org 70 i+ } Err parazyd.org 70 i+ wg.Done() Err parazyd.org 70 i+ }(i) Err parazyd.org 70 i+ } Err parazyd.org 70 i+ wg.Wait() Err parazyd.org 70 i+ Err parazyd.org 70 i+ if succ < 1 { Err parazyd.org 70 i+ log.Fatal("No successful announces.") Err parazyd.org 70 i+ } else { Err parazyd.org 70 i+ log.Printf("Successfully announced to %d peers.", succ) Err parazyd.org 70 i+ } Err parazyd.org 70 i+} Err parazyd.org 70 1diff --git a/config.go b/config.go /git/tordam/file/config.go.gph parazyd.org 70 it@@ -24,12 +24,10 @@ import ( Err parazyd.org 70 i Err parazyd.org 70 i // Config is the configuration structure, to be filled by library user. Err parazyd.org 70 i type Config struct { Err parazyd.org 70 i- Listen *net.TCPAddr // Local listen address for the JSON-RPC server Err parazyd.org 70 i- TorAddr *net.TCPAddr // Tor SOCKS5 proxy address, filled by SpawnTor() Err parazyd.org 70 i- Datadir string // Path to data directory Err parazyd.org 70 i- Portmap []string // The peer's portmap, to be mapped in the Tor HS Err parazyd.org 70 i- Seeds []string // Initial peer(s) Err parazyd.org 70 i- Announce bool // Announce or not Err parazyd.org 70 i+ Listen *net.TCPAddr // Local listen address for the JSON-RPC server Err parazyd.org 70 i+ TorAddr *net.TCPAddr // Tor SOCKS5 proxy address, filled by SpawnTor() Err parazyd.org 70 i+ Datadir string // Path to data directory Err parazyd.org 70 i+ Portmap []string // The peer's portmap, to be mapped in the Tor HS Err parazyd.org 70 i } Err parazyd.org 70 i Err parazyd.org 70 i // SignKey is an ed25519 private key, to be assigned by library user. Err parazyd.org 70 1diff --git a/go.mod b/go.mod /git/tordam/file/go.mod.gph parazyd.org 70 it@@ -1,3 +1,8 @@ Err parazyd.org 70 i module github.com/parazyd/tordam Err parazyd.org 70 i Err parazyd.org 70 i go 1.16 Err parazyd.org 70 i+ Err parazyd.org 70 i+require ( Err parazyd.org 70 i+ github.com/creachadair/jrpc2 v0.12.0 // indirect Err parazyd.org 70 i+ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect Err parazyd.org 70 i+) Err parazyd.org 70 1diff --git a/go.sum b/go.sum /git/tordam/file/go.sum.gph parazyd.org 70 it@@ -0,0 +1,18 @@ Err parazyd.org 70 i+bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= Err parazyd.org 70 i+bitbucket.org/creachadair/stringset v0.0.9 h1:L4vld9nzPt90UZNrXjNelTshD74ps4P5NGs3Iq6yN3o= Err parazyd.org 70 i+bitbucket.org/creachadair/stringset v0.0.9/go.mod h1:t+4WcQ4+PXTa8aQdNKe40ZP6iwesoMFWAxPGd3UGjyY= Err parazyd.org 70 i+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= Err parazyd.org 70 i+github.com/creachadair/jrpc2 v0.12.0 h1:cr7QMg8JYLubTneMy7UCQLWPD6LObJ7ZK8T5GeiwbLQ= Err parazyd.org 70 i+github.com/creachadair/jrpc2 v0.12.0/go.mod h1:aACneXzxBPPoiu+nAo5duWP8L4y0//yuHkpkW9uDpo8= Err parazyd.org 70 i+github.com/creachadair/staticfile v0.1.3/go.mod h1:a3qySzCIXEprDGxk6tSxSI+dBBdLzqeBOMhZ+o2d3pM= Err parazyd.org 70 i+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= Err parazyd.org 70 i+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= Err parazyd.org 70 i+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= Err parazyd.org 70 i+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= Err parazyd.org 70 i+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= Err parazyd.org 70 i+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= Err parazyd.org 70 i+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= Err parazyd.org 70 i+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= Err parazyd.org 70 i+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= Err parazyd.org 70 i+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= Err parazyd.org 70 i+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= Err parazyd.org 70 1diff --git a/rpc_announce.go b/rpc_announce.go /git/tordam/file/rpc_announce.go.gph parazyd.org 70 it@@ -26,7 +26,7 @@ import ( Err parazyd.org 70 i "time" Err parazyd.org 70 i ) Err parazyd.org 70 i Err parazyd.org 70 i-type ann struct{} Err parazyd.org 70 i+type Ann struct{} Err parazyd.org 70 i Err parazyd.org 70 i // Init takes three parameters: Err parazyd.org 70 i // - onion: onionaddress:port where the peer and tordam can be reached Err parazyd.org 70 it@@ -46,7 +46,7 @@ type ann struct{} Err parazyd.org 70 i // "result": ["somenonce", "somerevokekey"] Err parazyd.org 70 i // } Err parazyd.org 70 i // On any kind of failure returns an error and the reason. Err parazyd.org 70 i-func (ann) Init(ctx context.Context, vals []string) ([]string, error) { Err parazyd.org 70 i+func (Ann) Init(ctx context.Context, vals []string) ([]string, error) { Err parazyd.org 70 i if len(vals) != 3 && len(vals) != 4 { Err parazyd.org 70 i return nil, errors.New("invalid parameters") Err parazyd.org 70 i } Err parazyd.org 70 it@@ -137,7 +137,7 @@ func (ann) Init(ctx context.Context, vals []string) ([]string, error) { Err parazyd.org 70 i // "result": ["unlikelynameforan.onion:69", "yetanother.onion:420"] Err parazyd.org 70 i // } Err parazyd.org 70 i // On any kind of failure returns an error and the reason. Err parazyd.org 70 i-func (ann) Validate(ctx context.Context, vals []string) ([]string, error) { Err parazyd.org 70 i+func (Ann) Validate(ctx context.Context, vals []string) ([]string, error) { Err parazyd.org 70 i if len(vals) != 2 { Err parazyd.org 70 i return nil, errors.New("invalid parameters") Err parazyd.org 70 i } Err parazyd.org 70 .