URI: 
       tImplement fetching of network entry points. - 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 9bb95c0fdbb1c0bc6d70ad5d0b9e131141fc1e66
   DIR parent 7a8b2ca208e22bd2c99b47af8eec7b96e1bda418
  HTML Author: parazyd <parazyd@dyne.org>
       Date:   Wed, 20 Dec 2017 13:44:53 +0100
       
       Implement fetching of network entry points.
       
       This commit introduces fetching of entry nodes by downloading and
       parsing hardcoded lists of directories the client can announce to.
       
       This commit also makes the announce function run inside an infinite
       loop, announcing itself to freshly-fetched directories every ten
       minutes.
       
       Diffstat:
         M cmd/dam-client/main.go              |     133 ++++++++++++++++++++++---------
         M pkg/damlib/helpers.go               |      11 +++++++++++
         M pkg/damlib/net.go                   |      16 ++++++++++++++++
       
       3 files changed, 121 insertions(+), 39 deletions(-)
       ---
   DIR diff --git a/cmd/dam-client/main.go b/cmd/dam-client/main.go
       t@@ -8,9 +8,11 @@ import (
                "encoding/base64"
                "encoding/json"
                "log"
       +        "math/rand"
                "os"
                "os/exec"
                "strconv"
       +        "strings"
                "sync"
                "time"
        
       t@@ -21,6 +23,13 @@ type msgStruct struct {
                Secret string
        }
        
       +// Network entry points. These files hold the lists of directories we can
       +// announce to. Format is "DIR:22mobp7vrb7a4gt2.onion", other lines are ignored.
       +var dirHosts = []string{
       +        "https://pub.parazyd.cf/tmp/dirs.txt",
       +        "https://pub.parazyd.cf/tmp/moredirs.txt",
       +}
       +
        func announce(dir string, vals map[string]string, privkey *rsa.PrivateKey) (bool, error) {
                msg, err := json.Marshal(vals)
                if err != nil {
       t@@ -100,6 +109,47 @@ func announce(dir string, vals map[string]string, privkey *rsa.PrivateKey) (bool
                return false, nil
        }
        
       +func fetchDirlist(locations []string) ([]string, error) {
       +        var dirSlice, dirlist []string
       +        log.Println("Grabbing a list of directories.")
       +
       +        for _, i := range locations {
       +                log.Println("Fetching", i)
       +                dirs, err := lib.HTTPDownload(i)
       +                if err != nil {
       +                        return nil, err
       +                }
       +                dirStr := string(dirs)
       +                _dirs := strings.Split(dirStr, "\n")
       +                for _, j := range _dirs {
       +                        if strings.HasPrefix(j, "DIR:") {
       +                                t := strings.Split(j, "DIR:")
       +                                if !(lib.StringInSlice(t[1], dirSlice)) {
       +                                        dirSlice = append(dirSlice, t[1])
       +                                }
       +                        }
       +                }
       +        }
       +        if len(dirSlice) < 1 {
       +                log.Fatalln("Couldn't get any directories. Exiting.")
       +        } else if len(dirSlice) <= 6 {
       +                log.Printf("Found only %d directories.\n", len(dirSlice))
       +                dirlist = dirSlice
       +        } else {
       +                log.Println("Found enough directories. Picking out 6 random ones.")
       +                // Pick out 6 random directories from the retrieved list.
       +                for k := 0; k <= 5; k++ {
       +                        rand.Seed(time.Now().Unix())
       +                        n := rand.Int() % len(dirSlice)
       +                        dirlist = append(dirlist, dirSlice[n])
       +                        dirSlice[n] = dirSlice[len(dirSlice)-1]
       +                        dirSlice = dirSlice[:len(dirSlice)-1]
       +                }
       +        }
       +        dirlist = append(dirlist, "localhost")
       +        return dirlist, nil
       +}
       +
        func main() {
                if _, err := os.Stat(lib.Cwd); os.IsNotExist(err) {
                        err := os.Mkdir(lib.Cwd, 0700)
       t@@ -147,50 +197,55 @@ func main() {
                        }
                }
        
       -        key, err := lib.LoadRsaKeyFromFile(lib.PrivKeyPath)
       -        lib.CheckError(err)
       -
       -        sig, err := lib.SignMsgRsa([]byte(lib.PostMsg), key)
       -        lib.CheckError(err)
       -        encodedSig := base64.StdEncoding.EncodeToString(sig)
       -
       -        onionAddr, err := lib.OnionFromPubkeyRsa(key.PublicKey)
       -        lib.CheckError(err)
       +        for {
       +                key, err := lib.LoadRsaKeyFromFile(lib.PrivKeyPath)
       +                lib.CheckError(err)
        
       -        nodevals := map[string]string{
       -                "nodetype":  "node",
       -                "address":   string(onionAddr),
       -                "message":   lib.PostMsg,
       -                "signature": encodedSig,
       -                "secret":    "",
       -        }
       +                sig, err := lib.SignMsgRsa([]byte(lib.PostMsg), key)
       +                lib.CheckError(err)
       +                encodedSig := base64.StdEncoding.EncodeToString(sig)
        
       -        var ann = 0 // Track of how many successful authentications
       +                onionAddr, err := lib.OnionFromPubkeyRsa(key.PublicKey)
       +                lib.CheckError(err)
        
       -        dirs := []string{"3mb6b3exknytbqdg.onion", "localhost"}
       +                nodevals := map[string]string{
       +                        "nodetype":  "node",
       +                        "address":   string(onionAddr),
       +                        "message":   lib.PostMsg,
       +                        "signature": encodedSig,
       +                        "secret":    "",
       +                }
        
       -        var wg sync.WaitGroup
       -        for _, i := range dirs {
       -                wg.Add(1)
       -                go func(x string) {
       -                        valid, err := announce(x, nodevals, key)
       -                        if err != nil {
       -                                log.Printf("%s: %s\n", x, err.Error())
       -                        }
       -                        if valid {
       -                                ann++
       -                        }
       -                        wg.Done()
       -                }(i)
       -        }
       -        wg.Wait()
       +                log.Println("Announcing to directories...")
       +                var ann = 0 // Track of how many successful authentications
       +                var wg sync.WaitGroup
       +                dirlist, err := fetchDirlist(dirHosts)
       +                lib.CheckError(err)
       +                for _, i := range dirlist {
       +                        wg.Add(1)
       +                        go func(x string) {
       +                                valid, err := announce(x, nodevals, key)
       +                                if err != nil {
       +                                        log.Printf("%s: %s\n", x, err.Error())
       +                                }
       +                                if valid {
       +                                        ann++
       +                                }
       +                                wg.Done()
       +                        }(i)
       +                }
       +                wg.Wait()
        
       -        if ann < 1 {
       -                cmd.Process.Kill()
       -                log.Fatalln("No successful authentications. Exiting.")
       +                if ann < 1 {
       +                        cmd.Process.Kill()
       +                        log.Fatalln("No successful authentications. Exiting.")
       +                } else {
       +                        log.Printf("Successfully authenticated with %d nodes.\n", ann)
       +                }
       +                log.Println("Waiting 10 minutes before next announce.")
       +                time.Sleep(600 * time.Second)
                }
       -        log.Printf("Successfully authenticated with %d nodes.\n", ann)
        
       -        err = cmd.Wait() // Hidden service Python daemon
       -        lib.CheckError(err)
       +        //err = cmd.Wait() // Hidden service Python daemon
       +        //lib.CheckError(err)
        }
   DIR diff --git a/pkg/damlib/helpers.go b/pkg/damlib/helpers.go
       t@@ -13,3 +13,14 @@ func CheckError(err error) {
                        log.Fatalln(err)
                }
        }
       +
       +// StringInSlice loops over a slice of strings and checks if a given string is
       +// already an existing element. Returns true if so, and false if not.
       +func StringInSlice(str string, slice []string) bool {
       +        for _, i := range slice {
       +                if str == i {
       +                        return true
       +                }
       +        }
       +        return false
       +}
   DIR diff --git a/pkg/damlib/net.go b/pkg/damlib/net.go
       t@@ -4,6 +4,7 @@ package damlib
        
        import (
                "bytes"
       +        "io/ioutil"
                "log"
                "net/http"
                "net/url"
       t@@ -50,3 +51,18 @@ func HTTPPost(host string, data []byte) (*http.Response, error) {
        
                return resp, nil
        }
       +
       +// HTTPDownload tries to download a given uri and return it as a slice of bytes.
       +// On failure it will return an error.
       +func HTTPDownload(uri string) ([]byte, error) {
       +        res, err := http.Get(uri)
       +        if err != nil {
       +                return nil, err
       +        }
       +        defer res.Body.Close()
       +        d, err := ioutil.ReadAll(res.Body)
       +        if err != nil {
       +                return nil, err
       +        }
       +        return d, nil
       +}