aboutsummaryrefslogtreecommitdiff
path: root/server/dht.go
diff options
context:
space:
mode:
Diffstat (limited to 'server/dht.go')
-rw-r--r--server/dht.go139
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)
+ }
+}