announce.go (4416B)
1 package main 2 3 /* 4 * Copyright (c) 2017-2021 Ivan Jelincic <parazyd@dyne.org> 5 * 6 * This file is part of tor-dam 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Affero General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Affero General Public License for more details. 17 * 18 * You should have received a copy of the GNU Affero General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 import ( 23 "bytes" 24 "compress/gzip" 25 "crypto/ed25519" 26 "crypto/rand" 27 "encoding/base64" 28 "encoding/json" 29 "io/ioutil" 30 "log" 31 "math/big" 32 "os" 33 "strings" 34 ) 35 36 func fetchNodeList(epLists []string, remote bool) ([]string, error) { 37 var ns, nl []string 38 39 log.Println("Building a list of nodes") 40 41 // Remote network entrypoints 42 if !remote { 43 for _, i := range epLists { 44 log.Println("Fetching", i) 45 n, err := httpGet(i) 46 if err != nil { 47 return nil, err 48 } 49 ns = parseDirs(ns, n) 50 } 51 } 52 53 // Local workdir/dirs.txt 54 ld := strings.Join([]string{*workdir, "dirs.txt"}, "/") 55 if _, err := os.Stat(ld); err == nil { 56 ln, err := ioutil.ReadFile(ld) 57 if err != nil { 58 return nil, err 59 } 60 ns = parseDirs(ns, ln) 61 } 62 63 // Local nodes from redis 64 nodes, _ := rcli.Keys(rctx, "*.onion").Result() 65 for _, i := range nodes { 66 valid, err := rcli.HGet(rctx, i, "valid").Result() 67 if err != nil { 68 // Possible RedisCli bug, possible Redis bug. To be investigated. 69 // Sometimes it returns err, but it's empty and does not say what's 70 // happening exactly. 71 continue 72 } 73 if valid == "1" { 74 ns = append(ns, i) 75 } 76 } 77 78 // Remove possible dupes. Duplicates can cause race conditions and are 79 // redundant to the entire logic. 80 // TODO: Work this in above automatically (by changing the var type) 81 encounter := map[string]bool{} 82 for i := range ns { 83 encounter[ns[i]] = true 84 } 85 ns = []string{} 86 for key := range encounter { 87 ns = append(ns, key) 88 } 89 90 if len(ns) < 1 { 91 log.Fatal("Couldn't find any nodes to announce to. Exiting...") 92 } else if len(ns) <= 6 { 93 log.Printf("Found %d nodes\n", len(ns)) 94 nl = ns 95 } else { 96 log.Printf("Found %d nodes. Picking out 6 at random\n", len(ns)) 97 for i := 0; i <= 5; i++ { 98 n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(ns)))) 99 nl = append(nl, ns[n.Int64()]) 100 ns[n.Int64()] = ns[len(ns)-1] 101 ns = ns[:len(ns)-1] 102 } 103 } 104 105 return nl, nil 106 } 107 108 func announce(addr string, vals map[string]string) (bool, error) { 109 msg, _ := json.Marshal(vals) 110 111 log.Println("Announcing keypair to", addr) 112 resp, err := httpPost("http://"+addr+":49371"+"/announce", msg) 113 if err != nil { 114 return false, err 115 } 116 117 // Parse server's reply 118 var m Message 119 dec := json.NewDecoder(resp.Body) 120 if err := dec.Decode(&m); err != nil { 121 return false, err 122 } 123 124 if resp.StatusCode != 200 { 125 log.Printf("%s returned error: %s\n", addr, m.Secret) 126 return false, nil 127 } 128 129 log.Println("Got nonce from", addr) 130 131 sig := ed25519.Sign(signingKey, []byte(m.Secret)) 132 133 vals["secret"] = m.Secret 134 vals["message"] = m.Secret 135 vals["signature"] = base64.StdEncoding.EncodeToString(sig) 136 msg, _ = json.Marshal(vals) 137 138 log.Println("Sending back signed secret to", addr) 139 resp, err = httpPost("http://"+addr+":49371"+"/announce", msg) 140 if err != nil { 141 return false, err 142 } 143 144 dec = json.NewDecoder(resp.Body) 145 if err := dec.Decode(&m); err != nil { 146 return false, err 147 } 148 149 if resp.StatusCode != 200 { 150 log.Printf("%s returned error: %s\n", addr, m.Secret) 151 return false, nil 152 } 153 154 log.Printf("%s handshake valid\n", addr) 155 data, err := base64.StdEncoding.DecodeString(m.Secret) 156 if err != nil { 157 // Not a list of nodes 158 log.Printf("%s replied: %s\n", addr, m.Secret) 159 return true, nil 160 } 161 162 log.Println("Got node data, processing...") 163 b := bytes.NewReader(data) 164 r, _ := gzip.NewReader(b) 165 nodes := make(map[string]map[string]interface{}) 166 dec = json.NewDecoder(r) 167 if err = dec.Decode(&nodes); err != nil { 168 return false, err 169 } 170 171 for k, v := range nodes { 172 log.Printf("Adding %s to redis\n", k) 173 if _, err := rcli.HSet(rctx, k, v).Result(); err != nil { 174 log.Fatal(err) 175 } 176 } 177 178 return true, nil 179 }