tor-dam

tor distributed announce mechanism (not a dht)
git clone https://git.parazyd.org/tor-dam
Log | Files | Refs | README | LICENSE

api.go (5014B)


      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 	"encoding/json"
     24 	"log"
     25 	"net/http"
     26 	"strings"
     27 )
     28 
     29 func postback(rw http.ResponseWriter, data map[string]string, ret int) error {
     30 	val, err := json.Marshal(data)
     31 	if err != nil {
     32 		return err
     33 	}
     34 
     35 	rw.Header().Set("Content-Type", "application/json")
     36 	rw.WriteHeader(ret)
     37 	if _, err := rw.Write(val); err != nil {
     38 		return err
     39 	}
     40 	return nil
     41 }
     42 
     43 func handleAnnounce(rw http.ResponseWriter, req *http.Request) {
     44 	var r map[string]string
     45 	var n Node
     46 
     47 	if req.Method != "POST" || req.Header["Content-Type"][0] != "application/json" {
     48 		r = map[string]string{"secret": "Invalid request format"}
     49 		if err := postback(rw, r, 400); err != nil {
     50 			log.Fatal(err)
     51 		}
     52 		return
     53 	}
     54 
     55 	dec := json.NewDecoder(req.Body)
     56 	if err := dec.Decode(&n); err != nil {
     57 		log.Println("Failed decoding request:", err)
     58 		return
     59 	}
     60 
     61 	// Bail out as soon as possible
     62 	if len(n.Address) == 0 || len(n.Message) == 0 || len(n.Signature) == 0 {
     63 		r = map[string]string{"secret": "Invalid request format"}
     64 		if err := postback(rw, r, 400); err != nil {
     65 			log.Fatal(err)
     66 		}
     67 		return
     68 	}
     69 
     70 	if !validateOnionAddress(n.Address) {
     71 		log.Println("Invalid onion address:", n.Address)
     72 		r = map[string]string{"secret": "Invalid onion address"}
     73 		if err := postback(rw, r, 400); err != nil {
     74 			log.Fatal(err)
     75 		}
     76 		return
     77 	}
     78 
     79 	rq := map[string]string{
     80 		"address":   n.Address,
     81 		"message":   n.Message,
     82 		"pubkey":    n.Pubkey,
     83 		"signature": n.Signature,
     84 		"secret":    n.Secret,
     85 	}
     86 
     87 	// First handshake
     88 	if len(n.Message) != 88 || len(n.Secret) != 88 {
     89 		valid, msg := firstHandshake(rq)
     90 		r = map[string]string{"secret": msg}
     91 		if valid {
     92 			log.Printf("%s: 1/2 handskake valid\n", n.Address)
     93 			log.Println("Sending nonce to", n.Address)
     94 			if err := postback(rw, r, 200); err != nil {
     95 				log.Fatal(err)
     96 			}
     97 			return
     98 		}
     99 		log.Printf("%s: 1/2 handshake invalid: %s\n", n.Address, msg)
    100 		// Delete it all from redis
    101 		// TODO: Can this be abused?
    102 		if _, err := rcli.Del(rctx, n.Address).Result(); err != nil {
    103 			log.Fatal(err)
    104 		}
    105 		return
    106 	}
    107 
    108 	// Second handshake
    109 	if len(rq["secret"]) == 88 && len(rq["message"]) == 88 {
    110 		valid, msg := secondHandshake(rq)
    111 		r = map[string]string{"secret": msg}
    112 
    113 		if valid {
    114 			log.Printf("%s: 2/2 handshake valid\n", n.Address)
    115 			isTrusted, err := rcli.HGet(rctx, n.Address, "trusted").Result()
    116 			if err != nil {
    117 				log.Fatal(err)
    118 			}
    119 
    120 			// Assume our name is what was requested
    121 			us := strings.TrimSuffix(req.Host, ":49371")
    122 			nodemap := make(map[string]map[string]string)
    123 
    124 			if isTrusted == "1" {
    125 				// The node is marked as trusted so we'll teack it about other
    126 				// trusted nodes we know about.
    127 				log.Printf("%s is trusted. Propagating knowledge...\n", n.Address)
    128 				nodes, err := rcli.Keys(rctx, "*.onion").Result()
    129 				if err != nil {
    130 					log.Fatal(err)
    131 				}
    132 				for _, i := range nodes {
    133 					if i == n.Address {
    134 						continue
    135 					}
    136 					nodedata, err := rcli.HGetAll(rctx, i).Result()
    137 					if err != nil {
    138 						log.Fatal(err)
    139 					}
    140 					if nodedata["trusted"] == "1" {
    141 						nodemap[i] = nodedata
    142 						delete(nodemap[i], "secret")
    143 					}
    144 				}
    145 			} else {
    146 				log.Printf("%s is not trusted. Propagating self...", n.Address)
    147 				// The node doesn't have trust in the network. We will only
    148 				// teach it about ourself.
    149 				nodedata, err := rcli.HGetAll(rctx, us).Result()
    150 				if err != nil {
    151 					log.Fatal(err)
    152 				}
    153 				nodemap[us] = nodedata
    154 				delete(nodemap[us], "secret")
    155 			}
    156 
    157 			nodestr, err := json.Marshal(nodemap)
    158 			if err != nil {
    159 				log.Fatal(err)
    160 			}
    161 			comp, err := gzipEncode(nodestr)
    162 			if err != nil {
    163 				log.Fatal(err)
    164 			}
    165 			r = map[string]string{"secret": comp}
    166 			if err := postback(rw, r, 200); err != nil {
    167 				log.Fatal(err)
    168 			}
    169 
    170 			publishToRedis('M', n.Address)
    171 			return
    172 		}
    173 
    174 		// If we haven't returned so far, the handshake is invalid
    175 		log.Printf("%s: 2/2 handshake invalid\n", n.Address)
    176 		// Delete it all from redis
    177 		// TODO: Can this be abused?
    178 		publishToRedis('D', n.Address)
    179 		if _, err := rcli.Del(rctx, n.Address).Result(); err != nil {
    180 			log.Fatal(err)
    181 		}
    182 		if err := postback(rw, r, 400); err != nil {
    183 			log.Fatal(err)
    184 		}
    185 		return
    186 	}
    187 }
    188 
    189 func handleElse(rw http.ResponseWriter, req *http.Request) {
    190 	log.Println("Got handleElse")
    191 }