URI: 
       tRemove old code. - tordam - A library for peer discovery inside the Tor network
  HTML git clone https://git.parazyd.org/tordam
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 2f8bd41a607d578b727c1c8ee20f10b2cebb1bdc
   DIR parent 625b81777d0403bc36ebada9ecafd04071aef6f3
  HTML Author: parazyd <parazyd@dyne.org>
       Date:   Sun,  7 Mar 2021 20:07:26 +0100
       
       Remove old code.
       
       Diffstat:
         M README.md                           |      49 +++++--------------------------
         D announce.go                         |     179 -------------------------------
         D api.go                              |     191 -------------------------------
         D config.go                           |      36 -------------------------------
         A contrib/tordam.png                  |       0 
         D crypto.go                           |      63 -------------------------------
         D helpers.go                          |      88 -------------------------------
         D net.go                              |      83 -------------------------------
         D redis.go                            |     141 -------------------------------
         D tor-dam.go                          |     191 -------------------------------
         D tor.go                              |      68 -------------------------------
         D types.go                            |      37 -------------------------------
         D validate.go                         |     158 -------------------------------
       
       13 files changed, 8 insertions(+), 1276 deletions(-)
       ---
   DIR diff --git a/README.md b/README.md
       t@@ -1,55 +1,22 @@
        tor-dam (Tor Distributed Announce Mechanism)
        ============================================
        
       -Protocol and tooling for mapping machines in the Tor network running
       -this software.
       +![tordam](contrib/tordam.png)
        
       -![Network visualization](https://raw.githubusercontent.com/parazyd/tor-dam/master/contrib/network.gif)
       +A library for peer discovery inside the Tor network.
        
        
        Installation
        ------------
        
        ```
       -go get github.com/parazyd/tor-dam
       +go get github.com/parazyd/tordam
        ```
        
       -Usage
       ------
       +Documentation
       +-------------
        
       -```
       -Usage of ./tor-dam:
       -  -d string
       -        Working directory (default "/home/parazyd/.dam")
       -  -e int
       -        Node expiry time in minutes (0=unlimited)
       -  -g    (Re)generate keys and exit
       -  -i int
       -        Announce interval (in minutes) (default 5)
       -  -n    Don't fetch remote entrypoints
       -  -p string
       -        Map of ports forwarded to/from Tor (default "13010:13010,13011:13011,5000:5000")
       -  -r string
       -        Remote list of entrypoints (comma-separated) (default "https://parazyd.org/pub/tmp/tor-dam-dirs.txt")
       -  -t    Trust all new nodes automatically
       -```
       +https://pkg.go.dev/github.com/parazyd/tordam
        
       -Protocol
       ---------
       -
       -* Every node has an HTTP API allowing to list other nodes and announce
       -  new ones.
       -* They keep propagating to all trusted nodes they know.
       -* Announcing implies the need of knowledge of at least one node.
       -  * It is possible to make this random enough once there are at least
       -    6 nodes in the network.
       -* A node announces itself to others by sending a JSON-formatted HTTP
       -  POST request to one or more active nodes.
       -  * Once the initial POST request is received, the receiving node will
       -    ACK and return a random string (nonce) back to the requester for
       -    them to sign with their cryptographic key.
       -  * The requester will try to sign this nonce and return it back to
       -    the node it's announcing to, so the node can confirm the requester
       -    is in actual posession of the private key.
       -* tor-dam **does not validate** if a node should be trusted or not.
       -  This is a layer that has to be implemented with external software.
       +tor-dam is a small library that can be used to facilitate peer to peer
       +services in the Tor network with simple mechanisms.
   DIR diff --git a/announce.go b/announce.go
       t@@ -1,179 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -import (
       -        "bytes"
       -        "compress/gzip"
       -        "crypto/ed25519"
       -        "crypto/rand"
       -        "encoding/base64"
       -        "encoding/json"
       -        "io/ioutil"
       -        "log"
       -        "math/big"
       -        "os"
       -        "strings"
       -)
       -
       -func fetchNodeList(epLists []string, remote bool) ([]string, error) {
       -        var ns, nl []string
       -
       -        log.Println("Building a list of nodes")
       -
       -        // Remote network entrypoints
       -        if !remote {
       -                for _, i := range epLists {
       -                        log.Println("Fetching", i)
       -                        n, err := httpGet(i)
       -                        if err != nil {
       -                                return nil, err
       -                        }
       -                        ns = parseDirs(ns, n)
       -                }
       -        }
       -
       -        // Local workdir/dirs.txt
       -        ld := strings.Join([]string{*workdir, "dirs.txt"}, "/")
       -        if _, err := os.Stat(ld); err == nil {
       -                ln, err := ioutil.ReadFile(ld)
       -                if err != nil {
       -                        return nil, err
       -                }
       -                ns = parseDirs(ns, ln)
       -        }
       -
       -        // Local nodes from redis
       -        nodes, _ := rcli.Keys(rctx, "*.onion").Result()
       -        for _, i := range nodes {
       -                valid, err := rcli.HGet(rctx, i, "valid").Result()
       -                if err != nil {
       -                        // Possible RedisCli bug, possible Redis bug. To be investigated.
       -                        // Sometimes it returns err, but it's empty and does not say what's
       -                        // happening exactly.
       -                        continue
       -                }
       -                if valid == "1" {
       -                        ns = append(ns, i)
       -                }
       -        }
       -
       -        // Remove possible dupes. Duplicates can cause race conditions and are
       -        // redundant to the entire logic.
       -        // TODO: Work this in above automatically (by changing the var type)
       -        encounter := map[string]bool{}
       -        for i := range ns {
       -                encounter[ns[i]] = true
       -        }
       -        ns = []string{}
       -        for key := range encounter {
       -                ns = append(ns, key)
       -        }
       -
       -        if len(ns) < 1 {
       -                log.Fatal("Couldn't find any nodes to announce to. Exiting...")
       -        } else if len(ns) <= 6 {
       -                log.Printf("Found %d nodes\n", len(ns))
       -                nl = ns
       -        } else {
       -                log.Printf("Found %d nodes. Picking out 6 at random\n", len(ns))
       -                for i := 0; i <= 5; i++ {
       -                        n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(ns))))
       -                        nl = append(nl, ns[n.Int64()])
       -                        ns[n.Int64()] = ns[len(ns)-1]
       -                        ns = ns[:len(ns)-1]
       -                }
       -        }
       -
       -        return nl, nil
       -}
       -
       -func announce(addr string, vals map[string]string) (bool, error) {
       -        msg, _ := json.Marshal(vals)
       -
       -        log.Println("Announcing keypair to", addr)
       -        resp, err := httpPost("http://"+addr+":49371"+"/announce", msg)
       -        if err != nil {
       -                return false, err
       -        }
       -
       -        // Parse server's reply
       -        var m Message
       -        dec := json.NewDecoder(resp.Body)
       -        if err := dec.Decode(&m); err != nil {
       -                return false, err
       -        }
       -
       -        if resp.StatusCode != 200 {
       -                log.Printf("%s returned error: %s\n", addr, m.Secret)
       -                return false, nil
       -        }
       -
       -        log.Println("Got nonce from", addr)
       -
       -        sig := ed25519.Sign(signingKey, []byte(m.Secret))
       -
       -        vals["secret"] = m.Secret
       -        vals["message"] = m.Secret
       -        vals["signature"] = base64.StdEncoding.EncodeToString(sig)
       -        msg, _ = json.Marshal(vals)
       -
       -        log.Println("Sending back signed secret to", addr)
       -        resp, err = httpPost("http://"+addr+":49371"+"/announce", msg)
       -        if err != nil {
       -                return false, err
       -        }
       -
       -        dec = json.NewDecoder(resp.Body)
       -        if err := dec.Decode(&m); err != nil {
       -                return false, err
       -        }
       -
       -        if resp.StatusCode != 200 {
       -                log.Printf("%s returned error: %s\n", addr, m.Secret)
       -                return false, nil
       -        }
       -
       -        log.Printf("%s handshake valid\n", addr)
       -        data, err := base64.StdEncoding.DecodeString(m.Secret)
       -        if err != nil {
       -                // Not a list of nodes
       -                log.Printf("%s replied: %s\n", addr, m.Secret)
       -                return true, nil
       -        }
       -
       -        log.Println("Got node data, processing...")
       -        b := bytes.NewReader(data)
       -        r, _ := gzip.NewReader(b)
       -        nodes := make(map[string]map[string]interface{})
       -        dec = json.NewDecoder(r)
       -        if err = dec.Decode(&nodes); err != nil {
       -                return false, err
       -        }
       -
       -        for k, v := range nodes {
       -                log.Printf("Adding %s to redis\n", k)
       -                if _, err := rcli.HSet(rctx, k, v).Result(); err != nil {
       -                        log.Fatal(err)
       -                }
       -        }
       -
       -        return true, nil
       -}
   DIR diff --git a/api.go b/api.go
       t@@ -1,191 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -import (
       -        "encoding/json"
       -        "log"
       -        "net/http"
       -        "strings"
       -)
       -
       -func postback(rw http.ResponseWriter, data map[string]string, ret int) error {
       -        val, err := json.Marshal(data)
       -        if err != nil {
       -                return err
       -        }
       -
       -        rw.Header().Set("Content-Type", "application/json")
       -        rw.WriteHeader(ret)
       -        if _, err := rw.Write(val); err != nil {
       -                return err
       -        }
       -        return nil
       -}
       -
       -func handleAnnounce(rw http.ResponseWriter, req *http.Request) {
       -        var r map[string]string
       -        var n Node
       -
       -        if req.Method != "POST" || req.Header["Content-Type"][0] != "application/json" {
       -                r = map[string]string{"secret": "Invalid request format"}
       -                if err := postback(rw, r, 400); err != nil {
       -                        log.Fatal(err)
       -                }
       -                return
       -        }
       -
       -        dec := json.NewDecoder(req.Body)
       -        if err := dec.Decode(&n); err != nil {
       -                log.Println("Failed decoding request:", err)
       -                return
       -        }
       -
       -        // Bail out as soon as possible
       -        if len(n.Address) == 0 || len(n.Message) == 0 || len(n.Signature) == 0 {
       -                r = map[string]string{"secret": "Invalid request format"}
       -                if err := postback(rw, r, 400); err != nil {
       -                        log.Fatal(err)
       -                }
       -                return
       -        }
       -
       -        if !validateOnionAddress(n.Address) {
       -                log.Println("Invalid onion address:", n.Address)
       -                r = map[string]string{"secret": "Invalid onion address"}
       -                if err := postback(rw, r, 400); err != nil {
       -                        log.Fatal(err)
       -                }
       -                return
       -        }
       -
       -        rq := map[string]string{
       -                "address":   n.Address,
       -                "message":   n.Message,
       -                "pubkey":    n.Pubkey,
       -                "signature": n.Signature,
       -                "secret":    n.Secret,
       -        }
       -
       -        // First handshake
       -        if len(n.Message) != 88 || len(n.Secret) != 88 {
       -                valid, msg := firstHandshake(rq)
       -                r = map[string]string{"secret": msg}
       -                if valid {
       -                        log.Printf("%s: 1/2 handskake valid\n", n.Address)
       -                        log.Println("Sending nonce to", n.Address)
       -                        if err := postback(rw, r, 200); err != nil {
       -                                log.Fatal(err)
       -                        }
       -                        return
       -                }
       -                log.Printf("%s: 1/2 handshake invalid: %s\n", n.Address, msg)
       -                // Delete it all from redis
       -                // TODO: Can this be abused?
       -                if _, err := rcli.Del(rctx, n.Address).Result(); err != nil {
       -                        log.Fatal(err)
       -                }
       -                return
       -        }
       -
       -        // Second handshake
       -        if len(rq["secret"]) == 88 && len(rq["message"]) == 88 {
       -                valid, msg := secondHandshake(rq)
       -                r = map[string]string{"secret": msg}
       -
       -                if valid {
       -                        log.Printf("%s: 2/2 handshake valid\n", n.Address)
       -                        isTrusted, err := rcli.HGet(rctx, n.Address, "trusted").Result()
       -                        if err != nil {
       -                                log.Fatal(err)
       -                        }
       -
       -                        // Assume our name is what was requested
       -                        us := strings.TrimSuffix(req.Host, ":49371")
       -                        nodemap := make(map[string]map[string]string)
       -
       -                        if isTrusted == "1" {
       -                                // The node is marked as trusted so we'll teack it about other
       -                                // trusted nodes we know about.
       -                                log.Printf("%s is trusted. Propagating knowledge...\n", n.Address)
       -                                nodes, err := rcli.Keys(rctx, "*.onion").Result()
       -                                if err != nil {
       -                                        log.Fatal(err)
       -                                }
       -                                for _, i := range nodes {
       -                                        if i == n.Address {
       -                                                continue
       -                                        }
       -                                        nodedata, err := rcli.HGetAll(rctx, i).Result()
       -                                        if err != nil {
       -                                                log.Fatal(err)
       -                                        }
       -                                        if nodedata["trusted"] == "1" {
       -                                                nodemap[i] = nodedata
       -                                                delete(nodemap[i], "secret")
       -                                        }
       -                                }
       -                        } else {
       -                                log.Printf("%s is not trusted. Propagating self...", n.Address)
       -                                // The node doesn't have trust in the network. We will only
       -                                // teach it about ourself.
       -                                nodedata, err := rcli.HGetAll(rctx, us).Result()
       -                                if err != nil {
       -                                        log.Fatal(err)
       -                                }
       -                                nodemap[us] = nodedata
       -                                delete(nodemap[us], "secret")
       -                        }
       -
       -                        nodestr, err := json.Marshal(nodemap)
       -                        if err != nil {
       -                                log.Fatal(err)
       -                        }
       -                        comp, err := gzipEncode(nodestr)
       -                        if err != nil {
       -                                log.Fatal(err)
       -                        }
       -                        r = map[string]string{"secret": comp}
       -                        if err := postback(rw, r, 200); err != nil {
       -                                log.Fatal(err)
       -                        }
       -
       -                        publishToRedis('M', n.Address)
       -                        return
       -                }
       -
       -                // If we haven't returned so far, the handshake is invalid
       -                log.Printf("%s: 2/2 handshake invalid\n", n.Address)
       -                // Delete it all from redis
       -                // TODO: Can this be abused?
       -                publishToRedis('D', n.Address)
       -                if _, err := rcli.Del(rctx, n.Address).Result(); err != nil {
       -                        log.Fatal(err)
       -                }
       -                if err := postback(rw, r, 400); err != nil {
       -                        log.Fatal(err)
       -                }
       -                return
       -        }
       -}
       -
       -func handleElse(rw http.ResponseWriter, req *http.Request) {
       -        log.Println("Got handleElse")
       -}
   DIR diff --git a/config.go b/config.go
       t@@ -1,36 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -import (
       -        "crypto/ed25519"
       -        "net"
       -)
       -
       -const (
       -        seedName   = "ed25519.seed"
       -        pubsubChan = "tordam"
       -)
       -
       -var (
       -        redisAddr  *net.TCPAddr
       -        torAddr    *net.TCPAddr
       -        signingKey ed25519.PrivateKey
       -)
   DIR diff --git a/contrib/tordam.png b/contrib/tordam.png
       Binary files differ.
   DIR diff --git a/crypto.go b/crypto.go
       t@@ -1,63 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -import (
       -        "crypto/ed25519"
       -        "crypto/rand"
       -        "encoding/base64"
       -        "io/ioutil"
       -        "log"
       -        "os"
       -        "strings"
       -)
       -
       -func generateED25519Keypair(dir string) error {
       -        _, sk, err := ed25519.GenerateKey(rand.Reader)
       -        if err != nil {
       -                return err
       -        }
       -
       -        if err := os.MkdirAll(dir, 0700); err != nil {
       -                return err
       -        }
       -
       -        seedpath := strings.Join([]string{dir, seedName}, "/")
       -
       -        log.Println("Writing ed25519 key seed to", seedpath)
       -        return ioutil.WriteFile(seedpath,
       -                []byte(base64.StdEncoding.EncodeToString(sk.Seed())), 0600)
       -}
       -
       -func loadED25519Seed(file string) (ed25519.PrivateKey, error) {
       -        log.Println("Reading ed25519 seed from", file)
       -
       -        data, err := ioutil.ReadFile(file)
       -        if err != nil {
       -                return nil, err
       -        }
       -
       -        dec, err := base64.StdEncoding.DecodeString(string(data))
       -        if err != nil {
       -                return nil, err
       -        }
       -
       -        return ed25519.NewKeyFromSeed(dec), nil
       -}
   DIR diff --git a/helpers.go b/helpers.go
       t@@ -1,88 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -import (
       -        "bytes"
       -        "compress/gzip"
       -        "crypto/rand"
       -        "encoding/base64"
       -        "fmt"
       -        "math/big"
       -        "strings"
       -)
       -
       -func genRandomASCII(length int) (string, error) {
       -        var res string
       -        for {
       -                if len(res) == length {
       -                        return res, nil
       -                }
       -                num, err := rand.Int(rand.Reader, big.NewInt(int64(127)))
       -                if err != nil {
       -                        return "", err
       -                }
       -                n := num.Int64()
       -                if n > 32 && n < 127 {
       -                        res += fmt.Sprint(n)
       -                }
       -        }
       -}
       -
       -func gzipEncode(data []byte) (string, error) {
       -        var b bytes.Buffer
       -        gz := gzip.NewWriter(&b)
       -        if _, err := gz.Write(data); err != nil {
       -                return "", err
       -        }
       -        if err := gz.Flush(); err != nil {
       -                return "", err
       -        }
       -        if err := gz.Close(); err != nil {
       -                return "", err
       -        }
       -
       -        return base64.StdEncoding.EncodeToString(b.Bytes()), nil
       -}
       -
       -func stringInSlice(str string, slice []string) bool {
       -        for _, i := range slice {
       -                if str == i {
       -                        return true
       -                }
       -        }
       -        return false
       -}
       -
       -func parseDirs(sl []string, data []byte) []string {
       -        dirstr := string(data)
       -        _dirs := strings.Split(dirstr, "\n")
       -        for _, i := range _dirs {
       -                if strings.HasPrefix(i, "DIR:") {
       -                        t := strings.Split(i, "DIR:")
       -                        if !stringInSlice(t[1], sl) {
       -                                if validateOnionAddress(t[1]) {
       -                                        sl = append(sl, t[1])
       -                                }
       -                        }
       -                }
       -        }
       -        return sl
       -}
   DIR diff --git a/net.go b/net.go
       t@@ -1,83 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -import (
       -        "bytes"
       -        "io/ioutil"
       -        "net"
       -        "net/http"
       -
       -        "golang.org/x/net/proxy"
       -)
       -
       -func getListener() (*net.TCPAddr, error) {
       -        addr, err := net.ResolveTCPAddr("tcp4", "localhost:0")
       -        if err != nil {
       -                return nil, err
       -        }
       -
       -        l, err := net.ListenTCP("tcp4", addr)
       -        if err != nil {
       -                return nil, err
       -        }
       -        defer l.Close()
       -        return l.Addr().(*net.TCPAddr), nil
       -}
       -
       -func httpPost(host string, data []byte) (*http.Response, error) {
       -        httpTransp := &http.Transport{}
       -        httpClient := &http.Client{Transport: httpTransp}
       -        dialer, err := proxy.SOCKS5("tcp", torAddr.String(), nil, proxy.Direct)
       -        if err != nil {
       -                return nil, err
       -        }
       -        httpTransp.Dial = dialer.Dial
       -
       -        request, err := http.NewRequest("POST", host, bytes.NewBuffer(data))
       -        if err != nil {
       -                return nil, err
       -        }
       -
       -        request.Header.Set("Content-Type", "application/json")
       -        return httpClient.Do(request)
       -}
       -
       -func httpGet(uri string) ([]byte, error) {
       -        httpTransp := &http.Transport{}
       -        httpClient := &http.Client{Transport: httpTransp}
       -        dialer, err := proxy.SOCKS5("tcp", torAddr.String(), nil, proxy.Direct)
       -        if err != nil {
       -                return nil, err
       -        }
       -        httpTransp.Dial = dialer.Dial
       -
       -        request, err := http.NewRequest("GET", uri, nil)
       -        if err != nil {
       -                return nil, err
       -        }
       -
       -        res, err := httpClient.Do(request)
       -        if err != nil {
       -                return nil, err
       -        }
       -        defer res.Body.Close()
       -        return ioutil.ReadAll(res.Body)
       -}
   DIR diff --git a/redis.go b/redis.go
       t@@ -1,141 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -import (
       -        "context"
       -        "fmt"
       -        "log"
       -        "os/exec"
       -        "strconv"
       -        "strings"
       -        "time"
       -
       -        "github.com/go-redis/redis"
       -)
       -
       -// rctx is the Redis context (necessary in newer go-redis)
       -var rctx = context.Background()
       -var rcli *redis.Client
       -
       -func pollPrune(interval int64) {
       -        for {
       -                log.Println("Polling redis for expired nodes")
       -                nodes, err := rcli.Keys(rctx, "*.onion").Result()
       -                if err != nil {
       -                        log.Println("WARNING: Nonfatal error in pollPrune:", err.Error())
       -                }
       -                now := time.Now().Unix()
       -
       -                for _, i := range nodes {
       -                        res, err := rcli.HGet(rctx, i, "lastseen").Result()
       -                        if err != nil {
       -                                log.Println("WARNING: Nonfatal error in pollPrune:", err.Error())
       -                                continue
       -                        }
       -                        ls, err := strconv.Atoi(res)
       -                        if err != nil {
       -                                log.Println("WARNING: Nonfatal error in pollPrune:", err.Error())
       -                                continue
       -                        }
       -
       -                        diff := (now - int64(ls)) / 60
       -                        if diff > interval {
       -                                log.Printf("Deleting %s (expired)\n", i)
       -                                publishToRedis('D', i)
       -                                rcli.Del(rctx, i)
       -                        }
       -                }
       -                time.Sleep(time.Duration(interval) * time.Minute)
       -        }
       -}
       -
       -func publishToRedis(mt rune, addr string) {
       -        data, err := rcli.HGetAll(rctx, addr).Result()
       -        if err != nil {
       -                log.Println("WARNING: Nonfatal err in publishToRedis:", err.Error())
       -                return
       -        }
       -
       -        if data["lastseen"] == data["firstseen"] {
       -                mt = 'A'
       -        } else if mt != 'D' {
       -                mt = 'M'
       -        }
       -
       -        // TODO: First of the "addr" references could be alias/nickname
       -
       -        rcli.Publish(rctx, pubsubChan, fmt.Sprintf("%s|%s|%v|%s",
       -                data["lastseen"], addr, mt, addr))
       -}
       -
       -func newredisrc(dir string) string {
       -        return fmt.Sprintf(`daemonize no
       -bind %s
       -port %d
       -databases 1
       -dir %s
       -dbfilename tor-dam.rdb
       -save 900 1
       -save 300 10
       -save 60 10000
       -rdbcompression yes
       -rdbchecksum yes
       -stop-writes-on-bgsave-error no`,
       -                redisAddr.IP.String(), redisAddr.Port, dir)
       -}
       -
       -func spawnRedis() (*exec.Cmd, error) {
       -        var err error
       -        redisAddr, err = getListener()
       -        if err != nil {
       -                return nil, err
       -        }
       -
       -        rcli = redis.NewClient(&redis.Options{
       -                Addr:     redisAddr.String(),
       -                Password: "",
       -                DB:       0,
       -        })
       -
       -        log.Println("Forking Redis daemon on", redisAddr.String())
       -
       -        cmd := exec.Command("redis-server", "-")
       -        cmd.Stdin = strings.NewReader(newredisrc(*workdir))
       -
       -        err = cmd.Start()
       -        if err != nil {
       -                return nil, err
       -        }
       -
       -        time.Sleep(500 * time.Millisecond)
       -        if _, err := rcli.Ping(rctx).Result(); err != nil {
       -                return cmd, err
       -        }
       -
       -        pubsub := rcli.Subscribe(rctx, pubsubChan)
       -        if _, err := pubsub.Receive(rctx); err != nil {
       -                return cmd, err
       -        }
       -
       -        log.Printf("Created \"%s\" channel in Redis\n", pubsubChan)
       -
       -        return cmd, nil
       -}
   DIR diff --git a/tor-dam.go b/tor-dam.go
       t@@ -1,191 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -import (
       -        "crypto/ed25519"
       -        "encoding/base64"
       -        "flag"
       -        "fmt"
       -        "io/ioutil"
       -        "log"
       -        "net"
       -        "net/http"
       -        "net/url"
       -        "os"
       -        "strconv"
       -        "strings"
       -        "sync"
       -        "time"
       -)
       -
       -var (
       -        noremote = flag.Bool("n", false, "Don't fetch remote entrypoints")
       -        generate = flag.Bool("g", false, "(Re)generate keys and exit")
       -        annint   = flag.Int("i", 5, "Announce interval (in minutes)")
       -        remote   = flag.String("r", "https://parazyd.org/pub/tmp/tor-dam-dirs.txt",
       -                "Remote list of entrypoints (comma-separated)")
       -        portmap = flag.String("p", "13010:13010,13011:13011,5000:5000",
       -                "Map of ports forwarded to/from Tor")
       -        expiry   = flag.Int64("e", 0, "Node expiry time in minutes (0=unlimited)")
       -        trustall = flag.Bool("t", false, "Trust all new nodes automatically")
       -        listen   = "127.0.0.1:49371"
       -        //listen   = flag.String("l", "127.0.0.1:49371",
       -        //"Listen address for daemon (Will also map in Tor HS)")
       -        workdir = flag.String("d", os.Getenv("HOME")+"/.dam", "Working directory")
       -)
       -
       -func flagSanity() error {
       -        for _, i := range strings.Split(*remote, ",") {
       -                if _, err := url.ParseRequestURI(i); err != nil {
       -                        return fmt.Errorf("invalid URL \"%s\" in remote entrypoints", i)
       -                }
       -        }
       -
       -        for _, i := range strings.Split(*portmap, ",") {
       -                t := strings.Split(i, ":")
       -                if len(t) != 2 {
       -                        return fmt.Errorf("invalid portmap: %s (len != 2)", i)
       -                }
       -                if _, err := strconv.Atoi(t[0]); err != nil {
       -                        return fmt.Errorf("invalid portmap: %s (%s)", i, err)
       -                }
       -                if _, err := strconv.Atoi(t[1]); err != nil {
       -                        return fmt.Errorf("invalid portmap: %s (%s)", i, err)
       -                }
       -        }
       -
       -        if _, err := net.ResolveTCPAddr("tcp", listen); err != nil {
       -                return fmt.Errorf("invalid listen address: %s (%s)", listen, err)
       -        }
       -
       -        return nil
       -}
       -
       -func main() {
       -        flag.Parse()
       -        var wg sync.WaitGroup
       -        var err error
       -
       -        if err := flagSanity(); err != nil {
       -                log.Fatal(err)
       -        }
       -
       -        if *generate {
       -                if err := generateED25519Keypair(*workdir); err != nil {
       -                        log.Fatal(err)
       -                }
       -                os.Exit(0)
       -        }
       -
       -        signingKey, err = loadED25519Seed(strings.Join(
       -                []string{*workdir, seedName}, "/"))
       -        if err != nil {
       -                log.Fatal(err)
       -        }
       -
       -        tor, err := spawnTor()
       -        defer tor.Process.Kill()
       -        if err != nil {
       -                log.Fatal(err)
       -        }
       -
       -        red, err := spawnRedis()
       -        defer red.Process.Kill()
       -        if err != nil {
       -                log.Fatal(err)
       -        }
       -
       -        mux := http.NewServeMux()
       -        mux.HandleFunc("/announce", handleAnnounce)
       -        mux.HandleFunc("/", handleElse)
       -        srv := &http.Server{
       -                Addr:         listen,
       -                Handler:      mux,
       -                ReadTimeout:  30 * time.Second,
       -                WriteTimeout: 30 * time.Second,
       -        }
       -
       -        go srv.ListenAndServe()
       -        log.Println("tor-dam directory listening on", listen)
       -
       -        if *trustall {
       -                log.Println("Trustall enabled, will mark all nodes trusted by default")
       -        }
       -
       -        if *expiry > 0 {
       -                log.Printf("Enabling db prune polling (%d minute interval)\n", *expiry)
       -                go pollPrune(*expiry)
       -        }
       -
       -        onionaddr, err := ioutil.ReadFile(strings.Join([]string{
       -                *workdir, "hs", "hostname"}, "/"))
       -        if err != nil {
       -                log.Fatal(err)
       -        }
       -        onionaddr = []byte(strings.TrimSuffix(string(onionaddr), "\n"))
       -        log.Printf("Our hostname is: %s\n", string(onionaddr))
       -
       -        // Network entrypoints. These files hold the lists of nodes we can announce
       -        // to initially. Format is "DIR:unlikelynameforan.onion", other lines are
       -        // ignored and can be used as comments or siimilar.
       -        epLists := strings.Split(*remote, ",")
       -
       -        for {
       -                log.Println("Announcing to nodes...")
       -                var ann = 0 // Track of successful authentications
       -                nodes, err := fetchNodeList(epLists, *noremote)
       -                if err != nil {
       -                        // No route to host, or failed download. Try later.
       -                        log.Printf("Failed to fetch nodes, retrying in 1m (%s)\n", err)
       -                        time.Sleep(60 * time.Second)
       -                        continue
       -                }
       -
       -                sigmsg := []byte("Hi tor-dam!")
       -
       -                nv := map[string]string{
       -                        "address":   string(onionaddr),
       -                        "pubkey":    base64.StdEncoding.EncodeToString(signingKey.Public().(ed25519.PublicKey)),
       -                        "message":   string(sigmsg),
       -                        "signature": base64.StdEncoding.EncodeToString(ed25519.Sign(signingKey, sigmsg)),
       -                        "secret":    "",
       -                }
       -
       -                for _, i := range nodes {
       -                        wg.Add(1)
       -                        go func(x string) {
       -                                valid, err := announce(x, nv)
       -                                if err != nil {
       -                                        log.Printf("%s: %s\n", x, err)
       -                                }
       -                                if valid {
       -                                        ann++
       -                                }
       -                                wg.Done()
       -                        }(i)
       -                }
       -                wg.Wait()
       -
       -                log.Printf("%d successful authentications\n", ann)
       -                log.Printf("Waiting %d min before next announce\n", *annint)
       -                time.Sleep(time.Duration(*annint) * time.Minute)
       -        }
       -}
   DIR diff --git a/tor.go b/tor.go
       t@@ -1,68 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -import (
       -        "fmt"
       -        "log"
       -        "os/exec"
       -        "strings"
       -)
       -
       -func newtorrc(dir string) string {
       -        var pm []string
       -
       -        for _, i := range strings.Split(*portmap, ",") {
       -                p := strings.Split(i, ":")
       -                pm = append(pm, fmt.Sprintf("HiddenServicePort %s %s",
       -                        p[0], strings.Join([]string{"127.0.0.1", p[1]}, ":")))
       -        }
       -
       -        return fmt.Sprintf(`Log warn syslog
       -RunAsDaemon 0
       -DataDirectory %s/tor
       -SocksPort %s
       -HiddenServiceDir %s/hs
       -HiddenServicePort %s %s
       -%s
       -`,
       -                dir, torAddr.String(), dir, strings.Split(listen, ":")[1],
       -                listen, strings.Join(pm, "\n"))
       -}
       -
       -func spawnTor() (*exec.Cmd, error) {
       -        var err error
       -        torAddr, err = getListener()
       -        if err != nil {
       -                return nil, err
       -        }
       -
       -        log.Println("Forking Tor daemon on", torAddr.String())
       -
       -        cmd := exec.Command("tor", "-f", "-")
       -        cmd.Stdin = strings.NewReader(newtorrc(*workdir))
       -
       -        err = cmd.Start()
       -        if err != nil {
       -                return nil, err
       -        }
       -
       -        return cmd, nil
       -}
   DIR diff --git a/types.go b/types.go
       t@@ -1,37 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -// Message represents the message struct type
       -type Message struct {
       -        Secret string `json:"secret"`
       -}
       -
       -// Node represents the node struct type
       -type Node struct {
       -        Address   string `json:"address"`
       -        Message   string `json:"message"`
       -        Signature string `json:"signature"`
       -        Secret    string `json:"secret"`
       -        Pubkey    string `json:"pubkey"`
       -        Firstseen int64  `json:"firstseen"`
       -        Lastseen  int64  `json:"lastseen"`
       -        Trusted   int    `json:"trusted"`
       -}
   DIR diff --git a/validate.go b/validate.go
       t@@ -1,158 +0,0 @@
       -package main
       -
       -/*
       - * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org>
       - *
       - * This file is part of tor-dam
       - *
       - * This program 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.
       - *
       - * This program 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/>.
       - */
       -
       -import (
       -        "crypto/ed25519"
       -        "encoding/base64"
       -        "log"
       -        "regexp"
       -        "time"
       -)
       -
       -func validateOnionAddress(addr string) bool {
       -        re, _ := regexp.Compile(`^[a-z2-7](?:.{55})\.onion`)
       -        return len(re.FindString(addr)) == 62
       -}
       -
       -// firstHandshake will take the incoming public key either from the request
       -// or, if found, from redis. This key is stored, and a nonce is generated.
       -// This nonce is returned back to the client to sign with the key. In the
       -// second handshake, we verify this nonce signature against the retrieved
       -// public key.
       -func firstHandshake(req map[string]string) (bool, string) {
       -        var pubstr string
       -
       -        // Check if we have seen this node already
       -        ex, err := rcli.Exists(rctx, req["address"]).Result()
       -        if err != nil {
       -                log.Fatal(err)
       -        }
       -
       -        if ex == 1 {
       -                // We saw it so we should hae the public key stored in redis.
       -                // If we do not, that is an internal error.
       -                pubstr, err = rcli.HGet(rctx, req["address"], "pubkey").Result()
       -                if err != nil {
       -                        log.Fatal(err)
       -                }
       -        } else {
       -                // We take it from the request
       -                pubstr = req["pubkey"]
       -        }
       -
       -        randString, err := genRandomASCII(64)
       -        if err != nil {
       -                log.Fatal(err)
       -        }
       -
       -        enc := base64.StdEncoding.EncodeToString([]byte(randString))
       -
       -        var info = map[string]interface{}{
       -                "address":   req["address"],
       -                "message":   enc,
       -                "signature": req["signature"],
       -                "secret":    enc,
       -                "lastseen":  time.Now().Unix(),
       -        } // Can not cast, need this for HSet
       -
       -        if ex != 1 {
       -                // We did not have this node in redis
       -                info["pubkey"] = pubstr
       -                info["firstseen"] = time.Now().Unix()
       -                if *trustall {
       -                        info["trusted"] = 1
       -                } else {
       -                        info["trusted"] = 0
       -                }
       -        }
       -
       -        log.Printf("%s: Writing to redis\n", req["address"])
       -        if _, err := rcli.HSet(rctx, req["address"], info).Result(); err != nil {
       -                log.Fatal(err)
       -        }
       -
       -        return true, enc
       -}
       -
       -func secondHandshake(req map[string]string) (bool, string) {
       -        // Check if we have seen this node already
       -        ex, err := rcli.Exists(rctx, req["address"]).Result()
       -        if err != nil {
       -                log.Fatal(err)
       -        }
       -        if ex != 1 {
       -                log.Printf("%s tried to jump in 2/2 handshake before getting a nonce\n",
       -                        req["address"])
       -                return false, "We have not seen you before. Authenticate properly."
       -        }
       -
       -        // We saw it so we should have the public key in redis. If we do not,
       -        // then it's an internal error.
       -        pubstr, err := rcli.HGet(rctx, req["address"], "pubkey").Result()
       -        if err != nil {
       -                log.Fatal(err)
       -        }
       -
       -        lSec, err := rcli.HGet(rctx, req["address"], "secret").Result()
       -        if err != nil {
       -                log.Fatal(err)
       -        }
       -
       -        if lSec != req["secret"] || lSec != req["message"] {
       -                log.Printf("%s: Secrets didn't match\n", req["address"])
       -                return false, "Secrets didn't match."
       -        }
       -
       -        // Validate signature.
       -        msg := []byte(lSec)
       -        sig, _ := base64.StdEncoding.DecodeString(req["signature"])
       -        deckey, err := base64.StdEncoding.DecodeString(pubstr)
       -        if err != nil {
       -                log.Fatal(err)
       -        }
       -        pubkey := ed25519.PublicKey(deckey)
       -
       -        if !ed25519.Verify(pubkey, msg, sig) {
       -                log.Println("crypto/ed25519: Signature verification failure")
       -                return false, "Signature verification failure"
       -        }
       -
       -        // The request is valid at this point
       -
       -        // Make a new random secret to prevent reuse.
       -        randString, _ := genRandomASCII(64)
       -        encSecret := base64.StdEncoding.EncodeToString([]byte(randString))
       -
       -        var info = map[string]interface{}{
       -                "address":   req["address"],
       -                "message":   encSecret,
       -                "signature": req["signature"],
       -                "secret":    encSecret,
       -                "lastseen":  time.Now().Unix(),
       -        } // TODO: Use struct
       -
       -        log.Printf("Adding %s to redis\n", req["address"])
       -        if _, err := rcli.HSet(rctx, req["address"], info).Result(); err != nil {
       -                log.Fatal(err)
       -        }
       -
       -        return true, "Welcome to tor-dam"
       -}