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)
}
}
|