diff options
Diffstat (limited to 'server/dht.go')
-rw-r--r-- | server/dht.go | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/server/dht.go b/server/dht.go new file mode 100644 index 0000000..e5d96f1 --- /dev/null +++ b/server/dht.go @@ -0,0 +1,139 @@ +package main + +import ( + "crypto/ed25519" + "encoding/base64" + "fmt" + "io" + "log" + "net/http" + "sort" + "strings" +) + +// Try to peer with another server +func addPeer(peer string) error { + peerHash := sha256sum(peer) + // Check if already peered + mu.Lock() + _, ok := hashToDomain[peerHash] + mu.Unlock() + if ok { + return nil + } + mu.Lock() + hashToDomain[peerHash] = peer + mu.Unlock() + + // Try request to peer + log.Printf("%s trying to peer with %s", me, peer) + resp, err := http.Get(peer + "/peer?peer=" + me) + if err != nil { + // Request failed, delete peer + mu.Lock() + delete(hashToDomain, peerHash) + mu.Unlock() + return err + } + + log.Printf("%s successfully peered with %s", me, peer) + mu.Lock() + i := sort.SearchStrings(peerHashes, peerHash) + peerHashes = append(peerHashes, "") + copy(peerHashes[i+1:], peerHashes[i:]) + peerHashes[i] = peerHash + myPos = sort.SearchStrings(peerHashes, me) + mu.Unlock() + // Read response body + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + // Try adding all peers of this peer + newPeers := strings.Split(string(body), "\n") + for _, newPeer := range newPeers[:len(newPeers)-1] { + go addPeer(newPeer) + } + return nil +} + +// Handle incoming peer requests +func peerHandler(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + peer := r.Form.Get("peer") + if peer == "" { + w.WriteHeader(http.StatusBadRequest) + return + } + for _, p := range hashToDomain { + fmt.Fprintf(w, "%s\n", p) + } + go addPeer(peer) +} + +// Handle DHT requests +func dhtHandler(w http.ResponseWriter, r *http.Request) { + key := r.URL.String()[5:] + keyHash := sha256sum(key) + pubKey := asPubKey(key) + fmt.Println(key, keyHash, pubKey) + mu.Lock() + keyPos := sort.SearchStrings(peerHashes, keyHash) + if keyPos < myPos { + keyPos += len(peerHashes) + } + mu.Unlock() + if r.Method == "GET" { + if keyPos - myPos < 5 { + mu.Lock() + if val, ok := kvstore[key]; ok { + w.Write([]byte(val)) + } else { + w.WriteHeader(http.StatusNotFound) + } + mu.Unlock() + } else { + for i := 0; i < 5 && i < len(peerHashes); i++ { + j := hashToDomain[peerHashes[(keyPos+i)%len(peerHashes)]] + go func() string { + resp, err := http.Get(j + r.URL.String()) + if err != nil { + return "" + } + b, err := io.ReadAll(resp.Body) + if err != nil { + return "" + } + return string(b) + }() + + } + + + } + } else if r.Method == "PUT" { + // Read request body + b, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + // Extract signature + valSplit := strings.Split(string(b), "\n") + sig := valSplit[len(valSplit)-1] + // Verify signature + if !ed25519.Verify(pubKey, b[:len(b)-len(sig)-1], []byte(sig)) { + w.WriteHeader(http.StatusUnauthorized) + return + } + if keyPos - myPos < 5 { + mu.Lock() + kvstore[key] = string(b[:len(b)-len(sig)-1]) + mu.Unlock() + } else { + + } + } else { + w.WriteHeader(http.StatusMethodNotAllowed) + } +} |