tordam

A library for peer discovery inside the Tor network
git clone https://git.parazyd.org/tordam
Log | Files | Refs | README | LICENSE

commit 9bb95c0fdbb1c0bc6d70ad5d0b9e131141fc1e66
parent 7a8b2ca208e22bd2c99b47af8eec7b96e1bda418
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:
Mcmd/dam-client/main.go | 133++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mpkg/damlib/helpers.go | 11+++++++++++
Mpkg/damlib/net.go | 16++++++++++++++++
3 files changed, 121 insertions(+), 39 deletions(-)

diff --git a/cmd/dam-client/main.go b/cmd/dam-client/main.go @@ -8,9 +8,11 @@ import ( "encoding/base64" "encoding/json" "log" + "math/rand" "os" "os/exec" "strconv" + "strings" "sync" "time" @@ -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 { @@ -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) @@ -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) } diff --git a/pkg/damlib/helpers.go b/pkg/damlib/helpers.go @@ -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 +} diff --git a/pkg/damlib/net.go b/pkg/damlib/net.go @@ -4,6 +4,7 @@ package damlib import ( "bytes" + "io/ioutil" "log" "net/http" "net/url" @@ -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 +}