aboutsummaryrefslogtreecommitdiff
path: root/server/dht.go
blob: e5d96f1824a1146c6e51217201e4526b4e6513d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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)
	}
}