diff options
author | Bernd Fix <brf@hoi-polloi.org> | 2022-07-18 11:16:34 +0200 |
---|---|---|
committer | Bernd Fix <brf@hoi-polloi.org> | 2022-07-18 11:16:34 +0200 |
commit | f425c2aeef06d1a6105678c8b058bdde65a26e78 (patch) | |
tree | 1868bb9c2b343aca55108e97671631178521a4f0 /src | |
parent | 4261e07def81e7c3eb183b9d5c4059a2e9c53759 (diff) | |
download | gnunet-go-f425c2aeef06d1a6105678c8b058bdde65a26e78.tar.gz gnunet-go-f425c2aeef06d1a6105678c8b058bdde65a26e78.zip |
Improved handling of pending DHT results.
Diffstat (limited to 'src')
98 files changed, 4132 insertions, 2218 deletions
diff --git a/src/gnunet/build.sh b/src/gnunet/build.sh index 39ecbaf..20f6933 100755 --- a/src/gnunet/build.sh +++ b/src/gnunet/build.sh | |||
@@ -1,4 +1,4 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | 2 | ||
3 | go generate ./... | 3 | [ "$1" = "withgen" ] && go generate ./... |
4 | go install -v -gcflags "-N -l" ./... | 4 | go install -v -gcflags "-N -l" ./... |
diff --git a/src/gnunet/cmd/gnunet-service-dht-go/main.go b/src/gnunet/cmd/gnunet-service-dht-go/main.go index ef6da91..8e23ac2 100644 --- a/src/gnunet/cmd/gnunet-service-dht-go/main.go +++ b/src/gnunet/cmd/gnunet-service-dht-go/main.go | |||
@@ -21,7 +21,6 @@ package main | |||
21 | import ( | 21 | import ( |
22 | "context" | 22 | "context" |
23 | "flag" | 23 | "flag" |
24 | "net/rpc" | ||
25 | "os" | 24 | "os" |
26 | "os/signal" | 25 | "os/signal" |
27 | "strings" | 26 | "strings" |
@@ -58,7 +57,7 @@ func main() { | |||
58 | flag.StringVar(&cfgFile, "c", "gnunet-config.json", "GNUnet configuration file") | 57 | flag.StringVar(&cfgFile, "c", "gnunet-config.json", "GNUnet configuration file") |
59 | flag.StringVar(&socket, "s", "", "GNS service socket") | 58 | flag.StringVar(&socket, "s", "", "GNS service socket") |
60 | flag.StringVar(¶m, "p", "", "socket parameters (<key>=<value>,...)") | 59 | flag.StringVar(¶m, "p", "", "socket parameters (<key>=<value>,...)") |
61 | flag.IntVar(&logLevel, "L", logger.INFO, "DHT log level (default: INFO)") | 60 | flag.IntVar(&logLevel, "L", logger.DBG, "DHT log level (default: DBG)") |
62 | flag.StringVar(&rpcEndp, "R", "", "JSON-RPC endpoint (default: none)") | 61 | flag.StringVar(&rpcEndp, "R", "", "JSON-RPC endpoint (default: none)") |
63 | flag.Parse() | 62 | flag.Parse() |
64 | 63 | ||
@@ -71,7 +70,7 @@ func main() { | |||
71 | // apply configuration | 70 | // apply configuration |
72 | logger.SetLogLevel(logLevel) | 71 | logger.SetLogLevel(logLevel) |
73 | if len(socket) == 0 { | 72 | if len(socket) == 0 { |
74 | socket = config.Cfg.GNS.Service.Socket | 73 | socket = config.Cfg.DHT.Service.Socket |
75 | } | 74 | } |
76 | params := make(map[string]string) | 75 | params := make(map[string]string) |
77 | if len(param) > 0 { | 76 | if len(param) > 0 { |
@@ -80,7 +79,7 @@ func main() { | |||
80 | params[kv[0]] = kv[1] | 79 | params[kv[0]] = kv[1] |
81 | } | 80 | } |
82 | } else { | 81 | } else { |
83 | params = config.Cfg.GNS.Service.Params | 82 | params = config.Cfg.DHT.Service.Params |
84 | } | 83 | } |
85 | 84 | ||
86 | // instantiate core service | 85 | // instantiate core service |
@@ -93,8 +92,8 @@ func main() { | |||
93 | defer c.Shutdown() | 92 | defer c.Shutdown() |
94 | 93 | ||
95 | // start a new DHT service | 94 | // start a new DHT service |
96 | var dhtSrv service.Service | 95 | var dhtSrv *dht.Service |
97 | if dhtSrv, err = dht.NewService(ctx, c); err != nil { | 96 | if dhtSrv, err = dht.NewService(ctx, c, config.Cfg.DHT); err != nil { |
98 | logger.Printf(logger.ERROR, "[dht] failed to create DHT service: %s\n", err.Error()) | 97 | logger.Printf(logger.ERROR, "[dht] failed to create DHT service: %s\n", err.Error()) |
99 | return | 98 | return |
100 | } | 99 | } |
@@ -104,6 +103,14 @@ func main() { | |||
104 | return | 103 | return |
105 | } | 104 | } |
106 | 105 | ||
106 | // hande network size estimation: if a fixed number of peers are present | ||
107 | // in the network config, use that value; otherwise utilize the NSE | ||
108 | // algorithm (not implemented yet) | ||
109 | numPeers := config.Cfg.Network.NumPeers | ||
110 | if numPeers != 0 { | ||
111 | dhtSrv.SetNetworkSize(numPeers) | ||
112 | } | ||
113 | |||
107 | // handle command-line arguments for RPC | 114 | // handle command-line arguments for RPC |
108 | if len(rpcEndp) > 0 { | 115 | if len(rpcEndp) > 0 { |
109 | parts := strings.Split(rpcEndp, ":") | 116 | parts := strings.Split(rpcEndp, ":") |
@@ -115,8 +122,8 @@ func main() { | |||
115 | } | 122 | } |
116 | // start JSON-RPC server on request | 123 | // start JSON-RPC server on request |
117 | if ep := config.Cfg.RPC.Endpoint; len(ep) > 0 { | 124 | if ep := config.Cfg.RPC.Endpoint; len(ep) > 0 { |
118 | var rpc *rpc.Server | 125 | var rpc *service.JRPCServer |
119 | if rpc, err = service.StartRPC(ctx, ep); err != nil { | 126 | if rpc, err = service.RunRPCServer(ctx, ep); err != nil { |
120 | logger.Printf(logger.ERROR, "[dht] RPC failed to start: %s", err.Error()) | 127 | logger.Printf(logger.ERROR, "[dht] RPC failed to start: %s", err.Error()) |
121 | return | 128 | return |
122 | } | 129 | } |
@@ -125,7 +132,7 @@ func main() { | |||
125 | 132 | ||
126 | // handle bootstrap: collect known addresses | 133 | // handle bootstrap: collect known addresses |
127 | bsList := make([]*util.Address, 0) | 134 | bsList := make([]*util.Address, 0) |
128 | for _, bs := range config.Cfg.Bootstrap.Nodes { | 135 | for _, bs := range config.Cfg.Network.Bootstrap { |
129 | // check for HELLO URL | 136 | // check for HELLO URL |
130 | if strings.HasPrefix(bs, "gnunet://hello/") { | 137 | if strings.HasPrefix(bs, "gnunet://hello/") { |
131 | var hb *blocks.HelloBlock | 138 | var hb *blocks.HelloBlock |
@@ -147,7 +154,9 @@ func main() { | |||
147 | } | 154 | } |
148 | // send HELLO to all bootstrap addresses | 155 | // send HELLO to all bootstrap addresses |
149 | for _, addr := range bsList { | 156 | for _, addr := range bsList { |
150 | c.SendHello(ctx, addr) | 157 | if err := dhtSrv.SendHello(ctx, addr); err != nil { |
158 | logger.Printf(logger.ERROR, "[dht] send HELLO failed: %s", err.Error()) | ||
159 | } | ||
151 | } | 160 | } |
152 | 161 | ||
153 | // handle OS signals | 162 | // handle OS signals |
@@ -181,5 +190,7 @@ loop: | |||
181 | 190 | ||
182 | // terminating service | 191 | // terminating service |
183 | cancel() | 192 | cancel() |
184 | srv.Stop() | 193 | if err := srv.Stop(); err != nil { |
194 | logger.Printf(logger.ERROR, "[dht] Failed to stop service: %s", err.Error()) | ||
195 | } | ||
185 | } | 196 | } |
diff --git a/src/gnunet/cmd/gnunet-service-gns-go/main.go b/src/gnunet/cmd/gnunet-service-gns-go/main.go index 72f7de6..ebf2151 100644 --- a/src/gnunet/cmd/gnunet-service-gns-go/main.go +++ b/src/gnunet/cmd/gnunet-service-gns-go/main.go | |||
@@ -21,7 +21,6 @@ package main | |||
21 | import ( | 21 | import ( |
22 | "context" | 22 | "context" |
23 | "flag" | 23 | "flag" |
24 | "net/rpc" | ||
25 | "os" | 24 | "os" |
26 | "os/signal" | 25 | "os/signal" |
27 | "strings" | 26 | "strings" |
@@ -109,8 +108,8 @@ func main() { | |||
109 | } | 108 | } |
110 | // start JSON-RPC server on request | 109 | // start JSON-RPC server on request |
111 | if ep := config.Cfg.RPC.Endpoint; len(ep) > 0 { | 110 | if ep := config.Cfg.RPC.Endpoint; len(ep) > 0 { |
112 | var rpc *rpc.Server | 111 | var rpc *service.JRPCServer |
113 | if rpc, err = service.StartRPC(ctx, ep); err != nil { | 112 | if rpc, err = service.RunRPCServer(ctx, ep); err != nil { |
114 | logger.Printf(logger.ERROR, "[gns] RPC failed to start: %s", err.Error()) | 113 | logger.Printf(logger.ERROR, "[gns] RPC failed to start: %s", err.Error()) |
115 | return | 114 | return |
116 | } | 115 | } |
@@ -148,5 +147,7 @@ loop: | |||
148 | 147 | ||
149 | // terminating service | 148 | // terminating service |
150 | cancel() | 149 | cancel() |
151 | srv.Stop() | 150 | if err = srv.Stop(); err != nil { |
151 | logger.Printf(logger.ERROR, "[gns] Failed to stop service: %s", err.Error()) | ||
152 | } | ||
152 | } | 153 | } |
diff --git a/src/gnunet/cmd/gnunet-service-revocation-go/main.go b/src/gnunet/cmd/gnunet-service-revocation-go/main.go index 564f430..93c5432 100644 --- a/src/gnunet/cmd/gnunet-service-revocation-go/main.go +++ b/src/gnunet/cmd/gnunet-service-revocation-go/main.go | |||
@@ -21,7 +21,6 @@ package main | |||
21 | import ( | 21 | import ( |
22 | "context" | 22 | "context" |
23 | "flag" | 23 | "flag" |
24 | "net/rpc" | ||
25 | "os" | 24 | "os" |
26 | "os/signal" | 25 | "os/signal" |
27 | "strings" | 26 | "strings" |
@@ -109,8 +108,8 @@ func main() { | |||
109 | } | 108 | } |
110 | // start JSON-RPC server on request | 109 | // start JSON-RPC server on request |
111 | if ep := config.Cfg.RPC.Endpoint; len(ep) > 0 { | 110 | if ep := config.Cfg.RPC.Endpoint; len(ep) > 0 { |
112 | var rpc *rpc.Server | 111 | var rpc *service.JRPCServer |
113 | if rpc, err = service.StartRPC(ctx, ep); err != nil { | 112 | if rpc, err = service.RunRPCServer(ctx, ep); err != nil { |
114 | logger.Printf(logger.ERROR, "[revocation] RPC failed to start: %s", err.Error()) | 113 | logger.Printf(logger.ERROR, "[revocation] RPC failed to start: %s", err.Error()) |
115 | return | 114 | return |
116 | } | 115 | } |
@@ -148,5 +147,7 @@ loop: | |||
148 | 147 | ||
149 | // terminating service | 148 | // terminating service |
150 | cancel() | 149 | cancel() |
151 | srv.Stop() | 150 | if err := srv.Stop(); err != nil { |
151 | logger.Printf(logger.ERROR, "[revocation] Failed to stop service: %s", err.Error()) | ||
152 | } | ||
152 | } | 153 | } |
diff --git a/src/gnunet/cmd/peer_mockup/main.go b/src/gnunet/cmd/peer_mockup/main.go index 96f62da..4d29927 100644 --- a/src/gnunet/cmd/peer_mockup/main.go +++ b/src/gnunet/cmd/peer_mockup/main.go | |||
@@ -15,6 +15,7 @@ import ( | |||
15 | "gnunet/message" | 15 | "gnunet/message" |
16 | "gnunet/service" | 16 | "gnunet/service" |
17 | 17 | ||
18 | "github.com/bfix/gospel/crypto/ed25519" | ||
18 | "github.com/bfix/gospel/logger" | 19 | "github.com/bfix/gospel/logger" |
19 | ) | 20 | ) |
20 | 21 | ||
@@ -33,10 +34,9 @@ var ( | |||
33 | }, | 34 | }, |
34 | } | 35 | } |
35 | // configuration for remote node | 36 | // configuration for remote node |
36 | remoteCfg = "3GXXMNb5YpIUO7ejIR2Yy0Cf5texuLfDjHkXcqbPxkc=" | 37 | remoteCfg = "3GXXMNb5YpIUO7ejIR2Yy0Cf5texuLfDjHkXcqbPxkc=" |
37 | remoteAddr = "udp://172.17.0.5:2086" | ||
38 | 38 | ||
39 | // top-level variables used accross functions | 39 | // top-level variables used across functions |
40 | local *core.Peer // local peer (with private key) | 40 | local *core.Peer // local peer (with private key) |
41 | remote *core.Peer // remote peer | 41 | remote *core.Peer // remote peer |
42 | c *core.Core | 42 | c *core.Core |
@@ -79,7 +79,9 @@ func main() { | |||
79 | 79 | ||
80 | if !asServer { | 80 | if !asServer { |
81 | // we start the message exchange | 81 | // we start the message exchange |
82 | c.Send(ctx, remote.GetID(), message.NewTransportTCPWelcomeMsg(c.PeerID())) | 82 | if err := c.Send(ctx, remote.GetID(), message.NewTransportTCPWelcomeMsg(c.PeerID())); err != nil { |
83 | fmt.Printf("send message failed: %s", err.Error()) | ||
84 | } | ||
83 | } | 85 | } |
84 | 86 | ||
85 | // handle OS signals | 87 | // handle OS signals |
@@ -117,23 +119,27 @@ loop: | |||
117 | // process incoming messages and send responses; it is used for protocol exploration only. | 119 | // process incoming messages and send responses; it is used for protocol exploration only. |
118 | // it tries to mimick the message flow between "real" GNUnet peers. | 120 | // it tries to mimick the message flow between "real" GNUnet peers. |
119 | func process(ctx context.Context, ev *core.Event) { | 121 | func process(ctx context.Context, ev *core.Event) { |
120 | |||
121 | logger.Printf(logger.DBG, "<<< %s", ev.Msg.String()) | 122 | logger.Printf(logger.DBG, "<<< %s", ev.Msg.String()) |
122 | 123 | ||
123 | switch msg := ev.Msg.(type) { | 124 | switch msg := ev.Msg.(type) { |
124 | |||
125 | case *message.TransportTCPWelcomeMsg: | 125 | case *message.TransportTCPWelcomeMsg: |
126 | c.Send(ctx, ev.Peer, message.NewTransportPingMsg(ev.Peer, nil)) | 126 | if err := c.Send(ctx, ev.Peer, message.NewTransportPingMsg(ev.Peer, nil)); err != nil { |
127 | logger.Printf(logger.ERROR, "TransportTCPWelcomeMsg send failed: %s", err.Error()) | ||
128 | return | ||
129 | } | ||
127 | 130 | ||
128 | case *message.HelloMsg: | 131 | case *message.HelloMsg: |
129 | 132 | ||
130 | case *message.TransportPingMsg: | 133 | case *message.TransportPingMsg: |
131 | mOut := message.NewTransportPongMsg(msg.Challenge, nil) | 134 | mOut := message.NewTransportPongMsg(msg.Challenge, nil) |
132 | if err := mOut.Sign(local.PrvKey()); err != nil { | 135 | if err := mOut.Sign(local.PrvKey()); err != nil { |
133 | logger.Println(logger.ERROR, "PONG: signing failed") | 136 | logger.Printf(logger.ERROR, "PONG signing failed: %s", err.Error()) |
137 | return | ||
138 | } | ||
139 | if err := c.Send(ctx, ev.Peer, mOut); err != nil { | ||
140 | logger.Printf(logger.ERROR, "TransportPongMsg send failed: %s", err.Error()) | ||
134 | return | 141 | return |
135 | } | 142 | } |
136 | c.Send(ctx, ev.Peer, mOut) | ||
137 | logger.Printf(logger.DBG, ">>> %s", mOut) | 143 | logger.Printf(logger.DBG, ">>> %s", mOut) |
138 | 144 | ||
139 | case *message.TransportPongMsg: | 145 | case *message.TransportPongMsg: |
@@ -148,7 +154,9 @@ func process(ctx context.Context, ev *core.Event) { | |||
148 | case *message.SessionSynMsg: | 154 | case *message.SessionSynMsg: |
149 | mOut := message.NewSessionSynAckMsg() | 155 | mOut := message.NewSessionSynAckMsg() |
150 | mOut.Timestamp = msg.Timestamp | 156 | mOut.Timestamp = msg.Timestamp |
151 | c.Send(ctx, ev.Peer, mOut) | 157 | if err := c.Send(ctx, ev.Peer, mOut); err != nil { |
158 | logger.Printf(logger.ERROR, "SessionSynAckMsg send failed: %s", err.Error()) | ||
159 | } | ||
152 | logger.Printf(logger.DBG, ">>> %s", mOut) | 160 | logger.Printf(logger.DBG, ">>> %s", mOut) |
153 | 161 | ||
154 | case *message.SessionQuotaMsg: | 162 | case *message.SessionQuotaMsg: |
@@ -157,7 +165,9 @@ func process(ctx context.Context, ev *core.Event) { | |||
157 | 165 | ||
158 | case *message.SessionKeepAliveMsg: | 166 | case *message.SessionKeepAliveMsg: |
159 | mOut := message.NewSessionKeepAliveRespMsg(msg.Nonce) | 167 | mOut := message.NewSessionKeepAliveRespMsg(msg.Nonce) |
160 | c.Send(ctx, ev.Peer, mOut) | 168 | if err := c.Send(ctx, ev.Peer, mOut); err != nil { |
169 | logger.Printf(logger.ERROR, "SessionKeepAliveRespMsg send failed: %s", err.Error()) | ||
170 | } | ||
161 | logger.Printf(logger.DBG, ">>> %s", mOut) | 171 | logger.Printf(logger.DBG, ">>> %s", mOut) |
162 | 172 | ||
163 | case *message.EphemeralKeyMsg: | 173 | case *message.EphemeralKeyMsg: |
@@ -171,9 +181,13 @@ func process(ctx context.Context, ev *core.Event) { | |||
171 | } | 181 | } |
172 | remote.SetEphKeyMsg(msg) | 182 | remote.SetEphKeyMsg(msg) |
173 | mOut := local.EphKeyMsg() | 183 | mOut := local.EphKeyMsg() |
174 | c.Send(ctx, ev.Peer, mOut) | 184 | if err := c.Send(ctx, ev.Peer, mOut); err != nil { |
185 | logger.Printf(logger.ERROR, "EphKeyMsg send failed: %s", err.Error()) | ||
186 | } | ||
175 | logger.Printf(logger.DBG, ">>> %s", mOut) | 187 | logger.Printf(logger.DBG, ">>> %s", mOut) |
176 | secret = crypto.SharedSecret(local.EphPrvKey(), remote.EphKeyMsg().Public()) | 188 | pk := ed25519.NewPublicKeyFromBytes(remote.EphKeyMsg().Public().Data) |
189 | secret = crypto.SharedSecret(local.EphPrvKey(), pk) | ||
190 | fmt.Printf("Shared secret: %s\n", secret.String()) | ||
177 | 191 | ||
178 | default: | 192 | default: |
179 | fmt.Printf("!!! %v\n", msg) | 193 | fmt.Printf("!!! %v\n", msg) |
diff --git a/src/gnunet/cmd/revoke-zonekey/main.go b/src/gnunet/cmd/revoke-zonekey/main.go index 0713582..fe56946 100644 --- a/src/gnunet/cmd/revoke-zonekey/main.go +++ b/src/gnunet/cmd/revoke-zonekey/main.go | |||
@@ -43,10 +43,10 @@ import ( | |||
43 | 43 | ||
44 | // State of RevData calculation | 44 | // State of RevData calculation |
45 | const ( | 45 | const ( |
46 | S_NEW = iota // start new PoW calculation | 46 | StateNew = iota // start new PoW calculation |
47 | S_CONT // continue PoW calculation | 47 | StateCont // continue PoW calculation |
48 | S_DONE // PoW calculation done | 48 | StateDone // PoW calculation done |
49 | S_SIGNED // revocation data signed | 49 | StateSigned // revocation data signed |
50 | ) | 50 | ) |
51 | 51 | ||
52 | // RevData is the storage layout for persistent data used by this program. | 52 | // RevData is the storage layout for persistent data used by this program. |
@@ -67,7 +67,7 @@ func ReadRevData(filename string, bits int, zk *crypto.ZoneKey) (rd *RevData, er | |||
67 | Rd: revocation.NewRevDataCalc(zk), | 67 | Rd: revocation.NewRevDataCalc(zk), |
68 | Numbits: uint8(bits), | 68 | Numbits: uint8(bits), |
69 | T: util.NewRelativeTime(0), | 69 | T: util.NewRelativeTime(0), |
70 | State: S_NEW, | 70 | State: StateNew, |
71 | } | 71 | } |
72 | 72 | ||
73 | // read revocation object from file. If the file does not exist, a new | 73 | // read revocation object from file. If the file does not exist, a new |
@@ -80,24 +80,23 @@ func ReadRevData(filename string, bits int, zk *crypto.ZoneKey) (rd *RevData, er | |||
80 | dataBuf := make([]byte, rd.size()) | 80 | dataBuf := make([]byte, rd.size()) |
81 | var n int | 81 | var n int |
82 | if n, err = file.Read(dataBuf); err != nil { | 82 | if n, err = file.Read(dataBuf); err != nil { |
83 | err = fmt.Errorf("Error reading file: " + err.Error()) | 83 | err = fmt.Errorf("error reading file: " + err.Error()) |
84 | return | 84 | return |
85 | } | 85 | } |
86 | if n != len(dataBuf) { | 86 | if n != len(dataBuf) { |
87 | err = fmt.Errorf("File size mismatch") | 87 | err = fmt.Errorf("file size mismatch") |
88 | return | 88 | return |
89 | } | 89 | } |
90 | if err = data.Unmarshal(&rd, dataBuf); err != nil { | 90 | if err = data.Unmarshal(&rd, dataBuf); err != nil { |
91 | err = fmt.Errorf("File corrupted: " + err.Error()) | 91 | err = fmt.Errorf("file corrupted: " + err.Error()) |
92 | return | 92 | return |
93 | } | 93 | } |
94 | if !zk.Equal(&rd.Rd.RevData.ZoneKeySig.ZoneKey) { | 94 | if !zk.Equal(&rd.Rd.RevData.ZoneKeySig.ZoneKey) { |
95 | err = fmt.Errorf("Zone key mismatch") | 95 | err = fmt.Errorf("zone key mismatch") |
96 | return | 96 | return |
97 | } | 97 | } |
98 | bits = int(rd.Numbits) | ||
99 | if err = file.Close(); err != nil { | 98 | if err = file.Close(); err != nil { |
100 | err = fmt.Errorf("Error closing file: " + err.Error()) | 99 | err = fmt.Errorf("error closing file: " + err.Error()) |
101 | } | 100 | } |
102 | return | 101 | return |
103 | } | 102 | } |
@@ -106,24 +105,24 @@ func ReadRevData(filename string, bits int, zk *crypto.ZoneKey) (rd *RevData, er | |||
106 | func (r *RevData) Write(filename string) (err error) { | 105 | func (r *RevData) Write(filename string) (err error) { |
107 | var file *os.File | 106 | var file *os.File |
108 | if file, err = os.Create(filename); err != nil { | 107 | if file, err = os.Create(filename); err != nil { |
109 | return fmt.Errorf("Can't write to output file: " + err.Error()) | 108 | return fmt.Errorf("can't write to output file: " + err.Error()) |
110 | } | 109 | } |
111 | var buf []byte | 110 | var buf []byte |
112 | if buf, err = data.Marshal(r); err != nil { | 111 | if buf, err = data.Marshal(r); err != nil { |
113 | return fmt.Errorf("Internal error: " + err.Error()) | 112 | return fmt.Errorf("internal error: " + err.Error()) |
114 | } | 113 | } |
115 | if len(buf) != r.size() { | 114 | if len(buf) != r.size() { |
116 | return fmt.Errorf("Internal error: Buffer mismatch %d != %d", len(buf), r.size()) | 115 | return fmt.Errorf("internal error: Buffer mismatch %d != %d", len(buf), r.size()) |
117 | } | 116 | } |
118 | var n int | 117 | var n int |
119 | if n, err = file.Write(buf); err != nil { | 118 | if n, err = file.Write(buf); err != nil { |
120 | return fmt.Errorf("Can't write to output file: " + err.Error()) | 119 | return fmt.Errorf("can't write to output file: " + err.Error()) |
121 | } | 120 | } |
122 | if n != len(buf) { | 121 | if n != len(buf) { |
123 | return fmt.Errorf("Can't write data to output file!") | 122 | return fmt.Errorf("can't write data to output file") |
124 | } | 123 | } |
125 | if err = file.Close(); err != nil { | 124 | if err = file.Close(); err != nil { |
126 | return fmt.Errorf("Error closing file: " + err.Error()) | 125 | return fmt.Errorf("error closing file: " + err.Error()) |
127 | } | 126 | } |
128 | return | 127 | return |
129 | } | 128 | } |
@@ -138,7 +137,7 @@ func (r *RevData) size() int { | |||
138 | // | 137 | // |
139 | // (1) Generate the desired PoWs for the public zone key: | 138 | // (1) Generate the desired PoWs for the public zone key: |
140 | // This process can be started, stopped and resumed, so the long | 139 | // This process can be started, stopped and resumed, so the long |
141 | // calculation time (usually days or even weeks) can be interruped if | 140 | // calculation time (usually days or even weeks) can be interrupted if |
142 | // desired. For security reasons you should only pass the "-z" argument to | 141 | // desired. For security reasons you should only pass the "-z" argument to |
143 | // this step but not the "-k" argument (private key) as it is not required | 142 | // this step but not the "-k" argument (private key) as it is not required |
144 | // to calculate the PoWs. | 143 | // to calculate the PoWs. |
@@ -164,7 +163,7 @@ func main() { | |||
164 | zonekey string // zonekey to be revoked | 163 | zonekey string // zonekey to be revoked |
165 | prvkey string // private zonekey (base64-encoded key data) | 164 | prvkey string // private zonekey (base64-encoded key data) |
166 | testing bool // test mode (no minimum difficulty) | 165 | testing bool // test mode (no minimum difficulty) |
167 | filename string // name of file for persistance | 166 | filename string // name of file for persistence |
168 | ) | 167 | ) |
169 | minDiff := revocation.MinDifficulty | 168 | minDiff := revocation.MinDifficulty |
170 | flag.IntVar(&bits, "b", minDiff+1, "Number of leading zero bits") | 169 | flag.IntVar(&bits, "b", minDiff+1, "Number of leading zero bits") |
@@ -227,27 +226,27 @@ func main() { | |||
227 | 226 | ||
228 | // handle revocation data state | 227 | // handle revocation data state |
229 | switch rd.State { | 228 | switch rd.State { |
230 | case S_NEW: | 229 | case StateNew: |
231 | log.Println("Starting new revocation calculation...") | 230 | log.Println("Starting new revocation calculation...") |
232 | rd.State = S_CONT | 231 | rd.State = StateCont |
233 | 232 | ||
234 | case S_CONT: | 233 | case StateCont: |
235 | log.Printf("Revocation calculation started at %s\n", rd.Rd.Timestamp.String()) | 234 | log.Printf("Revocation calculation started at %s\n", rd.Rd.Timestamp.String()) |
236 | log.Printf("Time spent on calculation: %s\n", rd.T.String()) | 235 | log.Printf("Time spent on calculation: %s\n", rd.T.String()) |
237 | log.Printf("Last tested PoW value: %d\n", rd.Last) | 236 | log.Printf("Last tested PoW value: %d\n", rd.Last) |
238 | log.Println("Continuing...") | 237 | log.Println("Continuing...") |
239 | 238 | ||
240 | case S_DONE: | 239 | case StateDone: |
241 | // calculation complete: sign with private key | 240 | // calculation complete: sign with private key |
242 | if sk == nil { | 241 | if sk == nil { |
243 | log.Fatal("Need to sign revocation: private key is missing.") | 242 | log.Fatal("Need to sign revocation: private key is missing.") |
244 | } | 243 | } |
245 | log.Println("Signing revocation with private key") | 244 | log.Println("Signing revocation with private key") |
246 | if err := rd.Rd.Sign(sk); err != nil { | 245 | if err = rd.Rd.Sign(sk); err != nil { |
247 | log.Fatal("Failed to sign revocation: " + err.Error()) | 246 | log.Fatal("Failed to sign revocation: " + err.Error()) |
248 | } | 247 | } |
249 | // write final revocation | 248 | // write final revocation |
250 | rd.State = S_SIGNED | 249 | rd.State = StateSigned |
251 | if err = rd.Write(filename); err != nil { | 250 | if err = rd.Write(filename); err != nil { |
252 | log.Fatal("Failed to write revocation: " + err.Error()) | 251 | log.Fatal("Failed to write revocation: " + err.Error()) |
253 | } | 252 | } |
@@ -278,10 +277,10 @@ func main() { | |||
278 | // The calculation was interrupted; we still need to compute | 277 | // The calculation was interrupted; we still need to compute |
279 | // more and better PoWs... | 278 | // more and better PoWs... |
280 | log.Printf("Incomplete revocation: Only %f zero bits on average!\n", average) | 279 | log.Printf("Incomplete revocation: Only %f zero bits on average!\n", average) |
281 | rd.State = S_CONT | 280 | rd.State = StateCont |
282 | } else { | 281 | } else { |
283 | // we have reached the required PoW difficulty | 282 | // we have reached the required PoW difficulty |
284 | rd.State = S_DONE | 283 | rd.State = StateDone |
285 | // check if we have a valid revocation. | 284 | // check if we have a valid revocation. |
286 | log.Println("Revocation calculation complete:") | 285 | log.Println("Revocation calculation complete:") |
287 | diff, rc := rd.Rd.Verify(false) | 286 | diff, rc := rd.Rd.Verify(false) |
@@ -313,22 +312,19 @@ func main() { | |||
313 | sigCh := make(chan os.Signal, 5) | 312 | sigCh := make(chan os.Signal, 5) |
314 | signal.Notify(sigCh) | 313 | signal.Notify(sigCh) |
315 | loop: | 314 | loop: |
316 | for { | 315 | for sig := range sigCh { |
317 | select { | ||
318 | // handle OS signals | 316 | // handle OS signals |
319 | case sig := <-sigCh: | 317 | switch sig { |
320 | switch sig { | 318 | case syscall.SIGKILL, syscall.SIGINT, syscall.SIGTERM: |
321 | case syscall.SIGKILL, syscall.SIGINT, syscall.SIGTERM: | 319 | log.Printf("Terminating (on signal '%s')\n", sig) |
322 | log.Printf("Terminating (on signal '%s')\n", sig) | 320 | cancelFcn() |
323 | cancelFcn() | 321 | break loop |
324 | break loop | 322 | case syscall.SIGHUP: |
325 | case syscall.SIGHUP: | 323 | log.Println("SIGHUP") |
326 | log.Println("SIGHUP") | 324 | case syscall.SIGURG: |
327 | case syscall.SIGURG: | 325 | // TODO: https://github.com/golang/go/issues/37942 |
328 | // TODO: https://github.com/golang/go/issues/37942 | 326 | default: |
329 | default: | 327 | log.Println("Unhandled signal: " + sig.String()) |
330 | log.Println("Unhandled signal: " + sig.String()) | ||
331 | } | ||
332 | } | 328 | } |
333 | } | 329 | } |
334 | }() | 330 | }() |
diff --git a/src/gnunet/cmd/vanityid/main.go b/src/gnunet/cmd/vanityid/main.go index 938df61..1c0bf24 100644 --- a/src/gnunet/cmd/vanityid/main.go +++ b/src/gnunet/cmd/vanityid/main.go | |||
@@ -8,8 +8,9 @@ import ( | |||
8 | "regexp" | 8 | "regexp" |
9 | "time" | 9 | "time" |
10 | 10 | ||
11 | "github.com/bfix/gospel/crypto/ed25519" | ||
12 | "gnunet/util" | 11 | "gnunet/util" |
12 | |||
13 | "github.com/bfix/gospel/crypto/ed25519" | ||
13 | ) | 14 | ) |
14 | 15 | ||
15 | func main() { | 16 | func main() { |
@@ -32,16 +33,13 @@ func main() { | |||
32 | seed := make([]byte, 32) | 33 | seed := make([]byte, 32) |
33 | start := time.Now() | 34 | start := time.Now() |
34 | for i := 0; ; i++ { | 35 | for i := 0; ; i++ { |
35 | n, err := rand.Read(seed) | 36 | _, _ = rand.Read(seed) |
36 | if err != nil || n != 32 { | ||
37 | panic(err) | ||
38 | } | ||
39 | prv := ed25519.NewPrivateKeyFromSeed(seed) | 37 | prv := ed25519.NewPrivateKeyFromSeed(seed) |
40 | pub := prv.Public().Bytes() | 38 | pub := prv.Public().Bytes() |
41 | id := util.EncodeBinaryToString(pub) | 39 | id := util.EncodeBinaryToString(pub) |
42 | for _, r := range reg { | 40 | for _, r := range reg { |
43 | if r.MatchString(id) { | 41 | if r.MatchString(id) { |
44 | elapsed := time.Now().Sub(start) | 42 | elapsed := time.Since(start) |
45 | s1 := hex.EncodeToString(seed) | 43 | s1 := hex.EncodeToString(seed) |
46 | s2 := hex.EncodeToString(prv.D.Bytes()) | 44 | s2 := hex.EncodeToString(prv.D.Bytes()) |
47 | fmt.Printf("%s [%s][%s] (%d tries, %s elapsed)\n", id, s1, s2, i, elapsed) | 45 | fmt.Printf("%s [%s][%s] (%d tries, %s elapsed)\n", id, s1, s2, i, elapsed) |
diff --git a/src/gnunet/config/config.go b/src/gnunet/config/config.go index b4d2840..c4d3722 100644 --- a/src/gnunet/config/config.go +++ b/src/gnunet/config/config.go | |||
@@ -21,6 +21,7 @@ package config | |||
21 | import ( | 21 | import ( |
22 | "encoding/json" | 22 | "encoding/json" |
23 | "fmt" | 23 | "fmt" |
24 | "gnunet/util" | ||
24 | "io/ioutil" | 25 | "io/ioutil" |
25 | "reflect" | 26 | "reflect" |
26 | "regexp" | 27 | "regexp" |
@@ -56,12 +57,13 @@ type NodeConfig struct { | |||
56 | } | 57 | } |
57 | 58 | ||
58 | //---------------------------------------------------------------------- | 59 | //---------------------------------------------------------------------- |
59 | // Bootstrap configuration | 60 | // Network configuration |
60 | //---------------------------------------------------------------------- | 61 | //---------------------------------------------------------------------- |
61 | 62 | ||
62 | // BootstrapConfig holds parameters for the initial connection to the network. | 63 | // NetworkConfig holds parameters for the initial connection to the network. |
63 | type BootstrapConfig struct { | 64 | type NetworkConfig struct { |
64 | Nodes []string `json:"nodes"` // bootstrap nodes | 65 | Bootstrap []string `json:"bootstrap"` // bootstrap nodes |
66 | NumPeers int `json:"numPeers"` // estimated number of peers (0 = use NSE) | ||
65 | } | 67 | } |
66 | 68 | ||
67 | //---------------------------------------------------------------------- | 69 | //---------------------------------------------------------------------- |
@@ -94,33 +96,20 @@ type GNSConfig struct { | |||
94 | } | 96 | } |
95 | 97 | ||
96 | //---------------------------------------------------------------------- | 98 | //---------------------------------------------------------------------- |
97 | // Generic parameter configuration (handle any key/value settings) | ||
98 | //---------------------------------------------------------------------- | ||
99 | |||
100 | // ParameterConfig handle arbitrary values for a key strings. This necessary | ||
101 | // e.g. in the 'Storage' configuration, as custom storage implementations | ||
102 | // require different sets of parameters. | ||
103 | type ParameterConfig map[string]any | ||
104 | |||
105 | // Get a parameter value with given type 'V' | ||
106 | func GetParam[V any](params ParameterConfig, key string) (i V, ok bool) { | ||
107 | var v any | ||
108 | if v, ok = params[key]; ok { | ||
109 | if i, ok = v.(V); ok { | ||
110 | return | ||
111 | } | ||
112 | } | ||
113 | return | ||
114 | } | ||
115 | |||
116 | //---------------------------------------------------------------------- | ||
117 | // DHT configuration | 99 | // DHT configuration |
118 | //---------------------------------------------------------------------- | 100 | //---------------------------------------------------------------------- |
119 | 101 | ||
120 | // DHTConfig contains parameters for the distributed hash table (DHT) | 102 | // DHTConfig contains parameters for the distributed hash table (DHT) |
121 | type DHTConfig struct { | 103 | type DHTConfig struct { |
122 | Service *ServiceConfig `json:"service"` // socket for DHT service | 104 | Service *ServiceConfig `json:"service"` // socket for DHT service |
123 | Storage ParameterConfig `json:"storage"` // filesystem storage location | 105 | Storage util.ParameterSet `json:"storage"` // filesystem storage location |
106 | Routing *RoutingConfig `json:"routing"` // routing table configuration | ||
107 | Heartbeat int `json:"heartbeat"` // heartbeat intervall | ||
108 | } | ||
109 | |||
110 | // RoutingConfig holds parameters for routing tables | ||
111 | type RoutingConfig struct { | ||
112 | PeerTTL int `json:"peerTTL"` // time-out for peers in table | ||
124 | } | 113 | } |
125 | 114 | ||
126 | //---------------------------------------------------------------------- | 115 | //---------------------------------------------------------------------- |
@@ -129,8 +118,8 @@ type DHTConfig struct { | |||
129 | 118 | ||
130 | // NamecacheConfig contains parameters for the local name cache | 119 | // NamecacheConfig contains parameters for the local name cache |
131 | type NamecacheConfig struct { | 120 | type NamecacheConfig struct { |
132 | Service *ServiceConfig `json:"service"` // socket for Namecache service | 121 | Service *ServiceConfig `json:"service"` // socket for Namecache service |
133 | Storage ParameterConfig `json:"storage"` // key/value cache | 122 | Storage util.ParameterSet `json:"storage"` // key/value cache |
134 | } | 123 | } |
135 | 124 | ||
136 | //---------------------------------------------------------------------- | 125 | //---------------------------------------------------------------------- |
@@ -139,8 +128,8 @@ type NamecacheConfig struct { | |||
139 | 128 | ||
140 | // RevocationConfig contains parameters for the key revocation service | 129 | // RevocationConfig contains parameters for the key revocation service |
141 | type RevocationConfig struct { | 130 | type RevocationConfig struct { |
142 | Service *ServiceConfig `json:"service"` // socket for Revocation service | 131 | Service *ServiceConfig `json:"service"` // socket for Revocation service |
143 | Storage ParameterConfig `json:"storage"` // persistance mechanism for revocation data | 132 | Storage util.ParameterSet `json:"storage"` // persistence mechanism for revocation data |
144 | } | 133 | } |
145 | 134 | ||
146 | //---------------------------------------------------------------------- | 135 | //---------------------------------------------------------------------- |
@@ -153,7 +142,7 @@ type Environment map[string]string | |||
153 | // Config is the aggregated configuration for GNUnet. | 142 | // Config is the aggregated configuration for GNUnet. |
154 | type Config struct { | 143 | type Config struct { |
155 | Local *NodeConfig `json:"local"` | 144 | Local *NodeConfig `json:"local"` |
156 | Bootstrap *BootstrapConfig `json:"bootstrap"` | 145 | Network *NetworkConfig `json:"network"` |
157 | Env Environment `json:"environ"` | 146 | Env Environment `json:"environ"` |
158 | RPC *RPCConfig `json:"rpc"` | 147 | RPC *RPCConfig `json:"rpc"` |
159 | DHT *DHTConfig `json:"dht"` | 148 | DHT *DHTConfig `json:"dht"` |
@@ -193,7 +182,7 @@ func ParseConfigBytes(data []byte, subst bool) (err error) { | |||
193 | } | 182 | } |
194 | 183 | ||
195 | var ( | 184 | var ( |
196 | rx = regexp.MustCompile("\\$\\{([^\\}]*)\\}") | 185 | rx = regexp.MustCompile(`\$\{([^\}]*)\}`) |
197 | ) | 186 | ) |
198 | 187 | ||
199 | // substString is a helper function to substitute environment variables | 188 | // substString is a helper function to substitute environment variables |
@@ -215,7 +204,6 @@ func substString(s string, env map[string]string) string { | |||
215 | // applySubstitutions traverses the configuration data structure | 204 | // applySubstitutions traverses the configuration data structure |
216 | // and applies string substitutions to all string values. | 205 | // and applies string substitutions to all string values. |
217 | func applySubstitutions(x interface{}, env map[string]string) { | 206 | func applySubstitutions(x interface{}, env map[string]string) { |
218 | |||
219 | var process func(v reflect.Value) | 207 | var process func(v reflect.Value) |
220 | process = func(v reflect.Value) { | 208 | process = func(v reflect.Value) { |
221 | for i := 0; i < v.NumField(); i++ { | 209 | for i := 0; i < v.NumField(); i++ { |
@@ -224,7 +212,7 @@ func applySubstitutions(x interface{}, env map[string]string) { | |||
224 | switch fld.Kind() { | 212 | switch fld.Kind() { |
225 | case reflect.String: | 213 | case reflect.String: |
226 | // check for substitution | 214 | // check for substitution |
227 | s := fld.Interface().(string) | 215 | s, _ := fld.Interface().(string) |
228 | for { | 216 | for { |
229 | s1 := substString(s, env) | 217 | s1 := substString(s, env) |
230 | if s1 == s { | 218 | if s1 == s { |
diff --git a/src/gnunet/config/config_test.go b/src/gnunet/config/config_test.go index d82019e..b61cb67 100644 --- a/src/gnunet/config/config_test.go +++ b/src/gnunet/config/config_test.go | |||
@@ -35,7 +35,7 @@ func TestConfigRead(t *testing.T) { | |||
35 | t.Fatal(err) | 35 | t.Fatal(err) |
36 | } | 36 | } |
37 | // parse configuration | 37 | // parse configuration |
38 | if err := ParseConfigBytes(data, false); err != nil { | 38 | if err = ParseConfigBytes(data, false); err != nil { |
39 | t.Fatal(err) | 39 | t.Fatal(err) |
40 | } | 40 | } |
41 | // write configuration | 41 | // write configuration |
diff --git a/src/gnunet/config/gnunet-config.json b/src/gnunet/config/gnunet-config.json index 167bfa0..27678ab 100644 --- a/src/gnunet/config/gnunet-config.json +++ b/src/gnunet/config/gnunet-config.json | |||
@@ -1,8 +1,10 @@ | |||
1 | { | 1 | { |
2 | "bootstrap": { | 2 | "network": { |
3 | "nodes": [ | 3 | "bootstrap": [ |
4 | "ip+udp://172.17.0.5:10000", | ||
4 | "gnunet://hello/7KTBJ90340HF1Q2GB0A57E2XJER4FDHX8HP5GHEB9125VPWPD27G/BNMDFN6HJCPWSPNBSEC06MC1K8QN1Z2DHRQSRXDTFR7FTBD4JHNBJ2RJAAEZ31FWG1Q3PMN3PXGZQ3Q7NTNEKQZFA7TE2Y46FM8E20R/1653499308?r5n+ip+udp=127.0.0.1%3A7654" | 5 | "gnunet://hello/7KTBJ90340HF1Q2GB0A57E2XJER4FDHX8HP5GHEB9125VPWPD27G/BNMDFN6HJCPWSPNBSEC06MC1K8QN1Z2DHRQSRXDTFR7FTBD4JHNBJ2RJAAEZ31FWG1Q3PMN3PXGZQ3Q7NTNEKQZFA7TE2Y46FM8E20R/1653499308?r5n+ip+udp=127.0.0.1%3A7654" |
5 | ] | 6 | ], |
7 | "numPeers": 10 | ||
6 | }, | 8 | }, |
7 | "local": { | 9 | "local": { |
8 | "name": "ygng", | 10 | "name": "ygng", |
@@ -33,7 +35,11 @@ | |||
33 | "cache": false, | 35 | "cache": false, |
34 | "path": "/var/lib/gnunet/dht/store", | 36 | "path": "/var/lib/gnunet/dht/store", |
35 | "maxGB": 10 | 37 | "maxGB": 10 |
36 | } | 38 | }, |
39 | "routing": { | ||
40 | "peerTTL": 10800 | ||
41 | }, | ||
42 | "heartbeat": 900 | ||
37 | }, | 43 | }, |
38 | "gns": { | 44 | "gns": { |
39 | "service": { | 45 | "service": { |
diff --git a/src/gnunet/core/core.go b/src/gnunet/core/core.go index 83da8b1..128d154 100644 --- a/src/gnunet/core/core.go +++ b/src/gnunet/core/core.go | |||
@@ -20,10 +20,10 @@ package core | |||
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | 22 | "context" |
23 | "encoding/hex" | ||
23 | "errors" | 24 | "errors" |
24 | "gnunet/config" | 25 | "gnunet/config" |
25 | "gnunet/message" | 26 | "gnunet/message" |
26 | "gnunet/service/dht/blocks" | ||
27 | "gnunet/transport" | 27 | "gnunet/transport" |
28 | "gnunet/util" | 28 | "gnunet/util" |
29 | "net" | 29 | "net" |
@@ -47,7 +47,7 @@ type EndpointRef struct { | |||
47 | id string // endpoint identifier in configuration | 47 | id string // endpoint identifier in configuration |
48 | ep transport.Endpoint // reference to endpoint | 48 | ep transport.Endpoint // reference to endpoint |
49 | addr *util.Address // public endpoint address | 49 | addr *util.Address // public endpoint address |
50 | upnpId string // UPNP identifier (empty if unused) | 50 | upnpID string // UPNP identifier (empty if unused) |
51 | } | 51 | } |
52 | 52 | ||
53 | //---------------------------------------------------------------------- | 53 | //---------------------------------------------------------------------- |
@@ -57,7 +57,7 @@ type Core struct { | |||
57 | local *Peer | 57 | local *Peer |
58 | 58 | ||
59 | // incoming messages from transport | 59 | // incoming messages from transport |
60 | incoming chan *transport.TransportMessage | 60 | incoming chan *transport.Message |
61 | 61 | ||
62 | // reference to transport implementation | 62 | // reference to transport implementation |
63 | trans *transport.Transport | 63 | trans *transport.Transport |
@@ -68,37 +68,39 @@ type Core struct { | |||
68 | // list of known peers with addresses | 68 | // list of known peers with addresses |
69 | peers *util.PeerAddrList | 69 | peers *util.PeerAddrList |
70 | 70 | ||
71 | // list of connected peers | ||
72 | connected *util.Map[string, bool] | ||
73 | |||
71 | // List of registered endpoints | 74 | // List of registered endpoints |
72 | endpoints map[string]*EndpointRef | 75 | endpoints map[string]*EndpointRef |
73 | |||
74 | // last HELLO message used; re-create if expired | ||
75 | lastHello *message.HelloDHTMsg | ||
76 | } | 76 | } |
77 | 77 | ||
78 | //---------------------------------------------------------------------- | 78 | //---------------------------------------------------------------------- |
79 | 79 | ||
80 | // NewCore creates and runs a new core instance. | 80 | // NewCore creates and runs a new core instance. |
81 | func NewCore(ctx context.Context, node *config.NodeConfig) (c *Core, err error) { | 81 | func NewCore(ctx context.Context, node *config.NodeConfig) (c *Core, err error) { |
82 | |||
83 | // instantiate peer | 82 | // instantiate peer |
84 | var peer *Peer | 83 | var peer *Peer |
85 | if peer, err = NewLocalPeer(node); err != nil { | 84 | if peer, err = NewLocalPeer(node); err != nil { |
86 | return | 85 | return |
87 | } | 86 | } |
87 | logger.Printf(logger.DBG, "[core] Local node is %s", peer.GetID().String()) | ||
88 | |||
88 | // create new core instance | 89 | // create new core instance |
89 | incoming := make(chan *transport.TransportMessage) | 90 | incoming := make(chan *transport.Message) |
90 | c = &Core{ | 91 | c = &Core{ |
91 | local: peer, | 92 | local: peer, |
92 | incoming: incoming, | 93 | incoming: incoming, |
93 | listeners: make(map[string]*Listener), | 94 | listeners: make(map[string]*Listener), |
94 | trans: transport.NewTransport(ctx, node.Name, incoming), | 95 | trans: transport.NewTransport(ctx, node.Name, incoming), |
95 | peers: util.NewPeerAddrList(), | 96 | peers: util.NewPeerAddrList(), |
97 | connected: util.NewMap[string, bool](), | ||
96 | endpoints: make(map[string]*EndpointRef), | 98 | endpoints: make(map[string]*EndpointRef), |
97 | } | 99 | } |
98 | // add all local peer endpoints to transport. | 100 | // add all local peer endpoints to transport. |
99 | for _, epCfg := range node.Endpoints { | 101 | for _, epCfg := range node.Endpoints { |
100 | var ( | 102 | var ( |
101 | upnpId string // upnp identifier | 103 | upnpID string // upnp identifier |
102 | local *util.Address // local address | 104 | local *util.Address // local address |
103 | remote *util.Address // remote address | 105 | remote *util.Address // remote address |
104 | ep transport.Endpoint // endpoint reference | 106 | ep transport.Endpoint // endpoint reference |
@@ -113,7 +115,7 @@ func NewCore(ctx context.Context, node *config.NodeConfig) (c *Core, err error) | |||
113 | // handle UPNP port forwarding | 115 | // handle UPNP port forwarding |
114 | protocol := transport.EpProtocol(epCfg.Network) | 116 | protocol := transport.EpProtocol(epCfg.Network) |
115 | var localA, remoteA string | 117 | var localA, remoteA string |
116 | if upnpId, remoteA, localA, err = c.trans.ForwardOpen(protocol, epCfg.Address[5:], epCfg.Port); err != nil { | 118 | if upnpID, remoteA, localA, err = c.trans.ForwardOpen(protocol, epCfg.Address[5:], epCfg.Port); err != nil { |
117 | return | 119 | return |
118 | } | 120 | } |
119 | // parse local and remote addresses | 121 | // parse local and remote addresses |
@@ -129,7 +131,7 @@ func NewCore(ctx context.Context, node *config.NodeConfig) (c *Core, err error) | |||
129 | return | 131 | return |
130 | } | 132 | } |
131 | remote = local | 133 | remote = local |
132 | upnpId = "" | 134 | upnpID = "" |
133 | } | 135 | } |
134 | // add endpoint for address | 136 | // add endpoint for address |
135 | if ep, err = c.trans.AddEndpoint(ctx, local); err != nil { | 137 | if ep, err = c.trans.AddEndpoint(ctx, local); err != nil { |
@@ -148,7 +150,7 @@ func NewCore(ctx context.Context, node *config.NodeConfig) (c *Core, err error) | |||
148 | id: epCfg.ID, | 150 | id: epCfg.ID, |
149 | ep: ep, | 151 | ep: ep, |
150 | addr: remote, | 152 | addr: remote, |
151 | upnpId: upnpId, | 153 | upnpID: upnpID, |
152 | } | 154 | } |
153 | } | 155 | } |
154 | // run message pump | 156 | // run message pump |
@@ -165,35 +167,20 @@ func (c *Core) pump(ctx context.Context) { | |||
165 | case tm := <-c.incoming: | 167 | case tm := <-c.incoming: |
166 | logger.Printf(logger.DBG, "[core] Message received from %s: %s", tm.Peer, transport.Dump(tm.Msg, "json")) | 168 | logger.Printf(logger.DBG, "[core] Message received from %s: %s", tm.Peer, transport.Dump(tm.Msg, "json")) |
167 | 169 | ||
168 | // inspect message for peer state events | 170 | // check if peer is already connected (has an entry in PeerAddrist) |
169 | var ev *Event | 171 | _, connected := c.connected.Get(tm.Peer.String()) |
170 | switch msg := tm.Msg.(type) { | 172 | if !connected { |
171 | case *message.HelloDHTMsg: | 173 | // no: mark connected |
172 | 174 | c.connected.Put(tm.Peer.String(), true) | |
173 | // verify integrity of message | ||
174 | if ok, err := msg.Verify(tm.Peer); !ok || err != nil { | ||
175 | logger.Println(logger.WARN, "[core] Received invalid DHT_P2P_HELLO message") | ||
176 | break | ||
177 | } | ||
178 | // keep peer addresses | ||
179 | aList, err := msg.Addresses() | ||
180 | if err != nil { | ||
181 | logger.Println(logger.WARN, "[core] Failed to parse addresses from DHT_P2P_HELLO message") | ||
182 | break | ||
183 | } | ||
184 | if err := c.Learn(ctx, tm.Peer, aList); err != nil { | ||
185 | logger.Println(logger.WARN, "[core] Failed to learn addresses from DHT_P2P_HELLO message: "+err.Error()) | ||
186 | break | ||
187 | } | ||
188 | |||
189 | // generate EV_CONNECT event | 175 | // generate EV_CONNECT event |
190 | ev = &Event{ | 176 | c.dispatch(&Event{ |
191 | ID: EV_CONNECT, | 177 | ID: EV_CONNECT, |
192 | Peer: tm.Peer, | 178 | Peer: tm.Peer, |
193 | Msg: msg, | 179 | }) |
194 | } | 180 | // grace period for connection signal |
195 | c.dispatch(ev) | 181 | time.Sleep(time.Second) |
196 | } | 182 | } |
183 | |||
197 | // set default responder (core) if no custom responder | 184 | // set default responder (core) if no custom responder |
198 | // is defined by the receiving endpoint. | 185 | // is defined by the receiving endpoint. |
199 | resp := tm.Resp | 186 | resp := tm.Resp |
@@ -204,13 +191,12 @@ func (c *Core) pump(ctx context.Context) { | |||
204 | } | 191 | } |
205 | } | 192 | } |
206 | // generate EV_MESSAGE event | 193 | // generate EV_MESSAGE event |
207 | ev = &Event{ | 194 | c.dispatch(&Event{ |
208 | ID: EV_MESSAGE, | 195 | ID: EV_MESSAGE, |
209 | Peer: tm.Peer, | 196 | Peer: tm.Peer, |
210 | Msg: tm.Msg, | 197 | Msg: tm.Msg, |
211 | Resp: tm.Resp, | 198 | Resp: resp, |
212 | } | 199 | }) |
213 | c.dispatch(ev) | ||
214 | 200 | ||
215 | // wait for termination | 201 | // wait for termination |
216 | case <-ctx.Done(): | 202 | case <-ctx.Done(): |
@@ -234,12 +220,12 @@ func (c *Core) Send(ctx context.Context, peer *util.PeerID, msg message.Message) | |||
234 | netw := "ip+udp" | 220 | netw := "ip+udp" |
235 | 221 | ||
236 | // try all addresses for peer | 222 | // try all addresses for peer |
237 | aList := c.peers.Get(peer.String(), netw) | 223 | aList := c.peers.Get(peer, netw) |
238 | maybe := false // message may be sent... | 224 | maybe := false // message may be sent... |
239 | for _, addr := range aList { | 225 | for _, addr := range aList { |
240 | logger.Printf(logger.WARN, "[core] Trying to send to %s", addr.URI()) | 226 | logger.Printf(logger.INFO, "[core] Trying to send to %s", addr.URI()) |
241 | // send message to address | 227 | // send message to address |
242 | if err = c.send(ctx, addr, msg); err != nil { | 228 | if err = c.SendToAddr(ctx, addr, msg); err != nil { |
243 | // if it is possible that the message was not sent, try next address | 229 | // if it is possible that the message was not sent, try next address |
244 | if err != transport.ErrEndpMaybeSent { | 230 | if err != transport.ErrEndpMaybeSent { |
245 | logger.Printf(logger.WARN, "[core] Failed to send to %s: %s", addr.URI(), err.Error()) | 231 | logger.Printf(logger.WARN, "[core] Failed to send to %s: %s", addr.URI(), err.Error()) |
@@ -252,15 +238,16 @@ func (c *Core) Send(ctx context.Context, peer *util.PeerID, msg message.Message) | |||
252 | return | 238 | return |
253 | } | 239 | } |
254 | if maybe { | 240 | if maybe { |
255 | err = transport.ErrEndpMaybeSent | 241 | logger.Printf(logger.WARN, "[core] %s", transport.ErrEndpMaybeSent.Error()) |
242 | err = nil | ||
256 | } else { | 243 | } else { |
257 | err = ErrCoreNotSent | 244 | err = ErrCoreNotSent |
258 | } | 245 | } |
259 | return | 246 | return |
260 | } | 247 | } |
261 | 248 | ||
262 | // send message directly to address | 249 | // SendToAddr message directly to address |
263 | func (c *Core) send(ctx context.Context, addr *util.Address, msg message.Message) error { | 250 | func (c *Core) SendToAddr(ctx context.Context, addr *util.Address, msg message.Message) error { |
264 | // assemble transport message | 251 | // assemble transport message |
265 | tm := transport.NewTransportMessage(c.PeerID(), msg) | 252 | tm := transport.NewTransportMessage(c.PeerID(), msg) |
266 | // send on transport | 253 | // send on transport |
@@ -268,81 +255,16 @@ func (c *Core) send(ctx context.Context, addr *util.Address, msg message.Message | |||
268 | } | 255 | } |
269 | 256 | ||
270 | // Learn (new) addresses for peer | 257 | // Learn (new) addresses for peer |
271 | func (c *Core) Learn(ctx context.Context, peer *util.PeerID, addrs []*util.Address) (err error) { | 258 | func (c *Core) Learn(ctx context.Context, peer *util.PeerID, addrs []*util.Address) (newPeer bool) { |
272 | // learn all addresses for peer | 259 | // learn all addresses for peer |
273 | newPeer := false | 260 | newPeer = false |
274 | for _, addr := range addrs { | 261 | for _, addr := range addrs { |
275 | logger.Printf(logger.INFO, "[core] Learning %s for %s (expires %s)", addr.URI(), peer, addr.Expires) | 262 | logger.Printf(logger.INFO, "[core] Learning %s for %s (expires %s)", addr.URI(), peer, addr.Expires) |
276 | newPeer = (c.peers.Add(peer.String(), addr) == 1) || newPeer | 263 | newPeer = (c.peers.Add(peer, addr) == 1) || newPeer |
277 | } | ||
278 | // new peer detected? | ||
279 | if newPeer { | ||
280 | // we added a previously unknown peer: send a HELLO | ||
281 | var msg *message.HelloDHTMsg | ||
282 | if msg, err = c.getHello(); err != nil { | ||
283 | return | ||
284 | } | ||
285 | logger.Printf(logger.INFO, "[core] Sending HELLO to %s: %s", peer, msg) | ||
286 | err = c.Send(ctx, peer, msg) | ||
287 | // no error if the message might have been sent | ||
288 | if err == transport.ErrEndpMaybeSent { | ||
289 | err = nil | ||
290 | } | ||
291 | } | 264 | } |
292 | return | 265 | return |
293 | } | 266 | } |
294 | 267 | ||
295 | // Send the currently active HELLO to given network address | ||
296 | func (c *Core) SendHello(ctx context.Context, addr *util.Address) (err error) { | ||
297 | // get (buffered) HELLO | ||
298 | var msg *message.HelloDHTMsg | ||
299 | if msg, err = c.getHello(); err != nil { | ||
300 | return | ||
301 | } | ||
302 | logger.Printf(logger.INFO, "[core] Sending HELLO to %s: %s", addr.URI(), msg) | ||
303 | return c.send(ctx, addr, msg) | ||
304 | } | ||
305 | |||
306 | // get the recent HELLO if it is defined and not expired; | ||
307 | // create a new HELLO otherwise. | ||
308 | func (c *Core) getHello() (msg *message.HelloDHTMsg, err error) { | ||
309 | if c.lastHello == nil || c.lastHello.Expires.Expired() { | ||
310 | // assemble new HELLO message | ||
311 | addrList := make([]*util.Address, 0) | ||
312 | for _, epRef := range c.endpoints { | ||
313 | addrList = append(addrList, epRef.addr) | ||
314 | } | ||
315 | node := c.local | ||
316 | var hello *blocks.HelloBlock | ||
317 | hello, err = node.HelloData(time.Hour, addrList) | ||
318 | if err != nil { | ||
319 | return | ||
320 | } | ||
321 | msg = message.NewHelloDHTMsg() | ||
322 | msg.NumAddr = uint16(len(hello.Addresses())) | ||
323 | msg.SetAddresses(hello.Addresses()) | ||
324 | if err = msg.Sign(c.local.prv); err != nil { | ||
325 | return | ||
326 | } | ||
327 | // save for later use | ||
328 | c.lastHello = msg | ||
329 | |||
330 | // DEBUG | ||
331 | var ok bool | ||
332 | if ok, err = msg.Verify(c.PeerID()); !ok || err != nil { | ||
333 | if !ok { | ||
334 | err = errors.New("[core] failed to verify own HELLO") | ||
335 | } | ||
336 | logger.Println(logger.ERROR, err.Error()) | ||
337 | return | ||
338 | } | ||
339 | logger.Println(logger.DBG, "[core] New HELLO: "+transport.Dump(msg, "json")) | ||
340 | return | ||
341 | } | ||
342 | // we have a valid HELLO for re-use. | ||
343 | return c.lastHello, nil | ||
344 | } | ||
345 | |||
346 | // Addresses returns the list of listening endpoint addresses | 268 | // Addresses returns the list of listening endpoint addresses |
347 | func (c *Core) Addresses() (list []*util.Address, err error) { | 269 | func (c *Core) Addresses() (list []*util.Address, err error) { |
348 | for _, epRef := range c.endpoints { | 270 | for _, epRef := range c.endpoints { |
@@ -365,6 +287,29 @@ func (c *Core) PeerID() *util.PeerID { | |||
365 | 287 | ||
366 | //---------------------------------------------------------------------- | 288 | //---------------------------------------------------------------------- |
367 | 289 | ||
290 | // Signable interface for objects that can get signed by peer | ||
291 | type Signable interface { | ||
292 | // SignedData returns the byte array to be signed | ||
293 | SignedData() []byte | ||
294 | |||
295 | // SetSignature returns the signature to the signable object | ||
296 | SetSignature(*util.PeerSignature) error | ||
297 | } | ||
298 | |||
299 | // Sign a signable onject with private peer key | ||
300 | func (c *Core) Sign(obj Signable) error { | ||
301 | sd := obj.SignedData() | ||
302 | logger.Printf(logger.DBG, "[core] Signing data '%s'", hex.EncodeToString(sd)) | ||
303 | sig, err := c.local.prv.EdSign(sd) | ||
304 | if err != nil { | ||
305 | return err | ||
306 | } | ||
307 | logger.Printf(logger.DBG, "[core] --> signature '%s'", hex.EncodeToString(sig.Bytes())) | ||
308 | return obj.SetSignature(util.NewPeerSignature(sig.Bytes())) | ||
309 | } | ||
310 | |||
311 | //---------------------------------------------------------------------- | ||
312 | |||
368 | // TryConnect is a function which allows the local peer to attempt the | 313 | // TryConnect is a function which allows the local peer to attempt the |
369 | // establishment of a connection to another peer using an address. | 314 | // establishment of a connection to another peer using an address. |
370 | // When the connection attempt is successful, information on the new | 315 | // When the connection attempt is successful, information on the new |
@@ -390,14 +335,6 @@ func (c *Core) Hold(peer *util.PeerID) {} | |||
390 | // established by HOLD(). | 335 | // established by HOLD(). |
391 | func (c *Core) Drop(peer *util.PeerID) {} | 336 | func (c *Core) Drop(peer *util.PeerID) {} |
392 | 337 | ||
393 | // L2NSE is ESTIMATE_NETWORK_SIZE(), a procedure that provides estimates | ||
394 | // on the base-2 logarithm of the network size L2NSE, that is the base-2 | ||
395 | // logarithm number of peers in the network, for use by the routing | ||
396 | // algorithm. | ||
397 | func (c *Core) L2NSE() float64 { | ||
398 | return 0. | ||
399 | } | ||
400 | |||
401 | //---------------------------------------------------------------------- | 338 | //---------------------------------------------------------------------- |
402 | // Event listener and event dispatch. | 339 | // Event listener and event dispatch. |
403 | //---------------------------------------------------------------------- | 340 | //---------------------------------------------------------------------- |
@@ -418,11 +355,12 @@ func (c *Core) Unregister(name string) *Listener { | |||
418 | 355 | ||
419 | // internal: dispatch event to listeners | 356 | // internal: dispatch event to listeners |
420 | func (c *Core) dispatch(ev *Event) { | 357 | func (c *Core) dispatch(ev *Event) { |
358 | logger.Printf(logger.DBG, "[core] Dispatching %v...", ev) | ||
421 | // dispatch event to listeners | 359 | // dispatch event to listeners |
422 | for _, l := range c.listeners { | 360 | for _, l := range c.listeners { |
423 | if l.filter.CheckEvent(ev.ID) { | 361 | if l.filter.CheckEvent(ev.ID) { |
424 | mt := ev.Msg.Header().MsgType | ||
425 | if ev.ID == EV_MESSAGE { | 362 | if ev.ID == EV_MESSAGE { |
363 | mt := ev.Msg.Header().MsgType | ||
426 | if mt != 0 && !l.filter.CheckMsgType(mt) { | 364 | if mt != 0 && !l.filter.CheckMsgType(mt) { |
427 | // skip event | 365 | // skip event |
428 | return | 366 | return |
diff --git a/src/gnunet/core/core_test.go b/src/gnunet/core/core_test.go index 29f740b..f87dfef 100644 --- a/src/gnunet/core/core_test.go +++ b/src/gnunet/core/core_test.go | |||
@@ -36,7 +36,6 @@ import ( | |||
36 | 36 | ||
37 | // TestCoreSimple test a two node network | 37 | // TestCoreSimple test a two node network |
38 | func TestCoreSimple(t *testing.T) { | 38 | func TestCoreSimple(t *testing.T) { |
39 | |||
40 | var ( | 39 | var ( |
41 | peer1Cfg = &config.NodeConfig{ | 40 | peer1Cfg = &config.NodeConfig{ |
42 | Name: "p1", | 41 | Name: "p1", |
@@ -75,11 +74,11 @@ func TestCoreSimple(t *testing.T) { | |||
75 | }() | 74 | }() |
76 | 75 | ||
77 | // create and run nodes | 76 | // create and run nodes |
78 | node1, err := NewTestNode(t, ctx, peer1Cfg) | 77 | node1, err := NewTestNode(ctx, t, peer1Cfg) |
79 | if err != nil { | 78 | if err != nil { |
80 | t.Fatal(err) | 79 | t.Fatal(err) |
81 | } | 80 | } |
82 | node2, err := NewTestNode(t, ctx, peer2Cfg) | 81 | node2, err := NewTestNode(ctx, t, peer2Cfg) |
83 | if err != nil { | 82 | if err != nil { |
84 | t.Fatal(err) | 83 | t.Fatal(err) |
85 | } | 84 | } |
@@ -104,7 +103,6 @@ func TestCoreSimple(t *testing.T) { | |||
104 | 103 | ||
105 | // TestCoreSimple test a two node network | 104 | // TestCoreSimple test a two node network |
106 | func TestCoreUPNP(t *testing.T) { | 105 | func TestCoreUPNP(t *testing.T) { |
107 | |||
108 | // configuration data | 106 | // configuration data |
109 | var ( | 107 | var ( |
110 | peer1Cfg = &config.NodeConfig{ | 108 | peer1Cfg = &config.NodeConfig{ |
@@ -143,12 +141,16 @@ func TestCoreUPNP(t *testing.T) { | |||
143 | }() | 141 | }() |
144 | 142 | ||
145 | // create and run nodes | 143 | // create and run nodes |
146 | node1, err := NewTestNode(t, ctx, peer1Cfg) | 144 | node1, err := NewTestNode(ctx, t, peer1Cfg) |
147 | if err != nil { | 145 | if err != nil { |
146 | if err == transport.ErrTransNoUPNP { | ||
147 | t.Log("No UPnP available -- skipping test") | ||
148 | return | ||
149 | } | ||
148 | t.Fatal(err) | 150 | t.Fatal(err) |
149 | } | 151 | } |
150 | defer node1.Shutdown() | 152 | defer node1.Shutdown() |
151 | node2, err := NewTestNode(t, ctx, peer2Cfg) | 153 | node2, err := NewTestNode(ctx, t, peer2Cfg) |
152 | if err != nil { | 154 | if err != nil { |
153 | t.Fatal(err) | 155 | t.Fatal(err) |
154 | } | 156 | } |
@@ -180,7 +182,7 @@ func TestDHTU(t *testing.T) { | |||
180 | } | 182 | } |
181 | // convert arguments | 183 | // convert arguments |
182 | var ( | 184 | var ( |
183 | rId *util.PeerID | 185 | rID *util.PeerID |
184 | rAddr *util.Address | 186 | rAddr *util.Address |
185 | err error | 187 | err error |
186 | ) | 188 | ) |
@@ -211,14 +213,14 @@ func TestDHTU(t *testing.T) { | |||
211 | }() | 213 | }() |
212 | 214 | ||
213 | // create and run node | 215 | // create and run node |
214 | node, err := NewTestNode(t, ctx, peerCfg) | 216 | node, err := NewTestNode(ctx, t, peerCfg) |
215 | if err != nil { | 217 | if err != nil { |
216 | log.Fatal(err) | 218 | log.Fatal(err) |
217 | } | 219 | } |
218 | defer node.Shutdown() | 220 | defer node.Shutdown() |
219 | 221 | ||
220 | // learn bootstrap address (triggers HELLO) | 222 | // learn bootstrap address (triggers HELLO) |
221 | node.Learn(ctx, rId, rAddr) | 223 | node.Learn(ctx, rID, rAddr) |
222 | 224 | ||
223 | // run forever | 225 | // run forever |
224 | var ch chan struct{} | 226 | var ch chan struct{} |
@@ -246,14 +248,12 @@ func (n *TestNode) Learn(ctx context.Context, peer *util.PeerID, addr *util.Addr | |||
246 | if peer != nil { | 248 | if peer != nil { |
247 | label = peer.String() | 249 | label = peer.String() |
248 | } | 250 | } |
249 | n.t.Logf("[%d] Learning %s for %s", n.id, addr.StringAll(), label) | 251 | n.t.Logf("[%d] Learning %s for %s", n.id, addr.URI(), label) |
250 | if err := n.core.Learn(ctx, peer, []*util.Address{addr}); err != nil { | 252 | n.core.Learn(ctx, peer, []*util.Address{addr}) |
251 | n.t.Log("Learn: " + err.Error()) | ||
252 | } | ||
253 | } | 253 | } |
254 | 254 | ||
255 | func NewTestNode(t *testing.T, ctx context.Context, cfg *config.NodeConfig) (node *TestNode, err error) { | 255 | func NewTestNode(ctx context.Context, t *testing.T, cfg *config.NodeConfig) (node *TestNode, err error) { |
256 | 256 | t.Helper() | |
257 | // create test node | 257 | // create test node |
258 | node = new(TestNode) | 258 | node = new(TestNode) |
259 | node.t = t | 259 | node.t = t |
@@ -265,7 +265,7 @@ func NewTestNode(t *testing.T, ctx context.Context, cfg *config.NodeConfig) (nod | |||
265 | } | 265 | } |
266 | node.peer = node.core.Peer() | 266 | node.peer = node.core.Peer() |
267 | t.Logf("[%d] Node %s starting", node.id, node.peer.GetID()) | 267 | t.Logf("[%d] Node %s starting", node.id, node.peer.GetID()) |
268 | t.Logf("[%d] --> %s", node.id, hex.EncodeToString(node.peer.GetID().Key)) | 268 | t.Logf("[%d] --> %s", node.id, hex.EncodeToString(node.peer.GetID().Data)) |
269 | 269 | ||
270 | list, err := node.core.Addresses() | 270 | list, err := node.core.Addresses() |
271 | if err != nil { | 271 | if err != nil { |
diff --git a/src/gnunet/core/event.go b/src/gnunet/core/event.go index 8d05b0d..7d7c7e1 100644 --- a/src/gnunet/core/event.go +++ b/src/gnunet/core/event.go | |||
@@ -19,6 +19,7 @@ | |||
19 | package core | 19 | package core |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "fmt" | ||
22 | "gnunet/message" | 23 | "gnunet/message" |
23 | "gnunet/transport" | 24 | "gnunet/transport" |
24 | "gnunet/util" | 25 | "gnunet/util" |
@@ -29,6 +30,7 @@ import ( | |||
29 | //---------------------------------------------------------------------- | 30 | //---------------------------------------------------------------------- |
30 | 31 | ||
31 | // Event types | 32 | // Event types |
33 | //nolint:stylecheck // allow non-camel-case in constants | ||
32 | const ( | 34 | const ( |
33 | EV_CONNECT = iota // peer connected | 35 | EV_CONNECT = iota // peer connected |
34 | EV_DISCONNECT // peer disconnected | 36 | EV_DISCONNECT // peer disconnected |
@@ -82,6 +84,8 @@ func (f *EventFilter) CheckMsgType(mt uint16) bool { | |||
82 | return ok | 84 | return ok |
83 | } | 85 | } |
84 | 86 | ||
87 | //---------------------------------------------------------------------- | ||
88 | |||
85 | // Event sent to listeners | 89 | // Event sent to listeners |
86 | type Event struct { | 90 | type Event struct { |
87 | ID int // event type | 91 | ID int // event type |
@@ -91,6 +95,19 @@ type Event struct { | |||
91 | Label string // event label (can be empty) | 95 | Label string // event label (can be empty) |
92 | } | 96 | } |
93 | 97 | ||
98 | // String returns a human-readable representation of an event. | ||
99 | func (e *Event) String() string { | ||
100 | s := "Event{" | ||
101 | if len(e.Label) > 0 { | ||
102 | s += "label=" + e.Label + "," | ||
103 | } | ||
104 | s += fmt.Sprintf("id=%d,peer=%s", e.ID, e.Peer) | ||
105 | if e.Msg != nil { | ||
106 | s += fmt.Sprintf(",msg=%d", e.Msg.Header().MsgType) | ||
107 | } | ||
108 | return s + "}" | ||
109 | } | ||
110 | |||
94 | //---------------------------------------------------------------------- | 111 | //---------------------------------------------------------------------- |
95 | 112 | ||
96 | // Listener for network events | 113 | // Listener for network events |
diff --git a/src/gnunet/core/hello_test.go b/src/gnunet/core/hello_test.go index 4abad04..0590c90 100644 --- a/src/gnunet/core/hello_test.go +++ b/src/gnunet/core/hello_test.go | |||
@@ -71,7 +71,6 @@ func TestHelloURLDirect(t *testing.T) { | |||
71 | } | 71 | } |
72 | 72 | ||
73 | func TestHelloURL(t *testing.T) { | 73 | func TestHelloURL(t *testing.T) { |
74 | |||
75 | // prepare peer and HELLO data | 74 | // prepare peer and HELLO data |
76 | peer, err := NewLocalPeer(peerCfg) | 75 | peer, err := NewLocalPeer(peerCfg) |
77 | if err != nil { | 76 | if err != nil { |
diff --git a/src/gnunet/core/peer.go b/src/gnunet/core/peer.go index 86ed43a..5b0c3d0 100644 --- a/src/gnunet/core/peer.go +++ b/src/gnunet/core/peer.go | |||
@@ -103,11 +103,16 @@ func (p *Peer) HelloData(ttl time.Duration, a []*util.Address) (h *blocks.HelloB | |||
103 | // assemble HELLO data | 103 | // assemble HELLO data |
104 | h = new(blocks.HelloBlock) | 104 | h = new(blocks.HelloBlock) |
105 | h.PeerID = p.GetID() | 105 | h.PeerID = p.GetID() |
106 | h.Expire = util.NewAbsoluteTime(time.Now().Add(ttl)) | 106 | h.Expires = util.NewAbsoluteTime(time.Now().Add(ttl)) |
107 | h.SetAddresses(a) | 107 | h.SetAddresses(a) |
108 | 108 | ||
109 | // sign data | 109 | // sign data |
110 | err = h.Sign(p.prv) | 110 | sd := h.SignedData() |
111 | var sig *ed25519.EdSignature | ||
112 | if sig, err = p.prv.EdSign(sd); err != nil { | ||
113 | return | ||
114 | } | ||
115 | err = h.SetSignature(util.NewPeerSignature(sig.Bytes())) | ||
111 | return | 116 | return |
112 | } | 117 | } |
113 | 118 | ||
@@ -139,7 +144,7 @@ func (p *Peer) PubKey() *ed25519.PublicKey { | |||
139 | // GetID returns the node ID (public key) in binary format | 144 | // GetID returns the node ID (public key) in binary format |
140 | func (p *Peer) GetID() *util.PeerID { | 145 | func (p *Peer) GetID() *util.PeerID { |
141 | return &util.PeerID{ | 146 | return &util.PeerID{ |
142 | Key: util.Clone(p.pub.Bytes()), | 147 | Data: util.Clone(p.pub.Bytes()), |
143 | } | 148 | } |
144 | } | 149 | } |
145 | 150 | ||
@@ -151,7 +156,7 @@ func (p *Peer) GetIDString() string { | |||
151 | // Sign a message with the (long-term) private key. | 156 | // Sign a message with the (long-term) private key. |
152 | func (p *Peer) Sign(msg []byte) (*ed25519.EdSignature, error) { | 157 | func (p *Peer) Sign(msg []byte) (*ed25519.EdSignature, error) { |
153 | if p.prv == nil { | 158 | if p.prv == nil { |
154 | return nil, fmt.Errorf("No private key") | 159 | return nil, fmt.Errorf("no private key") |
155 | } | 160 | } |
156 | return p.prv.EdSign(msg) | 161 | return p.prv.EdSign(msg) |
157 | } | 162 | } |
diff --git a/src/gnunet/core/peer_test.go b/src/gnunet/core/peer_test.go index f546a52..1be4d42 100644 --- a/src/gnunet/core/peer_test.go +++ b/src/gnunet/core/peer_test.go | |||
@@ -43,7 +43,6 @@ var ( | |||
43 | ) | 43 | ) |
44 | 44 | ||
45 | func TestPeerHello(t *testing.T) { | 45 | func TestPeerHello(t *testing.T) { |
46 | |||
47 | // generate new local node | 46 | // generate new local node |
48 | node, err := NewLocalPeer(cfg) | 47 | node, err := NewLocalPeer(cfg) |
49 | if err != nil { | 48 | if err != nil { |
@@ -54,13 +53,16 @@ func TestPeerHello(t *testing.T) { | |||
54 | // This hack will only work for direct listening addresses | 53 | // This hack will only work for direct listening addresses |
55 | addrList := make([]*util.Address, 0) | 54 | addrList := make([]*util.Address, 0) |
56 | for _, epRef := range cfg.Endpoints { | 55 | for _, epRef := range cfg.Endpoints { |
57 | addr, err := util.ParseAddress(epRef.Addr()) | 56 | var addr *util.Address |
58 | if err != nil { | 57 | if addr, err = util.ParseAddress(epRef.Addr()); err != nil { |
59 | t.Fatal(err) | 58 | t.Fatal(err) |
60 | } | 59 | } |
61 | addrList = append(addrList, addr) | 60 | addrList = append(addrList, addr) |
62 | } | 61 | } |
63 | h, err := node.HelloData(TTL, addrList) | 62 | h, err := node.HelloData(TTL, addrList) |
63 | if err != nil { | ||
64 | t.Fatal(err) | ||
65 | } | ||
64 | 66 | ||
65 | // convert to URL and back | 67 | // convert to URL and back |
66 | u := h.URL() | 68 | u := h.URL() |
diff --git a/src/gnunet/crypto/gns.go b/src/gnunet/crypto/gns.go index d62a047..27a52d3 100644 --- a/src/gnunet/crypto/gns.go +++ b/src/gnunet/crypto/gns.go | |||
@@ -28,6 +28,7 @@ import ( | |||
28 | "gnunet/util" | 28 | "gnunet/util" |
29 | 29 | ||
30 | "github.com/bfix/gospel/data" | 30 | "github.com/bfix/gospel/data" |
31 | "github.com/bfix/gospel/logger" | ||
31 | "github.com/bfix/gospel/math" | 32 | "github.com/bfix/gospel/math" |
32 | "golang.org/x/crypto/hkdf" | 33 | "golang.org/x/crypto/hkdf" |
33 | ) | 34 | ) |
@@ -105,7 +106,7 @@ type ZoneKeyImpl interface { | |||
105 | 106 | ||
106 | // Derive a zone key from this zone key based on a big integer | 107 | // Derive a zone key from this zone key based on a big integer |
107 | // (key blinding). Returns the derived key and the blinding value. | 108 | // (key blinding). Returns the derived key and the blinding value. |
108 | Derive(h *math.Int) (ZoneKeyImpl, *math.Int) | 109 | Derive(h *math.Int) (ZoneKeyImpl, *math.Int, error) |
109 | 110 | ||
110 | // BlockKey returns the key for block en-/decryption | 111 | // BlockKey returns the key for block en-/decryption |
111 | BlockKey(label string, expires util.AbsoluteTime) (skey []byte) | 112 | BlockKey(label string, expires util.AbsoluteTime) (skey []byte) |
@@ -126,7 +127,7 @@ type ZonePrivateImpl interface { | |||
126 | 127 | ||
127 | // Derive a private key from this zone key based on a big integer | 128 | // Derive a private key from this zone key based on a big integer |
128 | // (key blinding). Returns the derived key and the blinding value. | 129 | // (key blinding). Returns the derived key and the blinding value. |
129 | Derive(h *math.Int) (ZonePrivateImpl, *math.Int) | 130 | Derive(h *math.Int) (ZonePrivateImpl, *math.Int, error) |
130 | 131 | ||
131 | // Sign binary data and return the signature | 132 | // Sign binary data and return the signature |
132 | Sign(data []byte) (*ZoneSignature, error) | 133 | Sign(data []byte) (*ZoneSignature, error) |
@@ -143,10 +144,14 @@ type ZoneSigImpl interface { | |||
143 | //---------------------------------------------------------------------- | 144 | //---------------------------------------------------------------------- |
144 | // Zone types | 145 | // Zone types |
145 | //---------------------------------------------------------------------- | 146 | //---------------------------------------------------------------------- |
147 | |||
148 | //nolint:stylecheck // allow non-camel-case in constants | ||
146 | var ( | 149 | var ( |
147 | ZONE_PKEY = uint32(enums.GNS_TYPE_PKEY) | 150 | ZONE_PKEY = uint32(enums.GNS_TYPE_PKEY) |
148 | ZONE_EDKEY = uint32(enums.GNS_TYPE_EDKEY) | 151 | ZONE_EDKEY = uint32(enums.GNS_TYPE_EDKEY) |
152 | ) | ||
149 | 153 | ||
154 | var ( | ||
150 | // register available zone types for BlockHandler | 155 | // register available zone types for BlockHandler |
151 | ZoneTypes = []enums.GNSType{ | 156 | ZoneTypes = []enums.GNSType{ |
152 | enums.GNS_TYPE_PKEY, | 157 | enums.GNS_TYPE_PKEY, |
@@ -177,6 +182,7 @@ var ( | |||
177 | // Error codes | 182 | // Error codes |
178 | var ( | 183 | var ( |
179 | ErrNoImplementation = errors.New("unknown zone implementation") | 184 | ErrNoImplementation = errors.New("unknown zone implementation") |
185 | ErrUnknownZoneType = errors.New("unknown zone type") | ||
180 | ) | 186 | ) |
181 | 187 | ||
182 | // GetImplementation return the factory for a given zone type. | 188 | // GetImplementation return the factory for a given zone type. |
@@ -204,14 +210,14 @@ type ZonePrivate struct { | |||
204 | } | 210 | } |
205 | 211 | ||
206 | // NewZonePrivate returns a new initialized ZonePrivate instance | 212 | // NewZonePrivate returns a new initialized ZonePrivate instance |
207 | func NewZonePrivate(ztype uint32, d []byte) (*ZonePrivate, error) { | 213 | func NewZonePrivate(ztype uint32, d []byte) (zp *ZonePrivate, err error) { |
208 | // get factory for given zone type | 214 | // get factory for given zone type |
209 | impl, ok := zoneImpl[ztype] | 215 | impl, ok := zoneImpl[ztype] |
210 | if !ok { | 216 | if !ok { |
211 | return nil, ErrNoImplementation | 217 | return nil, ErrNoImplementation |
212 | } | 218 | } |
213 | // assemble private zone key | 219 | // assemble private zone key |
214 | zp := &ZonePrivate{ | 220 | zp = &ZonePrivate{ |
215 | ZoneKey{ | 221 | ZoneKey{ |
216 | ztype, | 222 | ztype, |
217 | nil, | 223 | nil, |
@@ -220,11 +226,13 @@ func NewZonePrivate(ztype uint32, d []byte) (*ZonePrivate, error) { | |||
220 | nil, | 226 | nil, |
221 | } | 227 | } |
222 | zp.impl = impl.NewPrivate() | 228 | zp.impl = impl.NewPrivate() |
223 | zp.impl.Init(d) | 229 | if err = zp.impl.Init(d); err != nil { |
230 | return | ||
231 | } | ||
224 | zp.ZoneKey.KeyData = zp.impl.Public().Bytes() | 232 | zp.ZoneKey.KeyData = zp.impl.Public().Bytes() |
225 | zp.ZoneKey.impl = impl.NewPublic() | 233 | zp.ZoneKey.impl = impl.NewPublic() |
226 | zp.ZoneKey.impl.Init(zp.ZoneKey.KeyData) | 234 | err = zp.ZoneKey.impl.Init(zp.ZoneKey.KeyData) |
227 | return zp, nil | 235 | return |
228 | } | 236 | } |
229 | 237 | ||
230 | // KeySize returns the number of bytes of a key representation. | 238 | // KeySize returns the number of bytes of a key representation. |
@@ -237,17 +245,18 @@ func (zp *ZonePrivate) KeySize() uint { | |||
237 | } | 245 | } |
238 | 246 | ||
239 | // Derive key (key blinding) | 247 | // Derive key (key blinding) |
240 | func (zp *ZonePrivate) Derive(label, context string) (*ZonePrivate, *math.Int) { | 248 | func (zp *ZonePrivate) Derive(label, context string) (dzp *ZonePrivate, h *math.Int, err error) { |
241 | // get factory for given zone type | 249 | // get factory for given zone type |
242 | impl := zoneImpl[zp.Type] | 250 | impl := zoneImpl[zp.Type] |
243 | 251 | ||
244 | // caclulate derived key | 252 | // calculate derived key |
245 | h := deriveH(zp.impl.Bytes(), label, context) | 253 | h = deriveH(zp.impl.Bytes(), label, context) |
246 | var derived ZonePrivateImpl | 254 | var derived ZonePrivateImpl |
247 | derived, h = zp.impl.Derive(h) | 255 | if derived, h, err = zp.impl.Derive(h); err != nil { |
248 | 256 | return | |
257 | } | ||
249 | // assemble derived pivate key | 258 | // assemble derived pivate key |
250 | dzp := &ZonePrivate{ | 259 | dzp = &ZonePrivate{ |
251 | ZoneKey{ | 260 | ZoneKey{ |
252 | zp.Type, | 261 | zp.Type, |
253 | nil, | 262 | nil, |
@@ -257,8 +266,8 @@ func (zp *ZonePrivate) Derive(label, context string) (*ZonePrivate, *math.Int) { | |||
257 | } | 266 | } |
258 | zp.ZoneKey.KeyData = derived.Public().Bytes() | 267 | zp.ZoneKey.KeyData = derived.Public().Bytes() |
259 | zp.ZoneKey.impl = impl.NewPublic() | 268 | zp.ZoneKey.impl = impl.NewPublic() |
260 | zp.ZoneKey.impl.Init(zp.ZoneKey.KeyData) | 269 | err = zp.ZoneKey.impl.Init(zp.ZoneKey.KeyData) |
261 | return dzp, h | 270 | return |
262 | } | 271 | } |
263 | 272 | ||
264 | // ZoneSign data with a private key | 273 | // ZoneSign data with a private key |
@@ -284,20 +293,21 @@ type ZoneKey struct { | |||
284 | } | 293 | } |
285 | 294 | ||
286 | // NewZoneKey returns a new initialized ZoneKey instance | 295 | // NewZoneKey returns a new initialized ZoneKey instance |
287 | func NewZoneKey(d []byte) (*ZoneKey, error) { | 296 | func NewZoneKey(d []byte) (zk *ZoneKey, err error) { |
288 | // read zone key from data | 297 | // read zone key from data |
289 | zk := new(ZoneKey) | 298 | zk = new(ZoneKey) |
290 | if err := data.Unmarshal(zk, d); err != nil { | 299 | if err = data.Unmarshal(zk, d); err != nil { |
291 | return nil, err | 300 | return |
292 | } | 301 | } |
293 | // initialize implementation | 302 | // initialize implementation |
294 | impl, ok := zoneImpl[zk.Type] | 303 | impl, ok := zoneImpl[zk.Type] |
295 | if !ok { | 304 | if !ok { |
296 | return nil, errors.New("unknown zone type") | 305 | err = ErrUnknownZoneType |
306 | return | ||
297 | } | 307 | } |
298 | zk.impl = impl.NewPublic() | 308 | zk.impl = impl.NewPublic() |
299 | zk.impl.Init(zk.KeyData) | 309 | err = zk.impl.Init(zk.KeyData) |
300 | return zk, nil | 310 | return |
301 | } | 311 | } |
302 | 312 | ||
303 | // KeySize returns the number of bytes of a key representation. | 313 | // KeySize returns the number of bytes of a key representation. |
@@ -310,15 +320,18 @@ func (zk *ZoneKey) KeySize() uint { | |||
310 | } | 320 | } |
311 | 321 | ||
312 | // Derive key (key blinding) | 322 | // Derive key (key blinding) |
313 | func (zk *ZoneKey) Derive(label, context string) (*ZoneKey, *math.Int) { | 323 | func (zk *ZoneKey) Derive(label, context string) (dzk *ZoneKey, h *math.Int, err error) { |
314 | h := deriveH(zk.KeyData, label, context) | 324 | h = deriveH(zk.KeyData, label, context) |
315 | var derived ZoneKeyImpl | 325 | var derived ZoneKeyImpl |
316 | derived, h = zk.impl.Derive(h) | 326 | if derived, h, err = zk.impl.Derive(h); err != nil { |
317 | return &ZoneKey{ | 327 | return |
328 | } | ||
329 | dzk = &ZoneKey{ | ||
318 | Type: zk.Type, | 330 | Type: zk.Type, |
319 | KeyData: derived.Bytes(), | 331 | KeyData: derived.Bytes(), |
320 | impl: derived, | 332 | impl: derived, |
321 | }, h | 333 | } |
334 | return | ||
322 | } | 335 | } |
323 | 336 | ||
324 | // BlockKey returns the key for block en-/decryption | 337 | // BlockKey returns the key for block en-/decryption |
@@ -338,15 +351,22 @@ func (zk *ZoneKey) Decrypt(data []byte, label string, expire util.AbsoluteTime) | |||
338 | 351 | ||
339 | // Verify a zone signature | 352 | // Verify a zone signature |
340 | func (zk *ZoneKey) Verify(data []byte, zs *ZoneSignature) (ok bool, err error) { | 353 | func (zk *ZoneKey) Verify(data []byte, zs *ZoneSignature) (ok bool, err error) { |
341 | zk.withImpl() | 354 | if err = zk.withImpl(); err != nil { |
355 | return | ||
356 | } | ||
342 | return zk.impl.Verify(data, zs) | 357 | return zk.impl.Verify(data, zs) |
343 | } | 358 | } |
344 | 359 | ||
345 | // ID returns the human-readable zone identifier. | 360 | // ID returns the human-readable zone identifier. |
346 | func (zk *ZoneKey) ID() string { | 361 | func (zk *ZoneKey) ID() string { |
347 | buf := new(bytes.Buffer) | 362 | buf := new(bytes.Buffer) |
348 | binary.Write(buf, binary.BigEndian, zk.Type) | 363 | err := binary.Write(buf, binary.BigEndian, zk.Type) |
349 | buf.Write(zk.KeyData) | 364 | if err == nil { |
365 | _, err = buf.Write(zk.KeyData) | ||
366 | } | ||
367 | if err != nil { | ||
368 | logger.Printf(logger.ERROR, "[ZoneKey.ID] failed: %s", err.Error()) | ||
369 | } | ||
350 | return util.EncodeBinaryToString(buf.Bytes()) | 370 | return util.EncodeBinaryToString(buf.Bytes()) |
351 | } | 371 | } |
352 | 372 | ||
@@ -362,12 +382,13 @@ func (zk *ZoneKey) Equal(k *ZoneKey) bool { | |||
362 | } | 382 | } |
363 | 383 | ||
364 | // withImpl ensure that an implementation reference is available | 384 | // withImpl ensure that an implementation reference is available |
365 | func (zk *ZoneKey) withImpl() { | 385 | func (zk *ZoneKey) withImpl() (err error) { |
366 | if zk.impl == nil { | 386 | if zk.impl == nil { |
367 | factory := zoneImpl[zk.Type] | 387 | factory := zoneImpl[zk.Type] |
368 | zk.impl = factory.NewPublic() | 388 | zk.impl = factory.NewPublic() |
369 | zk.impl.Init(zk.KeyData) | 389 | err = zk.impl.Init(zk.KeyData) |
370 | } | 390 | } |
391 | return | ||
371 | } | 392 | } |
372 | 393 | ||
373 | //---------------------------------------------------------------------- | 394 | //---------------------------------------------------------------------- |
@@ -382,27 +403,29 @@ type ZoneSignature struct { | |||
382 | } | 403 | } |
383 | 404 | ||
384 | // NewZoneSignature returns a new initialized ZoneSignature instance | 405 | // NewZoneSignature returns a new initialized ZoneSignature instance |
385 | func NewZoneSignature(d []byte) (*ZoneSignature, error) { | 406 | func NewZoneSignature(d []byte) (sig *ZoneSignature, err error) { |
386 | // read signature | 407 | // read signature |
387 | sig := new(ZoneSignature) | 408 | sig = new(ZoneSignature) |
388 | if err := data.Unmarshal(sig, d); err != nil { | 409 | if err = data.Unmarshal(sig, d); err != nil { |
389 | return nil, err | 410 | return |
390 | } | 411 | } |
391 | // initialize implementations | 412 | // initialize implementations |
392 | impl, ok := zoneImpl[sig.Type] | 413 | impl, ok := zoneImpl[sig.Type] |
393 | if !ok { | 414 | if !ok { |
394 | return nil, errors.New("unknown zone type") | 415 | err = ErrUnknownZoneType |
416 | return | ||
395 | } | 417 | } |
396 | // set signature implementation | 418 | // set signature implementation |
397 | zs := impl.NewSignature() | 419 | zs := impl.NewSignature() |
398 | zs.Init(sig.Signature) | 420 | err = zs.Init(sig.Signature) |
399 | sig.impl = zs | 421 | sig.impl = zs |
400 | // set public key implementation | 422 | // set public key implementation |
401 | zk := impl.NewPublic() | 423 | zk := impl.NewPublic() |
402 | zk.Init(sig.KeyData) | 424 | if err = zk.Init(sig.KeyData); err != nil { |
425 | return | ||
426 | } | ||
403 | sig.ZoneKey.impl = zk | 427 | sig.ZoneKey.impl = zk |
404 | 428 | return | |
405 | return sig, nil | ||
406 | } | 429 | } |
407 | 430 | ||
408 | // SigSize returns the number of bytes of a signature that can be | 431 | // SigSize returns the number of bytes of a signature that can be |
@@ -435,6 +458,8 @@ func deriveH(key []byte, label, context string) *math.Int { | |||
435 | data := append([]byte(label), []byte(context)...) | 458 | data := append([]byte(label), []byte(context)...) |
436 | rdr := hkdf.Expand(sha256.New, prk, data) | 459 | rdr := hkdf.Expand(sha256.New, prk, data) |
437 | b := make([]byte, 64) | 460 | b := make([]byte, 64) |
438 | rdr.Read(b) | 461 | if _, err := rdr.Read(b); err != nil { |
462 | logger.Printf(logger.ERROR, "[deriveH] failed: %s", err.Error()) | ||
463 | } | ||
439 | return math.NewIntFromBytes(b) | 464 | return math.NewIntFromBytes(b) |
440 | } | 465 | } |
diff --git a/src/gnunet/crypto/gns_edkey.go b/src/gnunet/crypto/gns_edkey.go index 3153628..68a6444 100644 --- a/src/gnunet/crypto/gns_edkey.go +++ b/src/gnunet/crypto/gns_edkey.go | |||
@@ -26,6 +26,7 @@ import ( | |||
26 | 26 | ||
27 | "github.com/bfix/gospel/crypto/ed25519" | 27 | "github.com/bfix/gospel/crypto/ed25519" |
28 | "github.com/bfix/gospel/data" | 28 | "github.com/bfix/gospel/data" |
29 | "github.com/bfix/gospel/logger" | ||
29 | "github.com/bfix/gospel/math" | 30 | "github.com/bfix/gospel/math" |
30 | "golang.org/x/crypto/hkdf" | 31 | "golang.org/x/crypto/hkdf" |
31 | "golang.org/x/crypto/nacl/secretbox" | 32 | "golang.org/x/crypto/nacl/secretbox" |
@@ -76,15 +77,15 @@ func (pk *EDKEYPublicImpl) Bytes() []byte { | |||
76 | 77 | ||
77 | // Derive a public key from this key based on a big integer | 78 | // Derive a public key from this key based on a big integer |
78 | // (key blinding). Returns the derived key and the blinding value. | 79 | // (key blinding). Returns the derived key and the blinding value. |
79 | func (pk *EDKEYPublicImpl) Derive(h *math.Int) (ZoneKeyImpl, *math.Int) { | 80 | func (pk *EDKEYPublicImpl) Derive(h *math.Int) (dPk ZoneKeyImpl, hOut *math.Int, err error) { |
80 | // limit to allowed value range | 81 | // limit to allowed value range |
81 | h = h.Mod(ed25519.GetCurve().N) | 82 | hOut = h.Mod(ed25519.GetCurve().N) |
82 | derived := pk.pub.Mult(h) | 83 | derived := pk.pub.Mult(hOut) |
83 | dPk := &EDKEYPublicImpl{ | 84 | dPk = &EDKEYPublicImpl{ |
84 | pk.ztype, | 85 | pk.ztype, |
85 | derived, | 86 | derived, |
86 | } | 87 | } |
87 | return dPk, h | 88 | return |
88 | } | 89 | } |
89 | 90 | ||
90 | // Encrypt binary data (of any size). Output can be larger than input | 91 | // Encrypt binary data (of any size). Output can be larger than input |
@@ -137,8 +138,9 @@ func (pk *EDKEYPublicImpl) BlockKey(label string, expires util.AbsoluteTime) (sk | |||
137 | kd := pk.Bytes() | 138 | kd := pk.Bytes() |
138 | prk := hkdf.Extract(sha512.New, kd, []byte("gns-xsalsa-ctx-key")) | 139 | prk := hkdf.Extract(sha512.New, kd, []byte("gns-xsalsa-ctx-key")) |
139 | rdr := hkdf.Expand(sha256.New, prk, []byte(label)) | 140 | rdr := hkdf.Expand(sha256.New, prk, []byte(label)) |
140 | rdr.Read(skey[:32]) | 141 | if _, err := rdr.Read(skey[:32]); err != nil { |
141 | 142 | logger.Printf(logger.ERROR, "[EDKEYPublicImpl.BlockKey] failed: %s", err.Error()) | |
143 | } | ||
142 | // assemble initialization vector | 144 | // assemble initialization vector |
143 | iv := &struct { | 145 | iv := &struct { |
144 | Nonce []byte `size:"16"` // Nonce | 146 | Nonce []byte `size:"16"` // Nonce |
@@ -149,7 +151,9 @@ func (pk *EDKEYPublicImpl) BlockKey(label string, expires util.AbsoluteTime) (sk | |||
149 | } | 151 | } |
150 | prk = hkdf.Extract(sha512.New, kd, []byte("gns-xsalsa-ctx-iv")) | 152 | prk = hkdf.Extract(sha512.New, kd, []byte("gns-xsalsa-ctx-iv")) |
151 | rdr = hkdf.Expand(sha256.New, prk, []byte(label)) | 153 | rdr = hkdf.Expand(sha256.New, prk, []byte(label)) |
152 | rdr.Read(iv.Nonce) | 154 | if _, err := rdr.Read(iv.Nonce); err != nil { |
155 | logger.Printf(logger.ERROR, "[EDKEYPublicImpl.BlockKey] failed: %s", err.Error()) | ||
156 | } | ||
153 | buf, _ := data.Marshal(iv) | 157 | buf, _ := data.Marshal(iv) |
154 | copy(skey[32:], buf) | 158 | copy(skey[32:], buf) |
155 | return | 159 | return |
@@ -188,30 +192,32 @@ func (pk *EDKEYPrivateImpl) Public() ZoneKeyImpl { | |||
188 | 192 | ||
189 | // Derive a public key from this key based on a big integer | 193 | // Derive a public key from this key based on a big integer |
190 | // (key blinding). Returns the derived key and the blinding value. | 194 | // (key blinding). Returns the derived key and the blinding value. |
191 | func (pk *EDKEYPrivateImpl) Derive(h *math.Int) (ZonePrivateImpl, *math.Int) { | 195 | func (pk *EDKEYPrivateImpl) Derive(h *math.Int) (dPk ZonePrivateImpl, hOut *math.Int, err error) { |
192 | // limit to allowed value range | 196 | // limit to allowed value range |
193 | h = h.Mod(ed25519.GetCurve().N) | 197 | hOut = h.Mod(ed25519.GetCurve().N) |
194 | derived := pk.prv.Mult(h) | 198 | derived := pk.prv.Mult(hOut) |
195 | dPk := &EDKEYPrivateImpl{ | 199 | dPk = &EDKEYPrivateImpl{ |
196 | EDKEYPublicImpl{ | 200 | EDKEYPublicImpl{ |
197 | pk.ztype, | 201 | pk.ztype, |
198 | derived.Public(), | 202 | derived.Public(), |
199 | }, | 203 | }, |
200 | derived, | 204 | derived, |
201 | } | 205 | } |
202 | return dPk, h | 206 | return |
203 | } | 207 | } |
204 | 208 | ||
205 | // Sign binary data | 209 | // Sign binary data |
206 | func (pk *EDKEYPrivateImpl) Sign(data []byte) (*ZoneSignature, error) { | 210 | func (pk *EDKEYPrivateImpl) Sign(data []byte) (sig *ZoneSignature, err error) { |
207 | s, err := pk.prv.EdSign(data) | 211 | var s *ed25519.EdSignature |
208 | if err != nil { | 212 | if s, err = pk.prv.EdSign(data); err != nil { |
209 | return nil, err | 213 | return |
210 | } | 214 | } |
211 | sd := s.Bytes() | 215 | sd := s.Bytes() |
212 | sigImpl := new(EDKEYSigImpl) | 216 | sigImpl := new(EDKEYSigImpl) |
213 | sigImpl.Init(sd) | 217 | if err = sigImpl.Init(sd); err != nil { |
214 | sig := &ZoneSignature{ | 218 | return |
219 | } | ||
220 | sig = &ZoneSignature{ | ||
215 | ZoneKey{ | 221 | ZoneKey{ |
216 | Type: pk.ztype, | 222 | Type: pk.ztype, |
217 | KeyData: pk.pub.Bytes(), | 223 | KeyData: pk.pub.Bytes(), |
@@ -219,7 +225,7 @@ func (pk *EDKEYPrivateImpl) Sign(data []byte) (*ZoneSignature, error) { | |||
219 | sd, | 225 | sd, |
220 | sigImpl, | 226 | sigImpl, |
221 | } | 227 | } |
222 | return sig, nil | 228 | return |
223 | } | 229 | } |
224 | 230 | ||
225 | //---------------------------------------------------------------------- | 231 | //---------------------------------------------------------------------- |
diff --git a/src/gnunet/crypto/gns_pkey.go b/src/gnunet/crypto/gns_pkey.go index fa319a4..e9fdfb5 100644 --- a/src/gnunet/crypto/gns_pkey.go +++ b/src/gnunet/crypto/gns_pkey.go | |||
@@ -27,6 +27,7 @@ import ( | |||
27 | 27 | ||
28 | "github.com/bfix/gospel/crypto/ed25519" | 28 | "github.com/bfix/gospel/crypto/ed25519" |
29 | "github.com/bfix/gospel/data" | 29 | "github.com/bfix/gospel/data" |
30 | "github.com/bfix/gospel/logger" | ||
30 | "github.com/bfix/gospel/math" | 31 | "github.com/bfix/gospel/math" |
31 | "golang.org/x/crypto/hkdf" | 32 | "golang.org/x/crypto/hkdf" |
32 | ) | 33 | ) |
@@ -76,15 +77,15 @@ func (pk *PKEYPublicImpl) Bytes() []byte { | |||
76 | 77 | ||
77 | // Derive a public key from this key based on a big integer | 78 | // Derive a public key from this key based on a big integer |
78 | // (key blinding). Returns the derived key and the blinding value. | 79 | // (key blinding). Returns the derived key and the blinding value. |
79 | func (pk *PKEYPublicImpl) Derive(h *math.Int) (ZoneKeyImpl, *math.Int) { | 80 | func (pk *PKEYPublicImpl) Derive(h *math.Int) (dPk ZoneKeyImpl, hOut *math.Int, err error) { |
80 | // limit to allowed value range | 81 | // limit to allowed value range |
81 | h = h.Mod(ed25519.GetCurve().N) | 82 | hOut = h.Mod(ed25519.GetCurve().N) |
82 | derived := pk.pub.Mult(h) | 83 | derived := pk.pub.Mult(hOut) |
83 | dPk := &PKEYPublicImpl{ | 84 | dPk = &PKEYPublicImpl{ |
84 | pk.ztype, | 85 | pk.ztype, |
85 | derived, | 86 | derived, |
86 | } | 87 | } |
87 | return dPk, h | 88 | return |
88 | } | 89 | } |
89 | 90 | ||
90 | // Encrypt binary data (of any size). Output can be larger than input | 91 | // Encrypt binary data (of any size). Output can be larger than input |
@@ -114,7 +115,9 @@ func (pk *PKEYPublicImpl) BlockKey(label string, expires util.AbsoluteTime) (ske | |||
114 | kd := pk.pub.Bytes() | 115 | kd := pk.pub.Bytes() |
115 | prk := hkdf.Extract(sha512.New, kd, []byte("gns-aes-ctx-key")) | 116 | prk := hkdf.Extract(sha512.New, kd, []byte("gns-aes-ctx-key")) |
116 | rdr := hkdf.Expand(sha256.New, prk, []byte(label)) | 117 | rdr := hkdf.Expand(sha256.New, prk, []byte(label)) |
117 | rdr.Read(skey[:32]) | 118 | if _, err := rdr.Read(skey[:32]); err != nil { |
119 | logger.Printf(logger.ERROR, "[PKEYPublicImpl.BlockKey] failed: %s", err.Error()) | ||
120 | } | ||
118 | 121 | ||
119 | // assemble initialization vector | 122 | // assemble initialization vector |
120 | iv := &struct { | 123 | iv := &struct { |
@@ -128,7 +131,9 @@ func (pk *PKEYPublicImpl) BlockKey(label string, expires util.AbsoluteTime) (ske | |||
128 | } | 131 | } |
129 | prk = hkdf.Extract(sha512.New, kd, []byte("gns-aes-ctx-iv")) | 132 | prk = hkdf.Extract(sha512.New, kd, []byte("gns-aes-ctx-iv")) |
130 | rdr = hkdf.Expand(sha256.New, prk, []byte(label)) | 133 | rdr = hkdf.Expand(sha256.New, prk, []byte(label)) |
131 | rdr.Read(iv.Nonce) | 134 | if _, err := rdr.Read(iv.Nonce); err != nil { |
135 | logger.Printf(logger.ERROR, "[PKEYPublicImpl.BlockKey] failed: %s", err.Error()) | ||
136 | } | ||
132 | buf, _ := data.Marshal(iv) | 137 | buf, _ := data.Marshal(iv) |
133 | copy(skey[32:], buf) | 138 | copy(skey[32:], buf) |
134 | return | 139 | return |
@@ -184,30 +189,32 @@ func (pk *PKEYPrivateImpl) Public() ZoneKeyImpl { | |||
184 | 189 | ||
185 | // Derive a public key from this key based on a big integer | 190 | // Derive a public key from this key based on a big integer |
186 | // (key blinding). Returns the derived key and the blinding value. | 191 | // (key blinding). Returns the derived key and the blinding value. |
187 | func (pk *PKEYPrivateImpl) Derive(h *math.Int) (ZonePrivateImpl, *math.Int) { | 192 | func (pk *PKEYPrivateImpl) Derive(h *math.Int) (dPk ZonePrivateImpl, hOut *math.Int, err error) { |
188 | // limit to allowed value range | 193 | // limit to allowed value range |
189 | h = h.Mod(ed25519.GetCurve().N) | 194 | hOut = h.Mod(ed25519.GetCurve().N) |
190 | derived := pk.prv.Mult(h) | 195 | derived := pk.prv.Mult(hOut) |
191 | dPk := &PKEYPrivateImpl{ | 196 | dPk = &PKEYPrivateImpl{ |
192 | PKEYPublicImpl{ | 197 | PKEYPublicImpl{ |
193 | pk.ztype, | 198 | pk.ztype, |
194 | derived.Public(), | 199 | derived.Public(), |
195 | }, | 200 | }, |
196 | derived, | 201 | derived, |
197 | } | 202 | } |
198 | return dPk, h | 203 | return |
199 | } | 204 | } |
200 | 205 | ||
201 | // Verify a signature for binary data | 206 | // Verify a signature for binary data |
202 | func (pk *PKEYPrivateImpl) Sign(data []byte) (*ZoneSignature, error) { | 207 | func (pk *PKEYPrivateImpl) Sign(data []byte) (sig *ZoneSignature, err error) { |
203 | s, err := pk.prv.EcSign(data) | 208 | var s *ed25519.EcSignature |
204 | if err != nil { | 209 | if s, err = pk.prv.EcSign(data); err != nil { |
205 | return nil, err | 210 | return |
206 | } | 211 | } |
207 | sd := s.Bytes() | 212 | sd := s.Bytes() |
208 | sigImpl := new(PKEYSigImpl) | 213 | sigImpl := new(PKEYSigImpl) |
209 | sigImpl.Init(sd) | 214 | if err = sigImpl.Init(sd); err != nil { |
210 | sig := &ZoneSignature{ | 215 | return |
216 | } | ||
217 | sig = &ZoneSignature{ | ||
211 | ZoneKey{ | 218 | ZoneKey{ |
212 | Type: pk.ztype, | 219 | Type: pk.ztype, |
213 | KeyData: pk.pub.Bytes(), | 220 | KeyData: pk.pub.Bytes(), |
@@ -215,7 +222,7 @@ func (pk *PKEYPrivateImpl) Sign(data []byte) (*ZoneSignature, error) { | |||
215 | sd, | 222 | sd, |
216 | sigImpl, | 223 | sigImpl, |
217 | } | 224 | } |
218 | return sig, nil | 225 | return |
219 | } | 226 | } |
220 | 227 | ||
221 | //---------------------------------------------------------------------- | 228 | //---------------------------------------------------------------------- |
diff --git a/src/gnunet/crypto/gns_test.go b/src/gnunet/crypto/gns_test.go index 4ef64d4..12ab603 100644 --- a/src/gnunet/crypto/gns_test.go +++ b/src/gnunet/crypto/gns_test.go | |||
@@ -55,7 +55,9 @@ func TestDeriveBlockKey(t *testing.T) { | |||
55 | 55 | ||
56 | // create and initialize new public zone key (PKEY) | 56 | // create and initialize new public zone key (PKEY) |
57 | zkey := new(PKEYPublicImpl) | 57 | zkey := new(PKEYPublicImpl) |
58 | zkey.Init(PUB) | 58 | if err := zkey.Init(PUB); err != nil { |
59 | t.Fatal(err) | ||
60 | } | ||
59 | 61 | ||
60 | // derive and check a key for symmetric cipher | 62 | // derive and check a key for symmetric cipher |
61 | skey := zkey.BlockKey(LABEL, EXPIRE) | 63 | skey := zkey.BlockKey(LABEL, EXPIRE) |
@@ -267,7 +269,10 @@ func TestDeriveH(t *testing.T) { | |||
267 | } | 269 | } |
268 | 270 | ||
269 | // test key derivation | 271 | // test key derivation |
270 | dpub, h := pub.Derive(LABEL, CONTEXT) | 272 | dpub, h, err := pub.Derive(LABEL, CONTEXT) |
273 | if err != nil { | ||
274 | t.Fatal(err) | ||
275 | } | ||
271 | if !bytes.Equal(h.Bytes(), H) { | 276 | if !bytes.Equal(h.Bytes(), H) { |
272 | if testing.Verbose() { | 277 | if testing.Verbose() { |
273 | t.Logf("H(computed) = %s\n", hex.EncodeToString(h.Bytes())) | 278 | t.Logf("H(computed) = %s\n", hex.EncodeToString(h.Bytes())) |
@@ -297,7 +302,6 @@ func TestDeriveH(t *testing.T) { | |||
297 | } | 302 | } |
298 | 303 | ||
299 | func TestHKDF_gnunet(t *testing.T) { | 304 | func TestHKDF_gnunet(t *testing.T) { |
300 | |||
301 | var ( | 305 | var ( |
302 | // SALT as defined in GNUnet | 306 | // SALT as defined in GNUnet |
303 | salt = []byte("key-derivation") | 307 | salt = []byte("key-derivation") |
@@ -332,7 +336,9 @@ func TestHKDF_gnunet(t *testing.T) { | |||
332 | 336 | ||
333 | rdr := hkdf.Expand(sha256.New, prk, info) | 337 | rdr := hkdf.Expand(sha256.New, prk, info) |
334 | okm := make([]byte, len(OKM)) | 338 | okm := make([]byte, len(OKM)) |
335 | rdr.Read(okm) | 339 | if _, err := rdr.Read(okm); err != nil { |
340 | t.Fatal(err) | ||
341 | } | ||
336 | if testing.Verbose() { | 342 | if testing.Verbose() { |
337 | t.Log("OKM(computed) = " + hex.EncodeToString(okm)) | 343 | t.Log("OKM(computed) = " + hex.EncodeToString(okm)) |
338 | } | 344 | } |
@@ -387,7 +393,9 @@ func TestHDKF(t *testing.T) { | |||
387 | 393 | ||
388 | rdr := hkdf.Expand(sha512.New, prk, info) | 394 | rdr := hkdf.Expand(sha512.New, prk, info) |
389 | okm := make([]byte, len(OKM)) | 395 | okm := make([]byte, len(OKM)) |
390 | rdr.Read(okm) | 396 | if _, err := rdr.Read(okm); err != nil { |
397 | t.Fatal(err) | ||
398 | } | ||
391 | if testing.Verbose() { | 399 | if testing.Verbose() { |
392 | t.Log("OKM(computed) = " + hex.EncodeToString(okm)) | 400 | t.Log("OKM(computed) = " + hex.EncodeToString(okm)) |
393 | } | 401 | } |
diff --git a/src/gnunet/crypto/hash.go b/src/gnunet/crypto/hash.go index ed6edc7..437dcb2 100644 --- a/src/gnunet/crypto/hash.go +++ b/src/gnunet/crypto/hash.go | |||
@@ -21,6 +21,7 @@ package crypto | |||
21 | import ( | 21 | import ( |
22 | "bytes" | 22 | "bytes" |
23 | "crypto/sha512" | 23 | "crypto/sha512" |
24 | "encoding/hex" | ||
24 | 25 | ||
25 | "gnunet/util" | 26 | "gnunet/util" |
26 | ) | 27 | ) |
@@ -35,7 +36,17 @@ func (hc *HashCode) Equals(n *HashCode) bool { | |||
35 | return bytes.Equal(hc.Bits, n.Bits) | 36 | return bytes.Equal(hc.Bits, n.Bits) |
36 | } | 37 | } |
37 | 38 | ||
38 | // NewHashCode creates a new (initalized) hash value | 39 | // Clone the hash code |
40 | func (hc *HashCode) Clone() *HashCode { | ||
41 | return NewHashCode(hc.Bits) | ||
42 | } | ||
43 | |||
44 | // String returns a hex-representation of the hash code | ||
45 | func (hc *HashCode) String() string { | ||
46 | return hex.EncodeToString(hc.Bits) | ||
47 | } | ||
48 | |||
49 | // NewHashCode creates a new (initialized) hash value | ||
39 | func NewHashCode(buf []byte) *HashCode { | 50 | func NewHashCode(buf []byte) *HashCode { |
40 | hc := &HashCode{ | 51 | hc := &HashCode{ |
41 | Bits: make([]byte, 64), | 52 | Bits: make([]byte, 64), |
diff --git a/src/gnunet/crypto/key_exchange_test.go b/src/gnunet/crypto/key_exchange_test.go index 1e4f0dc..dbd1792 100644 --- a/src/gnunet/crypto/key_exchange_test.go +++ b/src/gnunet/crypto/key_exchange_test.go | |||
@@ -29,14 +29,14 @@ import ( | |||
29 | ) | 29 | ) |
30 | 30 | ||
31 | var ( | 31 | var ( |
32 | d_1 = []byte{ | 32 | d1 = []byte{ |
33 | 0x7F, 0xDE, 0x7A, 0xAA, 0xEA, 0x0D, 0xA1, 0x7A, | 33 | 0x7F, 0xDE, 0x7A, 0xAA, 0xEA, 0x0D, 0xA1, 0x7A, |
34 | 0x7B, 0xCB, 0x4F, 0x57, 0x49, 0xCC, 0xA9, 0xBE, | 34 | 0x7B, 0xCB, 0x4F, 0x57, 0x49, 0xCC, 0xA9, 0xBE, |
35 | 0xA7, 0xFB, 0x2B, 0x85, 0x77, 0xAD, 0xC9, 0x55, | 35 | 0xA7, 0xFB, 0x2B, 0x85, 0x77, 0xAD, 0xC9, 0x55, |
36 | 0xDA, 0xB2, 0x68, 0xB2, 0xB4, 0xCC, 0x24, 0x78, | 36 | 0xDA, 0xB2, 0x68, 0xB2, 0xB4, 0xCC, 0x24, 0x78, |
37 | } | 37 | } |
38 | 38 | ||
39 | d_2 = []byte{ | 39 | d2 = []byte{ |
40 | 0x20, 0x3f, 0x2f, 0x8c, 0x54, 0xf4, 0x1a, 0xd3, | 40 | 0x20, 0x3f, 0x2f, 0x8c, 0x54, 0xf4, 0x1a, 0xd3, |
41 | 0x01, 0x9a, 0x56, 0x92, 0x19, 0xda, 0xee, 0x4f, | 41 | 0x01, 0x9a, 0x56, 0x92, 0x19, 0xda, 0xee, 0x4f, |
42 | 0xd2, 0x53, 0x55, 0xa6, 0x3c, 0xfc, 0x57, 0x40, | 42 | 0xd2, 0x53, 0x55, 0xa6, 0x3c, 0xfc, 0x57, 0x40, |
@@ -54,11 +54,11 @@ var ( | |||
54 | 0x05, 0xbd, 0x1b, 0x85, 0xd5, 0xfd, 0x63, 0x60, | 54 | 0x05, 0xbd, 0x1b, 0x85, 0xd5, 0xfd, 0x63, 0x60, |
55 | } | 55 | } |
56 | 56 | ||
57 | prv_1, prv_2 *ed25519.PrivateKey | 57 | prv1, prv2 *ed25519.PrivateKey |
58 | pub_1, pub_2 *ed25519.PublicKey | 58 | pub1, pub2 *ed25519.PublicKey |
59 | ss_1, ss_2 []byte | 59 | ss1, ss2 []byte |
60 | 60 | ||
61 | ED25519_N = ed25519.GetCurve().N | 61 | ed25519N = ed25519.GetCurve().N |
62 | ) | 62 | ) |
63 | 63 | ||
64 | func calcSharedSecret() bool { | 64 | func calcSharedSecret() bool { |
@@ -67,29 +67,29 @@ func calcSharedSecret() bool { | |||
67 | return x[:] | 67 | return x[:] |
68 | } | 68 | } |
69 | // compute shared secret | 69 | // compute shared secret |
70 | ss_1 = calc(prv_1, pub_2) | 70 | ss1 = calc(prv1, pub2) |
71 | ss_2 = calc(prv_2, pub_1) | 71 | ss2 = calc(prv2, pub1) |
72 | return bytes.Equal(ss_1, ss_2) | 72 | return bytes.Equal(ss1, ss2) |
73 | } | 73 | } |
74 | 74 | ||
75 | func TestDHE(t *testing.T) { | 75 | func TestDHE(t *testing.T) { |
76 | // generate two key pairs | 76 | // generate two key pairs |
77 | prv_1 = ed25519.NewPrivateKeyFromD(math.NewIntFromBytes(d_1)) | 77 | prv1 = ed25519.NewPrivateKeyFromD(math.NewIntFromBytes(d1)) |
78 | pub_1 = prv_1.Public() | 78 | pub1 = prv1.Public() |
79 | prv_2 = ed25519.NewPrivateKeyFromD(math.NewIntFromBytes(d_2)) | 79 | prv2 = ed25519.NewPrivateKeyFromD(math.NewIntFromBytes(d2)) |
80 | pub_2 = prv_2.Public() | 80 | pub2 = prv2.Public() |
81 | 81 | ||
82 | if !calcSharedSecret() { | 82 | if !calcSharedSecret() { |
83 | t.Fatal("Shared secret mismatch") | 83 | t.Fatal("Shared secret mismatch") |
84 | } | 84 | } |
85 | if testing.Verbose() { | 85 | if testing.Verbose() { |
86 | t.Logf("SS_1 = %s\n", hex.EncodeToString(ss_1)) | 86 | t.Logf("SS_1 = %s\n", hex.EncodeToString(ss1)) |
87 | t.Logf("SS_2 = %s\n", hex.EncodeToString(ss_2)) | 87 | t.Logf("SS_2 = %s\n", hex.EncodeToString(ss2)) |
88 | } | 88 | } |
89 | 89 | ||
90 | if !bytes.Equal(ss_1[:], ss) { | 90 | if !bytes.Equal(ss1[:], ss) { |
91 | t.Logf("SS(expected) = %s\n", hex.EncodeToString(ss)) | 91 | t.Logf("SS(expected) = %s\n", hex.EncodeToString(ss)) |
92 | t.Logf("SS(computed) = %s\n", hex.EncodeToString(ss_1[:])) | 92 | t.Logf("SS(computed) = %s\n", hex.EncodeToString(ss1[:])) |
93 | t.Fatal("Wrong shared secret:") | 93 | t.Fatal("Wrong shared secret:") |
94 | } | 94 | } |
95 | } | 95 | } |
@@ -98,19 +98,19 @@ func TestDHERandom(t *testing.T) { | |||
98 | failed := 0 | 98 | failed := 0 |
99 | once := false | 99 | once := false |
100 | for i := 0; i < 100; i++ { | 100 | for i := 0; i < 100; i++ { |
101 | prv_1 = ed25519.NewPrivateKeyFromD(math.NewIntRnd(ED25519_N)) | 101 | prv1 = ed25519.NewPrivateKeyFromD(math.NewIntRnd(ed25519N)) |
102 | pub_1 = prv_1.Public() | 102 | pub1 = prv1.Public() |
103 | prv_2 = ed25519.NewPrivateKeyFromD(math.NewIntRnd(ED25519_N)) | 103 | prv2 = ed25519.NewPrivateKeyFromD(math.NewIntRnd(ed25519N)) |
104 | pub_2 = prv_2.Public() | 104 | pub2 = prv2.Public() |
105 | 105 | ||
106 | if !calcSharedSecret() { | 106 | if !calcSharedSecret() { |
107 | if !once { | 107 | if !once { |
108 | once = true | 108 | once = true |
109 | t.Logf("d1=%s\n", hex.EncodeToString(prv_1.D.Bytes())) | 109 | t.Logf("d1=%s\n", hex.EncodeToString(prv1.D.Bytes())) |
110 | t.Logf("d2=%s\n", hex.EncodeToString(prv_2.D.Bytes())) | 110 | t.Logf("d2=%s\n", hex.EncodeToString(prv2.D.Bytes())) |
111 | t.Logf("ss1=%s\n", hex.EncodeToString(ss_1)) | 111 | t.Logf("ss1=%s\n", hex.EncodeToString(ss1)) |
112 | t.Logf("ss2=%s\n", hex.EncodeToString(ss_2)) | 112 | t.Logf("ss2=%s\n", hex.EncodeToString(ss2)) |
113 | dd := prv_1.D.Mul(prv_2.D).Mod(ED25519_N) | 113 | dd := prv1.D.Mul(prv2.D).Mod(ed25519N) |
114 | pk := sha512.Sum512(ed25519.NewPrivateKeyFromD(dd).Public().Q.X().Bytes()) | 114 | pk := sha512.Sum512(ed25519.NewPrivateKeyFromD(dd).Public().Q.X().Bytes()) |
115 | t.Logf("ss0=%s\n", hex.EncodeToString(pk[:])) | 115 | t.Logf("ss0=%s\n", hex.EncodeToString(pk[:])) |
116 | } | 116 | } |
diff --git a/src/gnunet/crypto/keys_test.go b/src/gnunet/crypto/keys_test.go index d8ffe96..ca5c7b7 100644 --- a/src/gnunet/crypto/keys_test.go +++ b/src/gnunet/crypto/keys_test.go | |||
@@ -58,7 +58,7 @@ var ( | |||
58 | func TestPrvKey(t *testing.T) { | 58 | func TestPrvKey(t *testing.T) { |
59 | if testing.Verbose() { | 59 | if testing.Verbose() { |
60 | t.Logf("PRIVATE (seed=%s)\n", hex.EncodeToString(seed)) | 60 | t.Logf("PRIVATE (seed=%s)\n", hex.EncodeToString(seed)) |
61 | t.Logf(" d = %s\n", hex.EncodeToString(prv_1.D.Bytes())) | 61 | t.Logf(" d = %s\n", hex.EncodeToString(prv1.D.Bytes())) |
62 | t.Logf(" ID = '%s'\n", util.EncodeBinaryToString(seed)) | 62 | t.Logf(" ID = '%s'\n", util.EncodeBinaryToString(seed)) |
63 | } | 63 | } |
64 | 64 | ||
diff --git a/src/gnunet/enums/blocktype_string.go b/src/gnunet/enums/blocktype_string.go index 639501f..cc14d79 100644 --- a/src/gnunet/enums/blocktype_string.go +++ b/src/gnunet/enums/blocktype_string.go | |||
@@ -40,7 +40,7 @@ var ( | |||
40 | 40 | ||
41 | func (i BlockType) String() string { | 41 | func (i BlockType) String() string { |
42 | switch { | 42 | switch { |
43 | case 0 <= i && i <= 2: | 43 | case i <= 2: |
44 | return _BlockType_name_0[_BlockType_index_0[i]:_BlockType_index_0[i+1]] | 44 | return _BlockType_name_0[_BlockType_index_0[i]:_BlockType_index_0[i+1]] |
45 | case 6 <= i && i <= 13: | 45 | case 6 <= i && i <= 13: |
46 | i -= 6 | 46 | i -= 6 |
diff --git a/src/gnunet/enums/dht.go b/src/gnunet/enums/dht.go index 36c3d8a..040e72f 100644 --- a/src/gnunet/enums/dht.go +++ b/src/gnunet/enums/dht.go | |||
@@ -16,18 +16,16 @@ | |||
16 | // | 16 | // |
17 | // SPDX-License-Identifier: AGPL3.0-or-later | 17 | // SPDX-License-Identifier: AGPL3.0-or-later |
18 | 18 | ||
19 | //nolint:stylecheck // allow non-camel-case for constants | ||
19 | package enums | 20 | package enums |
20 | 21 | ||
21 | // DHT flags and settings | 22 | // DHT flags and settings |
22 | const ( | 23 | const ( |
23 | DHT_RO_NONE = 0 // Default. Do nothing special. | 24 | DHT_RO_NONE = 0 // Default. Do nothing special. |
24 | DHT_RO_DEMULTIPLEX_EVERYWHERE = 1 // Each peer along the way should look at 'enc' | 25 | DHT_RO_DEMULTIPLEX_EVERYWHERE = 1 // Each peer along the way should look at 'enc' |
25 | DHT_RO_RECORD_ROUTE = 2 // keep track of the route that the message took in the P2P network. | 26 | DHT_RO_RECORD_ROUTE = 2 // keep track of the route that the message took in the P2P network. |
26 | DHT_RO_FIND_PEER = 3 // This is a 'FIND-PEER' request, so approximate results are fine. | 27 | DHT_RO_FIND_APPROXIMATE = 4 // Approximate results are fine. |
27 | DHT_RO_BART = 4 // Possible message option for query key randomization. | 28 | DHT_RO_TRUNCATED = 8 // Flag if path is truncated |
28 | DHT_RO_LAST_HOP = 16 // Flag given to monitors if this was the last hop for a GET/PUT. | ||
29 | |||
30 | DHT_GNS_REPLICATION_LEVEL = 10 | ||
31 | ) | 29 | ) |
32 | 30 | ||
33 | //go:generate go run generate.go gnunet-dht.rec gnunet-dht.tpl dht_block_type.go | 31 | //go:generate go run generate.go gnunet-dht.rec gnunet-dht.tpl dht_block_type.go |
diff --git a/src/gnunet/enums/dht_block_type.go b/src/gnunet/enums/dht_block_type.go index e435419..beb52dc 100644 --- a/src/gnunet/enums/dht_block_type.go +++ b/src/gnunet/enums/dht_block_type.go | |||
@@ -1,8 +1,9 @@ | |||
1 | // Code generated by enum generator; DO NOT EDIT. | 1 | // Code generated by enum generator; DO NOT EDIT. |
2 | 2 | ||
3 | //nolint:stylecheck // allow non-camel-case for constants | ||
3 | package enums | 4 | package enums |
4 | 5 | ||
5 | type BlockType int | 6 | type BlockType uint16 |
6 | 7 | ||
7 | // DHT block types | 8 | // DHT block types |
8 | const ( | 9 | const ( |
diff --git a/src/gnunet/enums/gns.go b/src/gnunet/enums/gns.go index 5ab28ad..f6e58a2 100644 --- a/src/gnunet/enums/gns.go +++ b/src/gnunet/enums/gns.go | |||
@@ -16,6 +16,7 @@ | |||
16 | // | 16 | // |
17 | // SPDX-License-Identifier: AGPL3.0-or-later | 17 | // SPDX-License-Identifier: AGPL3.0-or-later |
18 | 18 | ||
19 | //nolint:stylecheck // allow non-camel-case for constants | ||
19 | package enums | 20 | package enums |
20 | 21 | ||
21 | const ( | 22 | const ( |
@@ -31,6 +32,8 @@ const ( | |||
31 | GNS_LO_LOCAL_MASTER = 2 // For the rightmost label, only look in the cache. | 32 | GNS_LO_LOCAL_MASTER = 2 // For the rightmost label, only look in the cache. |
32 | 33 | ||
33 | GNS_MAX_BLOCK_SIZE = (63 * 1024) // Maximum size of a value that can be stored in a GNS block. | 34 | GNS_MAX_BLOCK_SIZE = (63 * 1024) // Maximum size of a value that can be stored in a GNS block. |
35 | |||
36 | GNS_REPLICATION_LEVEL = 10 | ||
34 | ) | 37 | ) |
35 | 38 | ||
36 | //go:generate go run generate.go gnunet-gns.rec gnunet-gns.tpl gns_type.go | 39 | //go:generate go run generate.go gnunet-gns.rec gnunet-gns.tpl gns_type.go |
diff --git a/src/gnunet/enums/gns_type.go b/src/gnunet/enums/gns_type.go index 2b55817..5a13d67 100644 --- a/src/gnunet/enums/gns_type.go +++ b/src/gnunet/enums/gns_type.go | |||
@@ -1,5 +1,6 @@ | |||
1 | // Code generated by enum generator; DO NOT EDIT. | 1 | // Code generated by enum generator; DO NOT EDIT. |
2 | 2 | ||
3 | //nolint:stylecheck // allow non-camel-case for constants | ||
3 | package enums | 4 | package enums |
4 | 5 | ||
5 | type GNSType int | 6 | type GNSType int |
diff --git a/src/gnunet/enums/gnunet-dht.tpl b/src/gnunet/enums/gnunet-dht.tpl index 0010897..ed00e57 100644 --- a/src/gnunet/enums/gnunet-dht.tpl +++ b/src/gnunet/enums/gnunet-dht.tpl | |||
@@ -1,8 +1,9 @@ | |||
1 | // Code generated by enum generator; DO NOT EDIT. | 1 | // Code generated by enum generator; DO NOT EDIT. |
2 | 2 | ||
3 | //nolint:stylecheck // allow non-camel-case for constants | ||
3 | package enums | 4 | package enums |
4 | 5 | ||
5 | type BlockType int | 6 | type BlockType uint16 |
6 | 7 | ||
7 | // DHT block types | 8 | // DHT block types |
8 | const ( | 9 | const ( |
diff --git a/src/gnunet/enums/gnunet-gns.tpl b/src/gnunet/enums/gnunet-gns.tpl index 075fe73..3249569 100644 --- a/src/gnunet/enums/gnunet-gns.tpl +++ b/src/gnunet/enums/gnunet-gns.tpl | |||
@@ -1,5 +1,6 @@ | |||
1 | // Code generated by enum generator; DO NOT EDIT. | 1 | // Code generated by enum generator; DO NOT EDIT. |
2 | 2 | ||
3 | //nolint:stylecheck // allow non-camel-case for constants | ||
3 | package enums | 4 | package enums |
4 | 5 | ||
5 | type GNSType int | 6 | type GNSType int |
diff --git a/src/gnunet/enums/gnunet-signature.tpl b/src/gnunet/enums/gnunet-signature.tpl index 009c086..5af6c28 100644 --- a/src/gnunet/enums/gnunet-signature.tpl +++ b/src/gnunet/enums/gnunet-signature.tpl | |||
@@ -1,5 +1,6 @@ | |||
1 | // Code generated by enum generator; DO NOT EDIT. | 1 | // Code generated by enum generator; DO NOT EDIT. |
2 | 2 | ||
3 | //nolint:stylecheck // allow non-camel-case for constants | ||
3 | package enums | 4 | package enums |
4 | 5 | ||
5 | type SigPurpose int | 6 | type SigPurpose int |
diff --git a/src/gnunet/enums/signature_purpose.go b/src/gnunet/enums/signature_purpose.go index ad4bf0a..0c1901e 100644 --- a/src/gnunet/enums/signature_purpose.go +++ b/src/gnunet/enums/signature_purpose.go | |||
@@ -1,5 +1,6 @@ | |||
1 | // Code generated by enum generator; DO NOT EDIT. | 1 | // Code generated by enum generator; DO NOT EDIT. |
2 | 2 | ||
3 | //nolint:stylecheck // allow non-camel-case for constants | ||
3 | package enums | 4 | package enums |
4 | 5 | ||
5 | type SigPurpose int | 6 | type SigPurpose int |
diff --git a/src/gnunet/go.mod b/src/gnunet/go.mod index bb2e58f..5109da4 100644 --- a/src/gnunet/go.mod +++ b/src/gnunet/go.mod | |||
@@ -3,10 +3,11 @@ module gnunet | |||
3 | go 1.18 | 3 | go 1.18 |
4 | 4 | ||
5 | require ( | 5 | require ( |
6 | github.com/bfix/gospel v1.2.14 | 6 | github.com/bfix/gospel v1.2.15 |
7 | github.com/go-redis/redis/v8 v8.11.5 | 7 | github.com/go-redis/redis/v8 v8.11.5 |
8 | github.com/go-sql-driver/mysql v1.6.0 | 8 | github.com/go-sql-driver/mysql v1.6.0 |
9 | github.com/gorilla/mux v1.8.0 | 9 | github.com/gorilla/mux v1.8.0 |
10 | github.com/gorilla/rpc v1.2.0 | ||
10 | github.com/mattn/go-sqlite3 v1.14.13 | 11 | github.com/mattn/go-sqlite3 v1.14.13 |
11 | github.com/miekg/dns v1.1.49 | 12 | github.com/miekg/dns v1.1.49 |
12 | golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 | 13 | golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 |
diff --git a/src/gnunet/go.sum b/src/gnunet/go.sum index 2451a41..5a08cee 100644 --- a/src/gnunet/go.sum +++ b/src/gnunet/go.sum | |||
@@ -1,5 +1,5 @@ | |||
1 | github.com/bfix/gospel v1.2.14 h1:lIdagJvkebG+uYbVdfK6XbT1udnq/ezd/Gi54EaMtV0= | 1 | github.com/bfix/gospel v1.2.15 h1:f0t8dvihSXWvhnXDI2q7FCtG7LHg5qImjEWdzIN/luY= |
2 | github.com/bfix/gospel v1.2.14/go.mod h1:cdu63bA9ZdfeDoqZ+vnWOcbY9Puwdzmf5DMxMGMznRI= | 2 | github.com/bfix/gospel v1.2.15/go.mod h1:cdu63bA9ZdfeDoqZ+vnWOcbY9Puwdzmf5DMxMGMznRI= |
3 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= | 3 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= |
4 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | 4 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= |
5 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= | 5 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= |
@@ -11,6 +11,8 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC | |||
11 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | 11 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= |
12 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= | 12 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= |
13 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= | 13 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= |
14 | github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk= | ||
15 | github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ= | ||
14 | github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= | 16 | github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= |
15 | github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= | 17 | github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= |
16 | github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= | 18 | github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= |
diff --git a/src/gnunet/message/factory.go b/src/gnunet/message/factory.go index 25506dc..23da078 100644 --- a/src/gnunet/message/factory.go +++ b/src/gnunet/message/factory.go | |||
@@ -66,8 +66,18 @@ func NewEmptyMessage(msgType uint16) (Message, error) { | |||
66 | return NewDHTClientResultMsg(nil), nil | 66 | return NewDHTClientResultMsg(nil), nil |
67 | case DHT_CLIENT_GET_RESULTS_KNOWN: | 67 | case DHT_CLIENT_GET_RESULTS_KNOWN: |
68 | return NewDHTClientGetResultsKnownMsg(nil), nil | 68 | return NewDHTClientGetResultsKnownMsg(nil), nil |
69 | |||
70 | //------------------------------------------------------------------ | ||
71 | // DHT-P2P | ||
72 | //------------------------------------------------------------------ | ||
69 | case DHT_P2P_HELLO: | 73 | case DHT_P2P_HELLO: |
70 | return NewHelloDHTMsg(), nil | 74 | return NewDHTP2PHelloMsg(), nil |
75 | case DHT_P2P_GET: | ||
76 | return NewDHTP2PGetMsg(), nil | ||
77 | case DHT_P2P_PUT: | ||
78 | return NewDHTP2PPutMsg(), nil | ||
79 | case DHT_P2P_RESULT: | ||
80 | return NewDHTP2PResultMsg(), nil | ||
71 | 81 | ||
72 | //------------------------------------------------------------------ | 82 | //------------------------------------------------------------------ |
73 | // GNS | 83 | // GNS |
diff --git a/src/gnunet/message/msg_core.go b/src/gnunet/message/msg_core.go index 245c61d..208b438 100644 --- a/src/gnunet/message/msg_core.go +++ b/src/gnunet/message/msg_core.go | |||
@@ -36,17 +36,17 @@ type EphKeyBlock struct { | |||
36 | Purpose *crypto.SignaturePurpose // signature purpose: SIG_ECC_KEY | 36 | Purpose *crypto.SignaturePurpose // signature purpose: SIG_ECC_KEY |
37 | CreateTime util.AbsoluteTime // Time of key creation | 37 | CreateTime util.AbsoluteTime // Time of key creation |
38 | ExpireTime util.RelativeTime // Time to live for key | 38 | ExpireTime util.RelativeTime // Time to live for key |
39 | EphemeralKey []byte `size:"32"` // Ephemeral EdDSA public key | 39 | EphemeralKey *util.PeerPublicKey // Ephemeral EdDSA public key |
40 | PeerID *util.PeerID // Peer identity (EdDSA public key) | 40 | PeerID *util.PeerID // Peer identity (EdDSA public key) |
41 | } | 41 | } |
42 | 42 | ||
43 | // EphemeralKeyMsg announces a new transient key for a peer. The key is signed | 43 | // EphemeralKeyMsg announces a new transient key for a peer. The key is signed |
44 | // by the issuing peer. | 44 | // by the issuing peer. |
45 | type EphemeralKeyMsg struct { | 45 | type EphemeralKeyMsg struct { |
46 | MsgSize uint16 `order:"big"` // total size of message | 46 | MsgSize uint16 `order:"big"` // total size of message |
47 | MsgType uint16 `order:"big"` // CORE_EPHEMERAL_KEY (88) | 47 | MsgType uint16 `order:"big"` // CORE_EPHEMERAL_KEY (88) |
48 | SenderStatus uint32 `order:"big"` // enum PeerStateMachine | 48 | SenderStatus uint32 `order:"big"` // enum PeerStateMachine |
49 | Signature []byte `size:"64"` // EdDSA signature | 49 | Signature *util.PeerSignature `` // EdDSA signature |
50 | SignedBlock *EphKeyBlock | 50 | SignedBlock *EphKeyBlock |
51 | } | 51 | } |
52 | 52 | ||
@@ -56,7 +56,7 @@ func NewEphemeralKeyMsg() *EphemeralKeyMsg { | |||
56 | MsgSize: 160, | 56 | MsgSize: 160, |
57 | MsgType: CORE_EPHEMERAL_KEY, | 57 | MsgType: CORE_EPHEMERAL_KEY, |
58 | SenderStatus: 1, | 58 | SenderStatus: 1, |
59 | Signature: make([]byte, 64), | 59 | Signature: util.NewPeerSignature(nil), |
60 | SignedBlock: &EphKeyBlock{ | 60 | SignedBlock: &EphKeyBlock{ |
61 | Purpose: &crypto.SignaturePurpose{ | 61 | Purpose: &crypto.SignaturePurpose{ |
62 | Size: 88, | 62 | Size: 88, |
@@ -64,7 +64,7 @@ func NewEphemeralKeyMsg() *EphemeralKeyMsg { | |||
64 | }, | 64 | }, |
65 | CreateTime: util.AbsoluteTimeNow(), | 65 | CreateTime: util.AbsoluteTimeNow(), |
66 | ExpireTime: util.NewRelativeTime(12 * time.Hour), | 66 | ExpireTime: util.NewRelativeTime(12 * time.Hour), |
67 | EphemeralKey: make([]byte, 32), | 67 | EphemeralKey: util.NewPeerPublicKey(nil), |
68 | PeerID: util.NewPeerID(nil), | 68 | PeerID: util.NewPeerID(nil), |
69 | }, | 69 | }, |
70 | } | 70 | } |
@@ -73,8 +73,8 @@ func NewEphemeralKeyMsg() *EphemeralKeyMsg { | |||
73 | // String returns a human-readable representation of the message. | 73 | // String returns a human-readable representation of the message. |
74 | func (m *EphemeralKeyMsg) String() string { | 74 | func (m *EphemeralKeyMsg) String() string { |
75 | return fmt.Sprintf("EphKeyMsg{peer=%s,ephkey=%s,create=%s,expire=%s,status=%d}", | 75 | return fmt.Sprintf("EphKeyMsg{peer=%s,ephkey=%s,create=%s,expire=%s,status=%d}", |
76 | util.EncodeBinaryToString(m.SignedBlock.PeerID.Key), | 76 | util.EncodeBinaryToString(m.SignedBlock.PeerID.Data), |
77 | util.EncodeBinaryToString(m.SignedBlock.EphemeralKey), | 77 | util.EncodeBinaryToString(m.SignedBlock.EphemeralKey.Data), |
78 | m.SignedBlock.CreateTime, m.SignedBlock.ExpireTime, | 78 | m.SignedBlock.CreateTime, m.SignedBlock.ExpireTime, |
79 | m.SenderStatus) | 79 | m.SenderStatus) |
80 | } | 80 | } |
@@ -85,8 +85,8 @@ func (m *EphemeralKeyMsg) Header() *Header { | |||
85 | } | 85 | } |
86 | 86 | ||
87 | // Public extracts the public key of an announcing peer. | 87 | // Public extracts the public key of an announcing peer. |
88 | func (m *EphemeralKeyMsg) Public() *ed25519.PublicKey { | 88 | func (m *EphemeralKeyMsg) Public() *util.PeerPublicKey { |
89 | return m.SignedBlock.PeerID.PublicKey() | 89 | return util.NewPeerPublicKey(m.SignedBlock.PeerID.Data) |
90 | } | 90 | } |
91 | 91 | ||
92 | // Verify the integrity of the message data using the public key of the | 92 | // Verify the integrity of the message data using the public key of the |
@@ -96,7 +96,7 @@ func (m *EphemeralKeyMsg) Verify(pub *ed25519.PublicKey) (bool, error) { | |||
96 | if err != nil { | 96 | if err != nil { |
97 | return false, err | 97 | return false, err |
98 | } | 98 | } |
99 | sig, err := ed25519.NewEdSignatureFromBytes(m.Signature) | 99 | sig, err := ed25519.NewEdSignatureFromBytes(m.Signature.Data) |
100 | if err != nil { | 100 | if err != nil { |
101 | return false, err | 101 | return false, err |
102 | } | 102 | } |
@@ -107,10 +107,10 @@ func (m *EphemeralKeyMsg) Verify(pub *ed25519.PublicKey) (bool, error) { | |||
107 | // key and the corresponding GNUnet message to announce the new key. | 107 | // key and the corresponding GNUnet message to announce the new key. |
108 | func NewEphemeralKey(peerID []byte, ltPrv *ed25519.PrivateKey) (*ed25519.PrivateKey, *EphemeralKeyMsg, error) { | 108 | func NewEphemeralKey(peerID []byte, ltPrv *ed25519.PrivateKey) (*ed25519.PrivateKey, *EphemeralKeyMsg, error) { |
109 | msg := NewEphemeralKeyMsg() | 109 | msg := NewEphemeralKeyMsg() |
110 | copy(msg.SignedBlock.PeerID.Key, peerID) | 110 | copy(msg.SignedBlock.PeerID.Data, peerID) |
111 | seed := util.NewRndArray(32) | 111 | seed := util.NewRndArray(32) |
112 | prv := ed25519.NewPrivateKeyFromSeed(seed) | 112 | prv := ed25519.NewPrivateKeyFromSeed(seed) |
113 | copy(msg.SignedBlock.EphemeralKey, prv.Public().Bytes()) | 113 | copy(msg.SignedBlock.EphemeralKey.Data, prv.Public().Bytes()) |
114 | 114 | ||
115 | data, err := data.Marshal(msg.SignedBlock) | 115 | data, err := data.Marshal(msg.SignedBlock) |
116 | if err != nil { | 116 | if err != nil { |
@@ -120,7 +120,7 @@ func NewEphemeralKey(peerID []byte, ltPrv *ed25519.PrivateKey) (*ed25519.Private | |||
120 | if err != nil { | 120 | if err != nil { |
121 | return nil, nil, err | 121 | return nil, nil, err |
122 | } | 122 | } |
123 | copy(msg.Signature, sig.Bytes()) | 123 | copy(msg.Signature.Data, sig.Bytes()) |
124 | 124 | ||
125 | return prv, msg, nil | 125 | return prv, msg, nil |
126 | } | 126 | } |
diff --git a/src/gnunet/message/msg_dht_p2p.go b/src/gnunet/message/msg_dht_p2p.go new file mode 100644 index 0000000..55bb71d --- /dev/null +++ b/src/gnunet/message/msg_dht_p2p.go | |||
@@ -0,0 +1,473 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package message | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "crypto/sha512" | ||
24 | "encoding/binary" | ||
25 | "errors" | ||
26 | "fmt" | ||
27 | "gnunet/crypto" | ||
28 | "gnunet/enums" | ||
29 | "gnunet/service/dht/blocks" | ||
30 | "gnunet/util" | ||
31 | "time" | ||
32 | |||
33 | "github.com/bfix/gospel/crypto/ed25519" | ||
34 | "github.com/bfix/gospel/data" | ||
35 | "github.com/bfix/gospel/logger" | ||
36 | ) | ||
37 | |||
38 | //====================================================================== | ||
39 | // DHT-P2P is a next-generation implementation of the R5N DHT. | ||
40 | //====================================================================== | ||
41 | |||
42 | // shared path element data across types | ||
43 | type pathElementData struct { | ||
44 | Expiration util.AbsoluteTime // expiration date | ||
45 | BlockHash *crypto.HashCode // block hash | ||
46 | PeerPredecessor *util.PeerID // predecessor peer | ||
47 | PeerSuccessor *util.PeerID // successor peer | ||
48 | } | ||
49 | |||
50 | // helper type for signature creation/verification | ||
51 | type pathElementSignedData struct { | ||
52 | Size uint16 `order:"big"` // size of signed data | ||
53 | Purpose uint16 `order:"big"` // signature purpose (SIG_DHT_HOP) | ||
54 | Elem *pathElementData `` // path element data | ||
55 | } | ||
56 | |||
57 | // PathElement is the full-fledged data assembly for a path element in | ||
58 | // PUT/GET pathes. It is assembled programatically (on generation[1] and | ||
59 | // verification[2]) and not transferred in messages directly. | ||
60 | // | ||
61 | // [1] spe = &PathElement{...} | ||
62 | // core.Sign(spe) | ||
63 | // msg.putpath[i] = spe.Wire() | ||
64 | // | ||
65 | // [2] pe = &PathElement{...,Signature: wire.sig} | ||
66 | // if !pe.Verify(peerId) { ... } | ||
67 | // | ||
68 | type PathElement struct { | ||
69 | pathElementData | ||
70 | Signature *util.PeerSignature // signature | ||
71 | } | ||
72 | |||
73 | // NewPathElement creates a new path element from data | ||
74 | func NewPathElement(key *crypto.HashCode, pred, succ *util.PeerID) *PathElement { | ||
75 | return &PathElement{ | ||
76 | pathElementData: pathElementData{ | ||
77 | Expiration: util.AbsoluteTimeNow().Add(12 * time.Hour), | ||
78 | BlockHash: key, | ||
79 | PeerPredecessor: pred, | ||
80 | PeerSuccessor: succ, | ||
81 | }, | ||
82 | Signature: nil, | ||
83 | } | ||
84 | } | ||
85 | |||
86 | // PathElementWire is the data stored and retrieved from messages | ||
87 | type PathElementWire struct { | ||
88 | Predecessor *util.PeerID // peer id of predecessor | ||
89 | Signature *util.PeerSignature // path signature | ||
90 | } | ||
91 | |||
92 | // Size returns the size of a path element in wire format | ||
93 | func (pew *PathElementWire) Size() uint16 { | ||
94 | return 96 | ||
95 | } | ||
96 | |||
97 | // SignedData gets the data to be signed by peer ('Signable' interface) | ||
98 | func (pe *PathElement) SignedData() []byte { | ||
99 | sd := &pathElementSignedData{ | ||
100 | Size: 80, | ||
101 | Purpose: uint16(enums.SIG_DHT_HOP), | ||
102 | Elem: &(pe.pathElementData), | ||
103 | } | ||
104 | buf, err := data.Marshal(sd) | ||
105 | if err != nil { | ||
106 | logger.Println(logger.ERROR, "can't serialize path element for signature") | ||
107 | return nil | ||
108 | } | ||
109 | return buf | ||
110 | } | ||
111 | |||
112 | // SetSignature stores the generated signature. | ||
113 | func (pe *PathElement) SetSignature(sig *util.PeerSignature) error { | ||
114 | pe.Signature = sig | ||
115 | return nil | ||
116 | } | ||
117 | |||
118 | // Wire returns the path element suitable for inclusion into messages | ||
119 | func (pe *PathElement) Wire() *PathElementWire { | ||
120 | return &PathElementWire{ | ||
121 | Predecessor: pe.PeerPredecessor, | ||
122 | Signature: pe.Signature, | ||
123 | } | ||
124 | } | ||
125 | |||
126 | //---------------------------------------------------------------------- | ||
127 | // DHT-P2P-GET messages are used to request information from other | ||
128 | // peers in the DHT. | ||
129 | //---------------------------------------------------------------------- | ||
130 | |||
131 | // DHTP2PGetMsg wire layout | ||
132 | type DHTP2PGetMsg struct { | ||
133 | MsgSize uint16 `order:"big"` // total size of message | ||
134 | MsgType uint16 `order:"big"` // DHT_P2P_GET (147) | ||
135 | BType uint32 `order:"big"` // content type of the payload | ||
136 | Flags uint16 `order:"big"` // processing flags | ||
137 | HopCount uint16 `order:"big"` // number of hops so far | ||
138 | ReplLevel uint16 `order:"big"` // Replication level | ||
139 | RfSize uint16 `order:"big"` // size of result filter | ||
140 | PeerFilter *blocks.PeerFilter `` // peer filter to prevent loops | ||
141 | Query *crypto.HashCode `` // query hash | ||
142 | ResFilter []byte `size:"RfSize"` // result filter | ||
143 | XQuery []byte `size:"*"` // extended query | ||
144 | } | ||
145 | |||
146 | // NewDHTP2PGetMsg creates an empty DHT-P2P-Get message | ||
147 | func NewDHTP2PGetMsg() *DHTP2PGetMsg { | ||
148 | return &DHTP2PGetMsg{ | ||
149 | MsgSize: 208, // message size without ResFiter and XQuery | ||
150 | MsgType: DHT_P2P_GET, // DHT_P2P_GET (147) | ||
151 | BType: 0, // no block type defined | ||
152 | Flags: 0, // no flags defined | ||
153 | HopCount: 0, // no hops | ||
154 | ReplLevel: 0, // no replication level defined | ||
155 | RfSize: 0, // no result filter | ||
156 | PeerFilter: blocks.NewPeerFilter(), // allocate bloom filter | ||
157 | Query: crypto.NewHashCode(nil), // empty Query hash | ||
158 | ResFilter: nil, // empty result filter | ||
159 | XQuery: nil, // empty XQuery | ||
160 | } | ||
161 | } | ||
162 | |||
163 | // String returns a human-readable representation of the message. | ||
164 | func (m *DHTP2PGetMsg) String() string { | ||
165 | return fmt.Sprintf("DHTP2PGetMsg{btype=%s,hops=%d,flags=%d}", | ||
166 | enums.BlockType(m.BType).String(), m.HopCount, m.Flags) | ||
167 | } | ||
168 | |||
169 | // Header returns the message header in a separate instance. | ||
170 | func (m *DHTP2PGetMsg) Header() *Header { | ||
171 | return &Header{m.MsgSize, m.MsgType} | ||
172 | } | ||
173 | |||
174 | // Clone message | ||
175 | func (m *DHTP2PGetMsg) Update(pf *blocks.PeerFilter, rf blocks.ResultFilter, hop uint16) *DHTP2PGetMsg { | ||
176 | buf := rf.Bytes() | ||
177 | ns := uint16(len(buf)) | ||
178 | return &DHTP2PGetMsg{ | ||
179 | MsgSize: m.MsgSize - m.RfSize + ns, | ||
180 | MsgType: DHT_P2P_GET, | ||
181 | BType: m.BType, | ||
182 | Flags: m.Flags, | ||
183 | HopCount: hop, | ||
184 | ReplLevel: m.ReplLevel, | ||
185 | RfSize: ns, | ||
186 | PeerFilter: pf.Clone(), | ||
187 | Query: m.Query, | ||
188 | ResFilter: buf, | ||
189 | XQuery: util.Clone(m.XQuery), | ||
190 | } | ||
191 | } | ||
192 | |||
193 | //---------------------------------------------------------------------- | ||
194 | // DHT-P2P-PUT messages are used by other peers in the DHT to | ||
195 | // request block storage. | ||
196 | //---------------------------------------------------------------------- | ||
197 | |||
198 | // DHTP2PPutMsg wire layout | ||
199 | type DHTP2PPutMsg struct { | ||
200 | MsgSize uint16 `order:"big"` // total size of message | ||
201 | MsgType uint16 `order:"big"` // DHT_P2P_PUT (146) | ||
202 | BType uint32 `order:"big"` // block type | ||
203 | Flags uint16 `order:"big"` // processing flags | ||
204 | HopCount uint16 `order:"big"` // message hops | ||
205 | ReplLvl uint16 `order:"big"` // replication level | ||
206 | PathL uint16 `order:"big"` // path length | ||
207 | Expiration util.AbsoluteTime `` // expiration date | ||
208 | PeerFilter *blocks.PeerFilter `` // peer bloomfilter | ||
209 | Key *crypto.HashCode `` // query key to block | ||
210 | Origin []byte `size:"(PESize)"` // truncated origin (if TRUNCATED flag set) | ||
211 | PutPath []*PathElementWire `size:"PathL"` // PUT path | ||
212 | LastSig []byte `size:"(PESize)"` // signature of last hop (if RECORD_ROUTE flag is set) | ||
213 | Block []byte `size:"*"` // block data | ||
214 | } | ||
215 | |||
216 | // NewDHTP2PPutMsg creates an empty new DHTP2PPutMsg | ||
217 | func NewDHTP2PPutMsg() *DHTP2PPutMsg { | ||
218 | return &DHTP2PPutMsg{ | ||
219 | MsgSize: 218, // total size without path and block data | ||
220 | MsgType: DHT_P2P_PUT, // DHT_P2P_PUT (146) | ||
221 | BType: 0, // block type | ||
222 | Flags: 0, // processing flags | ||
223 | HopCount: 0, // message hops | ||
224 | ReplLvl: 0, // replication level | ||
225 | PathL: 0, // no PUT path | ||
226 | Expiration: util.AbsoluteTimeNever(), // expiration date | ||
227 | PeerFilter: blocks.NewPeerFilter(), // peer bloom filter | ||
228 | Key: crypto.NewHashCode(nil), // query key | ||
229 | Origin: nil, // no truncated path | ||
230 | PutPath: make([]*PathElementWire, 0), // empty PUT path | ||
231 | LastSig: nil, // no signature from last hop | ||
232 | Block: nil, // no block data | ||
233 | } | ||
234 | } | ||
235 | |||
236 | // PESize calculates field sizes based on flags and attributes | ||
237 | func (m *DHTP2PPutMsg) PESize(field string) uint { | ||
238 | switch field { | ||
239 | case "Origin": | ||
240 | if m.Flags&enums.DHT_RO_TRUNCATED != 0 { | ||
241 | return 32 | ||
242 | } | ||
243 | case "LastSig": | ||
244 | if m.Flags&enums.DHT_RO_RECORD_ROUTE != 0 { | ||
245 | return 64 | ||
246 | } | ||
247 | } | ||
248 | return 0 | ||
249 | } | ||
250 | |||
251 | // AddPutPath adds an element to the PUT path | ||
252 | func (m *DHTP2PPutMsg) AppendPutPath(pe *PathElement) { | ||
253 | pew := pe.Wire() | ||
254 | m.PutPath = append(m.PutPath, pew) | ||
255 | m.PathL++ | ||
256 | m.MsgSize += pew.Size() | ||
257 | } | ||
258 | |||
259 | // String returns a human-readable representation of the message. | ||
260 | func (m *DHTP2PPutMsg) String() string { | ||
261 | return fmt.Sprintf("DHTP2PPutMsg{btype=%s,hops=%d,flags=%d}", | ||
262 | enums.BlockType(m.BType).String(), m.HopCount, m.Flags) | ||
263 | } | ||
264 | |||
265 | // Header returns the message header in a separate instance. | ||
266 | func (m *DHTP2PPutMsg) Header() *Header { | ||
267 | return &Header{m.MsgSize, m.MsgType} | ||
268 | } | ||
269 | |||
270 | //---------------------------------------------------------------------- | ||
271 | // DHT-P2P-RESULT messages are used to answer peer requests for | ||
272 | // bock retrieval. | ||
273 | //---------------------------------------------------------------------- | ||
274 | |||
275 | // DHTP2PResultMsg wire layout | ||
276 | type DHTP2PResultMsg struct { | ||
277 | MsgSize uint16 `order:"big"` // total size of message | ||
278 | MsgType uint16 `order:"big"` // DHT_P2P_RESULT (148) | ||
279 | BType uint32 `order:"big"` // Block type of result | ||
280 | Reserved uint32 `order:"big"` // Reserved for further use | ||
281 | PutPathL uint16 `order:"big"` // size of PUTPATH field | ||
282 | GetPathL uint16 `order:"big"` // size of GETPATH field | ||
283 | Expires util.AbsoluteTime `` // expiration date | ||
284 | Query *crypto.HashCode `` // Query key for block | ||
285 | Origin []byte `size:"(PESize)"` // truncated origin (if TRUNCATED flag set) | ||
286 | PutPath []*PathElementWire `size:"PutPathL"` // PUTPATH | ||
287 | GetPath []*PathElementWire `size:"GetPathL"` // GETPATH | ||
288 | LastSig []byte `size:"(PESize)"` // signature of last hop (if RECORD_ROUTE flag is set) | ||
289 | Block []byte `size:"*"` // block data | ||
290 | } | ||
291 | |||
292 | // NewDHTP2PResultMsg creates a new empty DHTP2PResultMsg | ||
293 | func NewDHTP2PResultMsg() *DHTP2PResultMsg { | ||
294 | return &DHTP2PResultMsg{ | ||
295 | MsgSize: 88, // size of empty message | ||
296 | MsgType: DHT_P2P_RESULT, // DHT_P2P_RESULT (148) | ||
297 | BType: uint32(enums.BLOCK_TYPE_ANY), // type of returned block | ||
298 | Origin: nil, // no truncated origin | ||
299 | PutPathL: 0, // empty putpath | ||
300 | PutPath: nil, // -"- | ||
301 | GetPathL: 0, // empty getpath | ||
302 | GetPath: nil, // -"- | ||
303 | LastSig: nil, // no recorded route | ||
304 | Block: nil, // empty block | ||
305 | } | ||
306 | } | ||
307 | |||
308 | // PESize calculates field sizes based on flags and attributes | ||
309 | func (m *DHTP2PResultMsg) PESize(field string) uint { | ||
310 | switch field { | ||
311 | case "Origin": | ||
312 | //if m.Flags&enums.DHT_RO_TRUNCATED != 0 { | ||
313 | return 32 | ||
314 | //} | ||
315 | case "LastSig": | ||
316 | //if m.Flags&enums.DHT_RO_RECORD_ROUTE != 0 { | ||
317 | return 64 | ||
318 | //} | ||
319 | } | ||
320 | return 0 | ||
321 | } | ||
322 | |||
323 | // String returns a human-readable representation of the message. | ||
324 | func (m *DHTP2PResultMsg) String() string { | ||
325 | return fmt.Sprintf("DHTP2PResultMsg{btype=%s,putl=%d,getl=%d}", | ||
326 | enums.BlockType(m.BType).String(), m.PutPathL, m.GetPathL) | ||
327 | } | ||
328 | |||
329 | // Header returns the message header in a separate instance. | ||
330 | func (m *DHTP2PResultMsg) Header() *Header { | ||
331 | return &Header{m.MsgSize, m.MsgType} | ||
332 | } | ||
333 | |||
334 | //---------------------------------------------------------------------- | ||
335 | // DHT-P2P-HELLO | ||
336 | // | ||
337 | // A DHT-P2P-HELLO message is used to exchange information about transports | ||
338 | // with other DHT nodes. This struct is always followed by the actual | ||
339 | // network addresses of type "HelloAddress" | ||
340 | //---------------------------------------------------------------------- | ||
341 | |||
342 | // DHTP2PHelloMsg is a message send by peers to announce their presence | ||
343 | type DHTP2PHelloMsg struct { | ||
344 | MsgSize uint16 `order:"big"` // total size of message | ||
345 | MsgType uint16 `order:"big"` // DHT_P2P_HELLO (157) | ||
346 | Reserved uint16 `order:"big"` // Reserved for further use | ||
347 | NumAddr uint16 `order:"big"` // Number of addresses in list | ||
348 | Signature *util.PeerSignature `` // Signature | ||
349 | Expires util.AbsoluteTime `` // expiration time | ||
350 | AddrList []byte `size:"*"` // List of end-point addresses (HelloAddress) | ||
351 | } | ||
352 | |||
353 | // NewHelloMsgDHT creates an empty DHT_P2P_HELLO message. | ||
354 | func NewDHTP2PHelloMsg() *DHTP2PHelloMsg { | ||
355 | // return empty HelloMessage | ||
356 | exp := time.Now().Add(HelloAddressExpiration) | ||
357 | return &DHTP2PHelloMsg{ | ||
358 | MsgSize: 80, // size without 'AddrList' | ||
359 | MsgType: DHT_P2P_HELLO, // DHT_P2P_HELLO (157) | ||
360 | Reserved: 0, // not used here | ||
361 | NumAddr: 0, // start with empty address list | ||
362 | Signature: util.NewPeerSignature(nil), // signature | ||
363 | Expires: util.NewAbsoluteTime(exp), // default expiration | ||
364 | AddrList: make([]byte, 0), // list of addresses | ||
365 | } | ||
366 | } | ||
367 | |||
368 | // Addresses returns the list of HelloAddress | ||
369 | func (m *DHTP2PHelloMsg) Addresses() (list []*util.Address, err error) { | ||
370 | var addr *util.Address | ||
371 | var as string | ||
372 | num, pos := 0, 0 | ||
373 | for { | ||
374 | // parse address string from stream | ||
375 | if as, pos = util.ReadCString(m.AddrList, pos); pos == -1 { | ||
376 | break | ||
377 | } | ||
378 | if addr, err = util.ParseAddress(as); err != nil { | ||
379 | return | ||
380 | } | ||
381 | addr.Expires = m.Expires | ||
382 | list = append(list, addr) | ||
383 | num++ | ||
384 | } | ||
385 | // check numbers | ||
386 | if num != int(m.NumAddr) { | ||
387 | logger.Printf(logger.WARN, "[DHTP2PHelloMsg] Number of addresses does not match (got %d, expected %d)", num, m.NumAddr) | ||
388 | } | ||
389 | return | ||
390 | } | ||
391 | |||
392 | // SetAddresses adds addresses to the HELLO message. | ||
393 | func (m *DHTP2PHelloMsg) SetAddresses(list []*util.Address) { | ||
394 | // write addresses as blob and track earliest expiration | ||
395 | exp := util.NewAbsoluteTime(time.Now().Add(HelloAddressExpiration)) | ||
396 | wrt := new(bytes.Buffer) | ||
397 | for _, addr := range list { | ||
398 | // check if address expires before current expire | ||
399 | if exp.Compare(addr.Expires) > 0 { | ||
400 | exp = addr.Expires | ||
401 | } | ||
402 | n, _ := wrt.Write([]byte(addr.URI())) | ||
403 | wrt.WriteByte(0) | ||
404 | m.MsgSize += uint16(n + 1) | ||
405 | } | ||
406 | m.AddrList = wrt.Bytes() | ||
407 | m.Expires = exp | ||
408 | m.NumAddr = uint16(len(list)) | ||
409 | } | ||
410 | |||
411 | // String returns a human-readable representation of the message. | ||
412 | func (m *DHTP2PHelloMsg) String() string { | ||
413 | addrs, _ := m.Addresses() | ||
414 | aList := "" | ||
415 | for i, a := range addrs { | ||
416 | if i > 0 { | ||
417 | aList += "," | ||
418 | } | ||
419 | aList += a.URI() | ||
420 | } | ||
421 | return fmt.Sprintf("DHTP2PHelloMsg{expire:%s,addrs=%d:[%s]}", m.Expires, m.NumAddr, aList) | ||
422 | } | ||
423 | |||
424 | // Header returns the message header in a separate instance. | ||
425 | func (m *DHTP2PHelloMsg) Header() *Header { | ||
426 | return &Header{m.MsgSize, m.MsgType} | ||
427 | } | ||
428 | |||
429 | // Verify the message signature | ||
430 | func (m *DHTP2PHelloMsg) Verify(peer *util.PeerID) (bool, error) { | ||
431 | // assemble signed data and public key | ||
432 | sd := m.SignedData() | ||
433 | pub := ed25519.NewPublicKeyFromBytes(peer.Data) | ||
434 | sig, err := ed25519.NewEdSignatureFromBytes(m.Signature.Data) | ||
435 | if err != nil { | ||
436 | return false, err | ||
437 | } | ||
438 | return pub.EdVerify(sd, sig) | ||
439 | } | ||
440 | |||
441 | // SetSignature stores a signature in the the HELLO block | ||
442 | func (m *DHTP2PHelloMsg) SetSignature(sig *util.PeerSignature) error { | ||
443 | m.Signature = sig | ||
444 | return nil | ||
445 | } | ||
446 | |||
447 | // SignedData assembles a data block for sign and verify operations. | ||
448 | func (m *DHTP2PHelloMsg) SignedData() []byte { | ||
449 | // hash address block | ||
450 | hAddr := sha512.Sum512(m.AddrList) | ||
451 | var size uint32 = 80 | ||
452 | purpose := uint32(enums.SIG_HELLO) | ||
453 | |||
454 | // assemble signed data | ||
455 | buf := new(bytes.Buffer) | ||
456 | var n int | ||
457 | err := binary.Write(buf, binary.BigEndian, size) | ||
458 | if err == nil { | ||
459 | if err = binary.Write(buf, binary.BigEndian, purpose); err == nil { | ||
460 | if err = binary.Write(buf, binary.BigEndian, m.Expires.Epoch()*1000000); err == nil { | ||
461 | if n, err = buf.Write(hAddr[:]); err == nil { | ||
462 | if n != len(hAddr[:]) { | ||
463 | err = errors.New("write failed") | ||
464 | } | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | } | ||
469 | if err != nil { | ||
470 | logger.Printf(logger.ERROR, "[DHTP2PHelloMsg.SignedData] failed: %s", err.Error()) | ||
471 | } | ||
472 | return buf.Bytes() | ||
473 | } | ||
diff --git a/src/gnunet/message/msg_hello.go b/src/gnunet/message/msg_hello.go index ae4c0e2..fdd9696 100644 --- a/src/gnunet/message/msg_hello.go +++ b/src/gnunet/message/msg_hello.go | |||
@@ -25,6 +25,8 @@ import ( | |||
25 | "gnunet/util" | 25 | "gnunet/util" |
26 | "io" | 26 | "io" |
27 | "time" | 27 | "time" |
28 | |||
29 | "github.com/bfix/gospel/logger" | ||
28 | ) | 30 | ) |
29 | 31 | ||
30 | //---------------------------------------------------------------------- | 32 | //---------------------------------------------------------------------- |
@@ -112,11 +114,19 @@ func (a *HelloAddress) String() string { | |||
112 | // Bytes returns the binary representation of a HelloAddress | 114 | // Bytes returns the binary representation of a HelloAddress |
113 | func (a *HelloAddress) Bytes() []byte { | 115 | func (a *HelloAddress) Bytes() []byte { |
114 | buf := new(bytes.Buffer) | 116 | buf := new(bytes.Buffer) |
115 | buf.Write([]byte(a.transport)) | 117 | _, err := buf.Write([]byte(a.transport)) |
116 | buf.WriteByte(0) | 118 | if err == nil { |
117 | binary.Write(buf, binary.BigEndian, a.addrSize) | 119 | if err = buf.WriteByte(0); err == nil { |
118 | binary.Write(buf, binary.BigEndian, a.expires.Val) | 120 | if err = binary.Write(buf, binary.BigEndian, a.addrSize); err == nil { |
119 | buf.Write(a.address) | 121 | if err = binary.Write(buf, binary.BigEndian, a.expires.Val); err != nil { |
122 | _, err = buf.Write(a.address) | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | if err != nil { | ||
128 | logger.Printf(logger.ERROR, "[HelloAddress] failed: %s", err.Error()) | ||
129 | } | ||
120 | return buf.Bytes() | 130 | return buf.Bytes() |
121 | } | 131 | } |
122 | 132 | ||
diff --git a/src/gnunet/message/msg_hello_dht.go b/src/gnunet/message/msg_hello_dht.go deleted file mode 100644 index f51757c..0000000 --- a/src/gnunet/message/msg_hello_dht.go +++ /dev/null | |||
@@ -1,167 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package message | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "crypto/sha512" | ||
24 | "encoding/binary" | ||
25 | "fmt" | ||
26 | "gnunet/enums" | ||
27 | "gnunet/util" | ||
28 | "time" | ||
29 | |||
30 | "github.com/bfix/gospel/crypto/ed25519" | ||
31 | "github.com/bfix/gospel/logger" | ||
32 | ) | ||
33 | |||
34 | //---------------------------------------------------------------------- | ||
35 | // HELLO-DHT | ||
36 | // | ||
37 | // A HELLO message is used to exchange information about transports with | ||
38 | // other DHT nodes. This struct is always followed by the actual network | ||
39 | // addresses of type "HelloAddress" | ||
40 | //---------------------------------------------------------------------- | ||
41 | |||
42 | // HelloDHTMsg is a message send by peers to announce their presence | ||
43 | type HelloDHTMsg struct { | ||
44 | MsgSize uint16 `order:"big"` // total size of message | ||
45 | MsgType uint16 `order:"big"` // DHT_P2P_HELLO (157) | ||
46 | Reserved uint16 `order:"big"` // Reserved for further use | ||
47 | NumAddr uint16 `order:"big"` // Number of addresses in list | ||
48 | Signature []byte `size:"64"` // Signature | ||
49 | Expires util.AbsoluteTime `` // expiration time | ||
50 | AddrList []byte `size:"*"` // List of end-point addresses (HelloAddress) | ||
51 | } | ||
52 | |||
53 | // NewHelloMsgDHT creates an empty DHT_P2P_HELLO message. | ||
54 | func NewHelloDHTMsg() *HelloDHTMsg { | ||
55 | // return empty HelloMessage | ||
56 | exp := time.Now().Add(HelloAddressExpiration) | ||
57 | return &HelloDHTMsg{ | ||
58 | MsgSize: 80, // size without 'AddrList' | ||
59 | MsgType: DHT_P2P_HELLO, // DHT_P2P_HELLO (157) | ||
60 | Reserved: 0, // not used here | ||
61 | NumAddr: 0, // start with empty address list | ||
62 | Signature: make([]byte, 64), // signature | ||
63 | Expires: util.NewAbsoluteTime(exp), // default expiration | ||
64 | AddrList: make([]byte, 0), // list of addresses | ||
65 | } | ||
66 | } | ||
67 | |||
68 | // Addresses returns the list of HelloAddress | ||
69 | func (m *HelloDHTMsg) Addresses() (list []*util.Address, err error) { | ||
70 | var addr *util.Address | ||
71 | var as string | ||
72 | num, pos := 0, 0 | ||
73 | for { | ||
74 | // parse address string from stream | ||
75 | if as, pos = util.ReadCString(m.AddrList, pos); pos == -1 { | ||
76 | break | ||
77 | } | ||
78 | if addr, err = util.ParseAddress(as); err != nil { | ||
79 | return | ||
80 | } | ||
81 | addr.Expires = m.Expires | ||
82 | list = append(list, addr) | ||
83 | num++ | ||
84 | } | ||
85 | // check numbers | ||
86 | if num != int(m.NumAddr) { | ||
87 | logger.Printf(logger.WARN, "[HelloDHTMsg] Number of addresses does not match (got %d, expected %d)", num, m.NumAddr) | ||
88 | } | ||
89 | return | ||
90 | } | ||
91 | |||
92 | // SetAddresses adds addresses to the HELLO message. | ||
93 | func (m *HelloDHTMsg) SetAddresses(list []*util.Address) { | ||
94 | // write addresses as blob and track earliest expiration | ||
95 | exp := util.NewAbsoluteTime(time.Now().Add(HelloAddressExpiration)) | ||
96 | wrt := new(bytes.Buffer) | ||
97 | for _, addr := range list { | ||
98 | // check if address expires before current expire | ||
99 | if exp.Compare(addr.Expires) > 0 { | ||
100 | exp = addr.Expires | ||
101 | } | ||
102 | n, _ := wrt.Write([]byte(addr.URI())) | ||
103 | wrt.WriteByte(0) | ||
104 | m.MsgSize += uint16(n + 1) | ||
105 | } | ||
106 | m.AddrList = wrt.Bytes() | ||
107 | m.Expires = exp | ||
108 | m.NumAddr = uint16(len(list)) | ||
109 | } | ||
110 | |||
111 | // String returns a human-readable representation of the message. | ||
112 | func (m *HelloDHTMsg) String() string { | ||
113 | addrs, _ := m.Addresses() | ||
114 | aList := "" | ||
115 | for i, a := range addrs { | ||
116 | if i > 0 { | ||
117 | aList += "," | ||
118 | } | ||
119 | aList += a.URI() | ||
120 | } | ||
121 | return fmt.Sprintf("HelloDHTMsg{expire:%s,addrs=%d:[%s]}", m.Expires, m.NumAddr, aList) | ||
122 | } | ||
123 | |||
124 | // Header returns the message header in a separate instance. | ||
125 | func (m *HelloDHTMsg) Header() *Header { | ||
126 | return &Header{m.MsgSize, m.MsgType} | ||
127 | } | ||
128 | |||
129 | // Verify the message signature | ||
130 | func (m *HelloDHTMsg) Verify(peer *util.PeerID) (bool, error) { | ||
131 | // assemble signed data and public key | ||
132 | sd := m.signedData() | ||
133 | pub := peer.PublicKey() | ||
134 | sig, err := ed25519.NewEdSignatureFromBytes(m.Signature) | ||
135 | if err != nil { | ||
136 | return false, err | ||
137 | } | ||
138 | return pub.EdVerify(sd, sig) | ||
139 | } | ||
140 | |||
141 | // Sign the HELLO data with private key | ||
142 | func (m *HelloDHTMsg) Sign(prv *ed25519.PrivateKey) error { | ||
143 | // assemble signed data | ||
144 | sd := m.signedData() | ||
145 | sig, err := prv.EdSign(sd) | ||
146 | if err != nil { | ||
147 | return err | ||
148 | } | ||
149 | m.Signature = sig.Bytes() | ||
150 | return nil | ||
151 | } | ||
152 | |||
153 | // signedData assembles a data block for sign and verify operations. | ||
154 | func (m *HelloDHTMsg) signedData() []byte { | ||
155 | // hash address block | ||
156 | hAddr := sha512.Sum512(m.AddrList) | ||
157 | var size uint32 = 80 | ||
158 | purpose := uint32(enums.SIG_HELLO) | ||
159 | |||
160 | // assemble signed data | ||
161 | buf := new(bytes.Buffer) | ||
162 | binary.Write(buf, binary.BigEndian, size) | ||
163 | binary.Write(buf, binary.BigEndian, purpose) | ||
164 | binary.Write(buf, binary.BigEndian, m.Expires.Epoch()*1000000) | ||
165 | buf.Write(hAddr[:]) | ||
166 | return buf.Bytes() | ||
167 | } | ||
diff --git a/src/gnunet/message/msg_namecache.go b/src/gnunet/message/msg_namecache.go index 517f11b..c36ec7c 100644 --- a/src/gnunet/message/msg_namecache.go +++ b/src/gnunet/message/msg_namecache.go | |||
@@ -149,7 +149,7 @@ func (m *NamecacheCacheMsg) Header() *Header { | |||
149 | // NAMECACHE_BLOCK_CACHE_RESPONSE | 149 | // NAMECACHE_BLOCK_CACHE_RESPONSE |
150 | //---------------------------------------------------------------------- | 150 | //---------------------------------------------------------------------- |
151 | 151 | ||
152 | // NamecacheCacheResponseMsg is the reponse message for a put request | 152 | // NamecacheCacheResponseMsg is the response message for a put request |
153 | type NamecacheCacheResponseMsg struct { | 153 | type NamecacheCacheResponseMsg struct { |
154 | MsgSize uint16 `order:"big"` // total size of message | 154 | MsgSize uint16 `order:"big"` // total size of message |
155 | MsgType uint16 `order:"big"` // NAMECACHE_LOOKUP_BLOCK_RESPONSE (432) | 155 | MsgType uint16 `order:"big"` // NAMECACHE_LOOKUP_BLOCK_RESPONSE (432) |
diff --git a/src/gnunet/message/msg_transport.go b/src/gnunet/message/msg_transport.go index 18d8ceb..fca651f 100644 --- a/src/gnunet/message/msg_transport.go +++ b/src/gnunet/message/msg_transport.go | |||
@@ -28,6 +28,7 @@ import ( | |||
28 | 28 | ||
29 | "github.com/bfix/gospel/crypto/ed25519" | 29 | "github.com/bfix/gospel/crypto/ed25519" |
30 | "github.com/bfix/gospel/data" | 30 | "github.com/bfix/gospel/data" |
31 | "github.com/bfix/gospel/logger" | ||
31 | ) | 32 | ) |
32 | 33 | ||
33 | //---------------------------------------------------------------------- | 34 | //---------------------------------------------------------------------- |
@@ -106,7 +107,9 @@ func NewTransportPingMsg(target *util.PeerID, a *util.Address) *TransportPingMsg | |||
106 | // String returns a human-readable representation of the message. | 107 | // String returns a human-readable representation of the message. |
107 | func (m *TransportPingMsg) String() string { | 108 | func (m *TransportPingMsg) String() string { |
108 | a := new(util.Address) | 109 | a := new(util.Address) |
109 | data.Unmarshal(a, m.Address) | 110 | if err := data.Unmarshal(a, m.Address); err != nil { |
111 | logger.Printf(logger.ERROR, "[TransportPingMsg.String] failed: %s", err.Error()) | ||
112 | } | ||
110 | return fmt.Sprintf("TransportPingMsg{target=%s,addr=%s,challenge=%d}", | 113 | return fmt.Sprintf("TransportPingMsg{target=%s,addr=%s,challenge=%d}", |
111 | m.Target, a, m.Challenge) | 114 | m.Target, a, m.Challenge) |
112 | } | 115 | } |
@@ -155,7 +158,7 @@ func NewSignedAddress(a *util.Address) *SignedAddress { | |||
155 | return addr | 158 | return addr |
156 | } | 159 | } |
157 | 160 | ||
158 | // TransportPongMsg is a reponse message for a PING request | 161 | // TransportPongMsg is a response message for a PING request |
159 | type TransportPongMsg struct { | 162 | type TransportPongMsg struct { |
160 | MsgSize uint16 `order:"big"` // total size of message | 163 | MsgSize uint16 `order:"big"` // total size of message |
161 | MsgType uint16 `order:"big"` // TRANSPORT_PING (372) | 164 | MsgType uint16 `order:"big"` // TRANSPORT_PING (372) |
@@ -164,7 +167,7 @@ type TransportPongMsg struct { | |||
164 | SignedBlock *SignedAddress // signed block of data | 167 | SignedBlock *SignedAddress // signed block of data |
165 | } | 168 | } |
166 | 169 | ||
167 | // NewTransportPongMsg creates a reponse message with an address the replying | 170 | // NewTransportPongMsg creates a response message with an address the replying |
168 | // peer wants to be reached. | 171 | // peer wants to be reached. |
169 | func NewTransportPongMsg(challenge uint32, a *util.Address) *TransportPongMsg { | 172 | func NewTransportPongMsg(challenge uint32, a *util.Address) *TransportPongMsg { |
170 | m := &TransportPongMsg{ | 173 | m := &TransportPongMsg{ |
@@ -189,7 +192,7 @@ func (m *TransportPongMsg) String() string { | |||
189 | return fmt.Sprintf("TransportPongMsg{addr=%s,challenge=%d}", | 192 | return fmt.Sprintf("TransportPongMsg{addr=%s,challenge=%d}", |
190 | a, m.Challenge) | 193 | a, m.Challenge) |
191 | } | 194 | } |
192 | return fmt.Sprintf("TransportPongMsg{addr=<unkown>,%d}", m.Challenge) | 195 | return fmt.Sprintf("TransportPongMsg{addr=<unknown>,%d}", m.Challenge) |
193 | } | 196 | } |
194 | 197 | ||
195 | // Header returns the message header in a separate instance. | 198 | // Header returns the message header in a separate instance. |
@@ -228,7 +231,7 @@ func (m *TransportPongMsg) Verify(pub *ed25519.PublicKey) (bool, error) { | |||
228 | // TRANSPORT_SESSION_ACK | 231 | // TRANSPORT_SESSION_ACK |
229 | //---------------------------------------------------------------------- | 232 | //---------------------------------------------------------------------- |
230 | 233 | ||
231 | // SessionAckMsg is a message to acknowlege a session request | 234 | // SessionAckMsg is a message to acknowledge a session request |
232 | type SessionAckMsg struct { | 235 | type SessionAckMsg struct { |
233 | MsgSize uint16 `order:"big"` // total size of message | 236 | MsgSize uint16 `order:"big"` // total size of message |
234 | MsgType uint16 `order:"big"` // TRANSPORT_SESSION_ACK (377) | 237 | MsgType uint16 `order:"big"` // TRANSPORT_SESSION_ACK (377) |
diff --git a/src/gnunet/message/types.go b/src/gnunet/message/types.go index 3b1cf7a..6fa4d3a 100644 --- a/src/gnunet/message/types.go +++ b/src/gnunet/message/types.go | |||
@@ -16,6 +16,7 @@ | |||
16 | // | 16 | // |
17 | // SPDX-License-Identifier: AGPL3.0-or-later | 17 | // SPDX-License-Identifier: AGPL3.0-or-later |
18 | 18 | ||
19 | //nolint:stylecheck // allow non-camel-case in constants | ||
19 | package message | 20 | package message |
20 | 21 | ||
21 | // GNUnet message types | 22 | // GNUnet message types |
@@ -370,7 +371,7 @@ const ( | |||
370 | NAMESTORE_MONITOR_START = 441 // Client to service: start monitoring (yields sequence of "ZONE_ITERATION_RESPONSES" --- forever). | 371 | NAMESTORE_MONITOR_START = 441 // Client to service: start monitoring (yields sequence of "ZONE_ITERATION_RESPONSES" --- forever). |
371 | NAMESTORE_MONITOR_SYNC = 442 // Service to client: you're now in sync. | 372 | NAMESTORE_MONITOR_SYNC = 442 // Service to client: you're now in sync. |
372 | NAMESTORE_RECORD_RESULT = 443 // Service to client: here is a (plaintext) record you requested. | 373 | NAMESTORE_RECORD_RESULT = 443 // Service to client: here is a (plaintext) record you requested. |
373 | NAMESTORE_MONITOR_NEXT = 444 // Client to service: I am now ready for the next (set of) monitor events. Monitoring equivlaent of #NAMESTORE_ZONE_ITERATION_NEXT. | 374 | NAMESTORE_MONITOR_NEXT = 444 // Client to service: I am now ready for the next (set of) monitor events. Monitoring equivalent of #NAMESTORE_ZONE_ITERATION_NEXT. |
374 | NAMESTORE_ZONE_ITERATION_START = 445 // Client to service: please start iteration; receives "NAMESTORE_LOOKUP_NAME_RESPONSE" messages in return. | 375 | NAMESTORE_ZONE_ITERATION_START = 445 // Client to service: please start iteration; receives "NAMESTORE_LOOKUP_NAME_RESPONSE" messages in return. |
375 | NAMESTORE_ZONE_ITERATION_NEXT = 447 // Client to service: next record(s) in iteration please. | 376 | NAMESTORE_ZONE_ITERATION_NEXT = 447 // Client to service: next record(s) in iteration please. |
376 | NAMESTORE_ZONE_ITERATION_STOP = 448 // Client to service: stop iterating. | 377 | NAMESTORE_ZONE_ITERATION_STOP = 448 // Client to service: stop iterating. |
@@ -445,7 +446,7 @@ const ( | |||
445 | CONSENSUS_P2P_ELEMENTS_REQUEST = 544 // Elements, and requests for further elements | 446 | CONSENSUS_P2P_ELEMENTS_REQUEST = 544 // Elements, and requests for further elements |
446 | CONSENSUS_P2P_ELEMENTS_REPORT = 545 // Elements that a peer reports to be missing at the remote peer. | 447 | CONSENSUS_P2P_ELEMENTS_REPORT = 545 // Elements that a peer reports to be missing at the remote peer. |
447 | CONSENSUS_P2P_HELLO = 546 // Initialization message for consensus p2p communication. | 448 | CONSENSUS_P2P_HELLO = 546 // Initialization message for consensus p2p communication. |
448 | CONSENSUS_P2P_SYNCED = 547 // Report that the peer is synced with the partner after successfuly decoding the invertible bloom filter. | 449 | CONSENSUS_P2P_SYNCED = 547 // Report that the peer is synced with the partner after successfully decoding the invertible bloom filter. |
449 | CONSENSUS_P2P_FIN = 548 // Interaction os over, got synched and reported all elements | 450 | CONSENSUS_P2P_FIN = 548 // Interaction os over, got synched and reported all elements |
450 | CONSENSUS_P2P_ABORT = 548 // Abort a round, don't send requested elements anymore | 451 | CONSENSUS_P2P_ABORT = 548 // Abort a round, don't send requested elements anymore |
451 | CONSENSUS_P2P_ROUND_CONTEXT = 547 // Abort a round, don't send requested elements anymore | 452 | CONSENSUS_P2P_ROUND_CONTEXT = 547 // Abort a round, don't send requested elements anymore |
diff --git a/src/gnunet/service/client.go b/src/gnunet/service/client.go index 81a9f01..5f7f8f0 100644 --- a/src/gnunet/service/client.go +++ b/src/gnunet/service/client.go | |||
@@ -67,7 +67,6 @@ func RequestResponse( | |||
67 | callee string, | 67 | callee string, |
68 | path string, | 68 | path string, |
69 | req message.Message) (message.Message, error) { | 69 | req message.Message) (message.Message, error) { |
70 | |||
71 | // client-connect to the service | 70 | // client-connect to the service |
72 | logger.Printf(logger.DBG, "[%s] Connecting to %s service...\n", caller, callee) | 71 | logger.Printf(logger.DBG, "[%s] Connecting to %s service...\n", caller, callee) |
73 | cl, err := NewClient(ctx, path) | 72 | cl, err := NewClient(ctx, path) |
diff --git a/src/gnunet/service/connection.go b/src/gnunet/service/connection.go index 1c690c5..d443160 100644 --- a/src/gnunet/service/connection.go +++ b/src/gnunet/service/connection.go | |||
@@ -23,6 +23,7 @@ import ( | |||
23 | "errors" | 23 | "errors" |
24 | "fmt" | 24 | "fmt" |
25 | "gnunet/message" | 25 | "gnunet/message" |
26 | "gnunet/util" | ||
26 | "net" | 27 | "net" |
27 | "os" | 28 | "os" |
28 | "strconv" | 29 | "strconv" |
@@ -43,6 +44,7 @@ var ( | |||
43 | // based on Unix domain sockets. It is used locally by services and | 44 | // based on Unix domain sockets. It is used locally by services and |
44 | // clients in the standard GNUnet environment. | 45 | // clients in the standard GNUnet environment. |
45 | type Connection struct { | 46 | type Connection struct { |
47 | id int // connection identifier | ||
46 | path string // file name of Unix socket | 48 | path string // file name of Unix socket |
47 | conn net.Conn // associated connection | 49 | conn net.Conn // associated connection |
48 | buf []byte // read/write buffer | 50 | buf []byte // read/write buffer |
@@ -53,6 +55,7 @@ type Connection struct { | |||
53 | func NewConnection(ctx context.Context, path string) (s *Connection, err error) { | 55 | func NewConnection(ctx context.Context, path string) (s *Connection, err error) { |
54 | var d net.Dialer | 56 | var d net.Dialer |
55 | s = new(Connection) | 57 | s = new(Connection) |
58 | s.id = util.NextID() | ||
56 | s.path = path | 59 | s.path = path |
57 | s.buf = make([]byte, 65536) | 60 | s.buf = make([]byte, 65536) |
58 | s.conn, err = d.DialContext(ctx, "unix", path) | 61 | s.conn, err = d.DialContext(ctx, "unix", path) |
@@ -118,11 +121,11 @@ func (s *Connection) Receive(ctx context.Context) (message.Message, error) { | |||
118 | return nil, err | 121 | return nil, err |
119 | } | 122 | } |
120 | // get rest of message | 123 | // get rest of message |
121 | if err := get(4, int(mh.MsgSize)-4); err != nil { | 124 | if err = get(4, int(mh.MsgSize)-4); err != nil { |
122 | return nil, err | 125 | return nil, err |
123 | } | 126 | } |
124 | msg, err := message.NewEmptyMessage(mh.MsgType) | 127 | var msg message.Message |
125 | if err != nil { | 128 | if msg, err = message.NewEmptyMessage(mh.MsgType); err != nil { |
126 | return nil, err | 129 | return nil, err |
127 | } | 130 | } |
128 | if msg == nil { | 131 | if msg == nil { |
@@ -134,6 +137,11 @@ func (s *Connection) Receive(ctx context.Context) (message.Message, error) { | |||
134 | return msg, nil | 137 | return msg, nil |
135 | } | 138 | } |
136 | 139 | ||
140 | // Receiver returns the receiving client (string representation) | ||
141 | func (s *Connection) Receiver() string { | ||
142 | return fmt.Sprintf("uds:%d", s.id) | ||
143 | } | ||
144 | |||
137 | //---------------------------------------------------------------------- | 145 | //---------------------------------------------------------------------- |
138 | // internal methods | 146 | // internal methods |
139 | //---------------------------------------------------------------------- | 147 | //---------------------------------------------------------------------- |
@@ -212,7 +220,6 @@ func NewConnectionManager( | |||
212 | params map[string]string, // connection parameters | 220 | params map[string]string, // connection parameters |
213 | hdlr chan *Connection, // handler for incoming connections | 221 | hdlr chan *Connection, // handler for incoming connections |
214 | ) (cs *ConnectionManager, err error) { | 222 | ) (cs *ConnectionManager, err error) { |
215 | |||
216 | // instantiate channel server | 223 | // instantiate channel server |
217 | cs = &ConnectionManager{ | 224 | cs = &ConnectionManager{ |
218 | listener: nil, | 225 | listener: nil, |
@@ -224,24 +231,21 @@ func NewConnectionManager( | |||
224 | return | 231 | return |
225 | } | 232 | } |
226 | // handle additional parameters | 233 | // handle additional parameters |
227 | if params != nil { | 234 | for key, value := range params { |
228 | for key, value := range params { | 235 | switch key { |
229 | switch key { | 236 | case "perm": // set permissions on 'unix' |
230 | case "perm": // set permissions on 'unix' | 237 | if perm, err := strconv.ParseInt(value, 8, 32); err == nil { |
231 | if perm, err := strconv.ParseInt(value, 8, 32); err == nil { | 238 | if err := os.Chmod(path, os.FileMode(perm)); err != nil { |
232 | if err := os.Chmod(path, os.FileMode(perm)); err != nil { | ||
233 | logger.Printf( | ||
234 | logger.ERROR, | ||
235 | "MsgChannelServer: Failed to set permissions %s on %s: %s\n", | ||
236 | path, value, err.Error()) | ||
237 | |||
238 | } | ||
239 | } else { | ||
240 | logger.Printf( | 239 | logger.Printf( |
241 | logger.ERROR, | 240 | logger.ERROR, |
242 | "MsgChannelServer: Invalid permissions '%s'\n", | 241 | "MsgChannelServer: Failed to set permissions %s on %s: %s\n", |
243 | value) | 242 | path, value, err.Error()) |
244 | } | 243 | } |
244 | } else { | ||
245 | logger.Printf( | ||
246 | logger.ERROR, | ||
247 | "MsgChannelServer: Invalid permissions '%s'\n", | ||
248 | value) | ||
245 | } | 249 | } |
246 | } | 250 | } |
247 | } | 251 | } |
diff --git a/src/gnunet/service/dht/blocks/filters.go b/src/gnunet/service/dht/blocks/filters.go new file mode 100644 index 0000000..0d194cc --- /dev/null +++ b/src/gnunet/service/dht/blocks/filters.go | |||
@@ -0,0 +1,296 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "crypto/sha512" | ||
24 | "encoding/binary" | ||
25 | "gnunet/util" | ||
26 | |||
27 | "github.com/bfix/gospel/logger" | ||
28 | ) | ||
29 | |||
30 | //====================================================================== | ||
31 | // Peer filter | ||
32 | //====================================================================== | ||
33 | |||
34 | // PeerFilter is a bloom filter without mutator | ||
35 | type PeerFilter struct { | ||
36 | BF *BloomFilter | ||
37 | } | ||
38 | |||
39 | // NewPeerFilter creates an empty peer filter instance. | ||
40 | func NewPeerFilter() *PeerFilter { | ||
41 | return &PeerFilter{ | ||
42 | BF: NewBloomFilter(128), | ||
43 | } | ||
44 | } | ||
45 | |||
46 | // Add peer id to the filter | ||
47 | func (pf *PeerFilter) Add(p *util.PeerID) { | ||
48 | pf.BF.Add(p.Data) | ||
49 | } | ||
50 | |||
51 | // Contains returns true if the peer id is filtered (in the filter) | ||
52 | func (pf *PeerFilter) Contains(p *util.PeerID) bool { | ||
53 | return pf.BF.Contains(p.Data) | ||
54 | } | ||
55 | |||
56 | // Cloone peer filter instance | ||
57 | func (pf *PeerFilter) Clone() *PeerFilter { | ||
58 | return &PeerFilter{ | ||
59 | BF: pf.BF.Clone(), | ||
60 | } | ||
61 | } | ||
62 | |||
63 | //====================================================================== | ||
64 | // Result filter | ||
65 | //====================================================================== | ||
66 | |||
67 | // ResultFilter return values | ||
68 | //nolint:stylecheck // allow non-camel-case in constants | ||
69 | const ( | ||
70 | RF_MORE = iota // Valid result, and there may be more. | ||
71 | RF_LAST // Last possible valid result. | ||
72 | RF_DUPLICATE // Valid result, but duplicate (was filtered by the result filter). | ||
73 | RF_IRRELEVANT // Block does not satisfy the constraints imposed by the XQuery. | ||
74 | ) | ||
75 | |||
76 | // Compare return values | ||
77 | //nolint:stylecheck // allow non-camel-case in constants | ||
78 | const ( | ||
79 | CMP_SAME = iota // the two result filter are the same | ||
80 | CMP_MERGE // the two result filter can be merged | ||
81 | CMP_DIFFER // the two result filter are different | ||
82 | CMP_1 // used as state by derived/complex compare functions | ||
83 | CMP_2 | ||
84 | CMP_3 | ||
85 | ) | ||
86 | |||
87 | //---------------------------------------------------------------------- | ||
88 | |||
89 | // ResultFilter is used to indicate to other peers which results are not of | ||
90 | // interest when processing a GetMessage. Any peer which is processing | ||
91 | // GetMessages and has a result which matches the query key MUST check the | ||
92 | // result filter and only send a reply message if the result does not test | ||
93 | // positive under the result filter. Before forwarding the GetMessage, the | ||
94 | // result filter MUST be updated to filter out all results already returned | ||
95 | // by the local peer. | ||
96 | type ResultFilter interface { | ||
97 | |||
98 | // Add entry to filter | ||
99 | Add(Block) | ||
100 | |||
101 | // Contains returns true if entry is filtered | ||
102 | Contains(Block) bool | ||
103 | |||
104 | // Bytes returns the binary representation of a result filter | ||
105 | Bytes() []byte | ||
106 | |||
107 | // Compare two result filters | ||
108 | Compare(ResultFilter) int | ||
109 | |||
110 | // Merge two result filters | ||
111 | Merge(ResultFilter) bool | ||
112 | } | ||
113 | |||
114 | //---------------------------------------------------------------------- | ||
115 | // Dummy result filter | ||
116 | // [Additional filters (per block type) are defined in corresponding files] | ||
117 | //---------------------------------------------------------------------- | ||
118 | |||
119 | // PassResultFilter is a dummy result filter with no state. | ||
120 | type PassResultFilter struct{} | ||
121 | |||
122 | // Add a block to the result filter. | ||
123 | func (rf *PassResultFilter) Add(Block) { | ||
124 | } | ||
125 | |||
126 | // Contains returns true if entry (binary representation) is filtered | ||
127 | func (rf *PassResultFilter) Contains(Block) bool { | ||
128 | return false | ||
129 | } | ||
130 | |||
131 | // Bytes returns the binary representation of a result filter | ||
132 | func (rf *PassResultFilter) Bytes() (buf []byte) { | ||
133 | return | ||
134 | } | ||
135 | |||
136 | // Merge two result filters | ||
137 | func (rf *PassResultFilter) Merge(ResultFilter) bool { | ||
138 | return true | ||
139 | } | ||
140 | |||
141 | // Compare two result filters | ||
142 | func (rf *PassResultFilter) Compare(t ResultFilter) int { | ||
143 | if _, ok := t.(*PassResultFilter); ok { | ||
144 | return CMP_SAME | ||
145 | } | ||
146 | return CMP_DIFFER | ||
147 | } | ||
148 | |||
149 | //====================================================================== | ||
150 | // Generic bllom filter with mutator | ||
151 | //====================================================================== | ||
152 | |||
153 | // BloomFilter is a space-efficient probabilistic datastructure to test if | ||
154 | // an element is part of a set of elementsis defined as a string of bits | ||
155 | // always initially empty. An optional mutator can be used to additionally | ||
156 | // "randomize" the computation of the bloomfilter while remaining deterministic. | ||
157 | type BloomFilter struct { | ||
158 | Bits []byte // filter bits | ||
159 | |||
160 | // transient attributes | ||
161 | mInput []byte // mutator input | ||
162 | mData []byte // mutator data | ||
163 | } | ||
164 | |||
165 | // NewBloomFilter creates a new empty filter of given size (8*n bits). | ||
166 | func NewBloomFilter(n int) *BloomFilter { | ||
167 | return &BloomFilter{ | ||
168 | Bits: make([]byte, n), | ||
169 | mInput: nil, | ||
170 | mData: nil, | ||
171 | } | ||
172 | } | ||
173 | |||
174 | // SetMutator to define a mutator for randomization. If 'm' is nil, | ||
175 | // the mutator is removed from the filter (use with care!) | ||
176 | func (bf *BloomFilter) SetMutator(m any) { | ||
177 | // handle mutator input | ||
178 | switch v := m.(type) { | ||
179 | case uint32: | ||
180 | buf := new(bytes.Buffer) | ||
181 | if err := binary.Write(buf, binary.BigEndian, v); err != nil { | ||
182 | logger.Printf(logger.ERROR, "[BloomFilter.SetMutator] failed: %s", err.Error()) | ||
183 | } | ||
184 | bf.mInput = buf.Bytes() | ||
185 | case []byte: | ||
186 | bf.mInput = make([]byte, 4) | ||
187 | util.CopyAlignedBlock(bf.mInput, v) | ||
188 | case nil: | ||
189 | bf.mInput = nil | ||
190 | bf.mData = nil | ||
191 | return | ||
192 | } | ||
193 | // generate mutator bytes | ||
194 | h := sha512.New() | ||
195 | if _, err := h.Write(bf.mInput); err != nil { | ||
196 | logger.Printf(logger.ERROR, "[BloomFilter.SetMutator] failed: %s", err.Error()) | ||
197 | } | ||
198 | bf.mData = h.Sum(nil) | ||
199 | |||
200 | //logger.Printf(logger.DBG, "[filter] Mutator %s -> %s", hex.EncodeToString(bf.mInput), hex.EncodeToString(bf.mData)) | ||
201 | } | ||
202 | |||
203 | // Mutator returns the mutator input as a 4-byte array | ||
204 | func (bf *BloomFilter) Mutator() []byte { | ||
205 | return bf.mInput | ||
206 | } | ||
207 | |||
208 | // Bytes returns the binary representation of a bloom filter | ||
209 | func (bf *BloomFilter) Bytes() []byte { | ||
210 | var buf []byte | ||
211 | if bf.mInput != nil { | ||
212 | buf = append(buf, bf.mInput...) | ||
213 | } | ||
214 | buf = append(buf, bf.Bits...) | ||
215 | return buf | ||
216 | } | ||
217 | |||
218 | // Compare two bloom filters | ||
219 | func (bf *BloomFilter) Compare(a *BloomFilter) int { | ||
220 | if len(bf.Bits) != len(a.Bits) || !bytes.Equal(bf.mInput, a.mInput) { | ||
221 | return CMP_DIFFER | ||
222 | } | ||
223 | if bytes.Equal(bf.Bits, a.Bits) { | ||
224 | return CMP_SAME | ||
225 | } | ||
226 | return CMP_MERGE | ||
227 | } | ||
228 | |||
229 | // Merge two bloom filters | ||
230 | func (bf *BloomFilter) Merge(a *BloomFilter) bool { | ||
231 | if len(bf.Bits) != len(a.Bits) || !bytes.Equal(bf.mInput, a.mInput) { | ||
232 | return false | ||
233 | } | ||
234 | for i := range bf.Bits { | ||
235 | bf.Bits[i] |= a.Bits[i] | ||
236 | } | ||
237 | return true | ||
238 | } | ||
239 | |||
240 | // Clone a bloom filter instance | ||
241 | func (bf *BloomFilter) Clone() *BloomFilter { | ||
242 | return &BloomFilter{ | ||
243 | Bits: util.Clone(bf.Bits), | ||
244 | mInput: util.Clone(bf.mInput), | ||
245 | mData: util.Clone(bf.mData), | ||
246 | } | ||
247 | } | ||
248 | |||
249 | // Add entry (binary representation): | ||
250 | // When adding an element to the Bloom filter bf using BF-SET(bf,e), each | ||
251 | // integer n of the mapping M(e) is interpreted as a bit offset n mod L | ||
252 | // within bf and set to 1. | ||
253 | func (bf *BloomFilter) Add(e []byte) { | ||
254 | for _, idx := range bf.indices(e) { | ||
255 | bf.Bits[idx/8] |= (1 << (idx % 8)) | ||
256 | } | ||
257 | } | ||
258 | |||
259 | // Contains returns true if the entry is most likely to be included: | ||
260 | // When testing if an element may be in the Bloom filter bf using | ||
261 | // BF-TEST(bf,e), each bit offset n mod L within bf MUST have been set to 1. | ||
262 | // Otherwise, the element is not considered to be in the Bloom filter. | ||
263 | func (bf *BloomFilter) Contains(e []byte) bool { | ||
264 | for _, idx := range bf.indices(e) { | ||
265 | if bf.Bits[idx/8]&(1<<(idx%8)) == 0 { | ||
266 | return false | ||
267 | } | ||
268 | } | ||
269 | return true | ||
270 | } | ||
271 | |||
272 | // indices returns the list of bit indices for antry e: | ||
273 | // The element e is hashed using SHA-512. If a mutator is present, the | ||
274 | // hash values are XOR-ed. The resulting value is interpreted as a list | ||
275 | // of 16 32-bit integers in network byte order. | ||
276 | func (bf *BloomFilter) indices(e []byte) []uint32 { | ||
277 | // hash the entry | ||
278 | h := sha512.Sum512(e) | ||
279 | // apply mutator if available | ||
280 | if bf.mData != nil { | ||
281 | for i := range h { | ||
282 | h[i] ^= bf.mData[i] | ||
283 | } | ||
284 | } | ||
285 | // compute the indices for the entry | ||
286 | size := uint32(8 * len(bf.Bits)) | ||
287 | idx := make([]uint32, 16) | ||
288 | buf := bytes.NewReader(h[:]) | ||
289 | for i := range idx { | ||
290 | if err := binary.Read(buf, binary.BigEndian, &idx[i]); err != nil { | ||
291 | logger.Printf(logger.ERROR, "[BloomFilter.indices] failed: %s", err.Error()) | ||
292 | } | ||
293 | idx[i] %= size | ||
294 | } | ||
295 | return idx | ||
296 | } | ||
diff --git a/src/gnunet/service/dht/blocks/filters_test.go b/src/gnunet/service/dht/blocks/filters_test.go new file mode 100644 index 0000000..ef1331c --- /dev/null +++ b/src/gnunet/service/dht/blocks/filters_test.go | |||
@@ -0,0 +1,110 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "crypto/rand" | ||
24 | "sort" | ||
25 | "testing" | ||
26 | ) | ||
27 | |||
28 | type Entry []byte | ||
29 | |||
30 | type EntryList []Entry | ||
31 | |||
32 | func (list EntryList) Len() int { return len(list) } | ||
33 | func (list EntryList) Swap(i, j int) { list[i], list[j] = list[j], list[i] } | ||
34 | func (list EntryList) Less(i, j int) bool { return bytes.Compare(list[i], list[j]) < 0 } | ||
35 | |||
36 | func (list EntryList) Contains(e Entry) bool { | ||
37 | size := len(list) | ||
38 | i := sort.Search(size, func(i int) bool { return bytes.Compare(list[i], e) >= 0 }) | ||
39 | return i != size | ||
40 | } | ||
41 | |||
42 | func TestBloomfilter(t *testing.T) { | ||
43 | F := 500 // number of expected entries | ||
44 | |||
45 | // The K-value for the HELLO_BF Bloom filter is always 16. The size S of | ||
46 | // the Bloom filter in bytes depends on the number of elements F known to | ||
47 | // be filtered at the initiator. If F is zero, the size S is just 8 (bytes). | ||
48 | // Otherwise, S is set to the minimum of 2^15 and the lowest power of 2 that | ||
49 | // is strictly larger than K*F/4 (in bytes). The wire format of HELLO_BF is | ||
50 | // the resulting byte array. In particular, K is never transmitted. | ||
51 | S := 1 | ||
52 | for S < 4*F && S < 32768 { | ||
53 | S <<= 1 | ||
54 | } | ||
55 | t.Logf("BloomFilter size in bytes: %d\n", S) | ||
56 | |||
57 | // generate positives (entries in the set) | ||
58 | positives := make(EntryList, F) | ||
59 | for i := 0; i < F; i++ { | ||
60 | data := make(Entry, 32) | ||
61 | if _, err := rand.Read(data); err != nil { | ||
62 | t.Fatal(err) | ||
63 | } | ||
64 | positives[i] = data | ||
65 | } | ||
66 | sort.Sort(positives) | ||
67 | |||
68 | // generate negatives (entries outside the set) | ||
69 | negatives := make(EntryList, F) | ||
70 | for i := 0; i < F; { | ||
71 | data := make(Entry, 32) | ||
72 | if _, err := rand.Read(data); err != nil { | ||
73 | t.Fatal(err) | ||
74 | } | ||
75 | if !positives.Contains(data) { | ||
76 | negatives[i] = data | ||
77 | i++ | ||
78 | } | ||
79 | } | ||
80 | |||
81 | // create BloomFilter | ||
82 | bf := NewBloomFilter(S) | ||
83 | |||
84 | // add positives to bloomfilter | ||
85 | for _, e := range positives { | ||
86 | bf.Add(e) | ||
87 | } | ||
88 | |||
89 | // check lookup of positives | ||
90 | count := 0 | ||
91 | for _, e := range positives { | ||
92 | if !bf.Contains(e) { | ||
93 | count++ | ||
94 | } | ||
95 | } | ||
96 | if count > 0 { | ||
97 | t.Logf("FAILED with %d false-negatives", count) | ||
98 | } | ||
99 | |||
100 | // check lookup of negatives | ||
101 | count = 0 | ||
102 | for _, e := range negatives { | ||
103 | if bf.Contains(e) { | ||
104 | count++ | ||
105 | } | ||
106 | } | ||
107 | if count > 0 { | ||
108 | t.Logf("FAILED with %d false-positives", count) | ||
109 | } | ||
110 | } | ||
diff --git a/src/gnunet/service/dht/blocks/generic.go b/src/gnunet/service/dht/blocks/generic.go index 6301e3b..2962025 100644 --- a/src/gnunet/service/dht/blocks/generic.go +++ b/src/gnunet/service/dht/blocks/generic.go | |||
@@ -19,11 +19,10 @@ | |||
19 | package blocks | 19 | package blocks |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "bytes" | ||
23 | "encoding/gob" | ||
24 | "encoding/hex" | 22 | "encoding/hex" |
25 | "fmt" | 23 | "fmt" |
26 | "gnunet/crypto" | 24 | "gnunet/crypto" |
25 | "gnunet/enums" | ||
27 | "gnunet/util" | 26 | "gnunet/util" |
28 | 27 | ||
29 | "github.com/bfix/gospel/data" | 28 | "github.com/bfix/gospel/data" |
@@ -39,13 +38,11 @@ type Query interface { | |||
39 | // Key returns the DHT key for a block | 38 | // Key returns the DHT key for a block |
40 | Key() *crypto.HashCode | 39 | Key() *crypto.HashCode |
41 | 40 | ||
42 | // Get retrieves the value of a named query parameter. The value is | 41 | // Type returns the requested block type |
43 | // unchanged if the key is not in the map or if the value in the map | 42 | Type() uint16 |
44 | // has an incompatible type. | ||
45 | Get(key string, value any) bool | ||
46 | 43 | ||
47 | // Set stores the value of a named query parameter | 44 | // Flags returns the query flags |
48 | Set(key string, value any) | 45 | Flags() uint16 |
49 | 46 | ||
50 | // Verify the integrity of a retrieved block (optional). Override in | 47 | // Verify the integrity of a retrieved block (optional). Override in |
51 | // custom query types to implement block-specific integrity checks | 48 | // custom query types to implement block-specific integrity checks |
@@ -77,7 +74,7 @@ type Block interface { | |||
77 | // types to implement block-specific integrity checks (see GNSBlock for | 74 | // types to implement block-specific integrity checks (see GNSBlock for |
78 | // example). This verification is usually weaker than the verification | 75 | // example). This verification is usually weaker than the verification |
79 | // method from a Query (see GNSBlock.Verify for explanation). | 76 | // method from a Query (see GNSBlock.Verify for explanation). |
80 | Verify() error | 77 | Verify() (bool, error) |
81 | 78 | ||
82 | // String returns the human-readable representation of a block | 79 | // String returns the human-readable representation of a block |
83 | String() string | 80 | String() string |
@@ -97,8 +94,14 @@ type GenericQuery struct { | |||
97 | // Key for repository queries (local/remote) | 94 | // Key for repository queries (local/remote) |
98 | key *crypto.HashCode | 95 | key *crypto.HashCode |
99 | 96 | ||
100 | // query parameters (binary value representation) | 97 | // block type requested |
101 | params map[string][]byte | 98 | btype uint16 |
99 | |||
100 | // query flags | ||
101 | flags uint16 | ||
102 | |||
103 | // Params holds additional query parameters | ||
104 | Params util.ParameterSet | ||
102 | } | 105 | } |
103 | 106 | ||
104 | // Key interface method implementation | 107 | // Key interface method implementation |
@@ -106,23 +109,14 @@ func (q *GenericQuery) Key() *crypto.HashCode { | |||
106 | return q.key | 109 | return q.key |
107 | } | 110 | } |
108 | 111 | ||
109 | // Get retrieves the value of a named query parameter | 112 | // Type returns the requested block type |
110 | func (q *GenericQuery) Get(key string, value any) bool { | 113 | func (q *GenericQuery) Type() uint16 { |
111 | data, ok := q.params[key] | 114 | return q.btype |
112 | if !ok { | ||
113 | return false | ||
114 | } | ||
115 | dec := gob.NewDecoder(bytes.NewReader(data)) | ||
116 | return dec.Decode(value) != nil | ||
117 | } | 115 | } |
118 | 116 | ||
119 | // Set stores the value of a named query parameter | 117 | // Flags returns the query flags |
120 | func (q *GenericQuery) Set(key string, value any) { | 118 | func (q *GenericQuery) Flags() uint16 { |
121 | wrt := new(bytes.Buffer) | 119 | return q.flags |
122 | enc := gob.NewEncoder(wrt) | ||
123 | if enc.Encode(value) == nil { | ||
124 | q.params[key] = wrt.Bytes() | ||
125 | } | ||
126 | } | 120 | } |
127 | 121 | ||
128 | // Verify interface method implementation | 122 | // Verify interface method implementation |
@@ -139,14 +133,16 @@ func (q *GenericQuery) Decrypt(b Block) error { | |||
139 | 133 | ||
140 | // String returns the human-readable representation of a block | 134 | // String returns the human-readable representation of a block |
141 | func (q *GenericQuery) String() string { | 135 | func (q *GenericQuery) String() string { |
142 | return fmt.Sprintf("GenericQuery{key=%s}", hex.EncodeToString(q.Key().Bits)) | 136 | return fmt.Sprintf("GenericQuery{btype=%d,key=%s}", q.btype, hex.EncodeToString(q.Key().Bits)) |
143 | } | 137 | } |
144 | 138 | ||
145 | // NewGenericQuery creates a simple Query from hash code. | 139 | // NewGenericQuery creates a simple Query from hash code. |
146 | func NewGenericQuery(buf []byte) *GenericQuery { | 140 | func NewGenericQuery(key []byte, btype enums.BlockType, flags uint16) *GenericQuery { |
147 | return &GenericQuery{ | 141 | return &GenericQuery{ |
148 | key: crypto.NewHashCode(buf), | 142 | key: crypto.NewHashCode(key), |
149 | params: make(map[string][]byte), | 143 | btype: uint16(btype), |
144 | flags: flags, | ||
145 | Params: make(util.ParameterSet), | ||
150 | } | 146 | } |
151 | } | 147 | } |
152 | 148 | ||
@@ -181,16 +177,16 @@ func (b *GenericBlock) String() string { | |||
181 | } | 177 | } |
182 | 178 | ||
183 | // Verify interface method implementation | 179 | // Verify interface method implementation |
184 | func (b *GenericBlock) Verify() error { | 180 | func (b *GenericBlock) Verify() (bool, error) { |
185 | // no verification, no errors ;) | 181 | // no verification, no errors ;) |
186 | return nil | 182 | return true, nil |
187 | } | 183 | } |
188 | 184 | ||
189 | // NewGenericBlock creates a Block from binary data. | 185 | // NewGenericBlock creates a Block from binary data. |
190 | func NewGenericBlock(buf []byte) *GenericBlock { | 186 | func NewGenericBlock(buf []byte) *GenericBlock { |
191 | return &GenericBlock{ | 187 | return &GenericBlock{ |
192 | block: util.Clone(buf), | 188 | block: util.Clone(buf), |
193 | btype: DHT_BLOCK_ANY, // unknown block type | 189 | btype: uint16(enums.BLOCK_TYPE_ANY), // unknown block type |
194 | expire: util.AbsoluteTimeNever(), // never expires | 190 | expire: util.AbsoluteTimeNever(), // never expires |
195 | } | 191 | } |
196 | } | 192 | } |
diff --git a/src/gnunet/service/dht/blocks/generic_test.go b/src/gnunet/service/dht/blocks/generic_test.go deleted file mode 100644 index 51ee5a1..0000000 --- a/src/gnunet/service/dht/blocks/generic_test.go +++ /dev/null | |||
@@ -1,67 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "testing" | ||
24 | ) | ||
25 | |||
26 | // Test parameter handling for queries | ||
27 | func TestQueryParams(t *testing.T) { | ||
28 | q := NewGenericQuery(nil) | ||
29 | |||
30 | // set parameters | ||
31 | var ( | ||
32 | btype uint16 = DHT_BLOCK_ANY | ||
33 | flags uint32 = 0 | ||
34 | name string = "Test" | ||
35 | data = make([]byte, 8) | ||
36 | ) | ||
37 | q.Set("btype", btype) | ||
38 | q.Set("flags", flags) | ||
39 | q.Set("name", name) | ||
40 | q.Set("data", data) | ||
41 | |||
42 | // get parameters | ||
43 | var ( | ||
44 | t_btype uint16 | ||
45 | t_flags uint32 | ||
46 | t_name string | ||
47 | t_data []byte | ||
48 | ) | ||
49 | q.Get("btype", &t_btype) | ||
50 | q.Get("flags", &t_flags) | ||
51 | q.Get("name", &t_name) | ||
52 | q.Get("data", &t_data) | ||
53 | |||
54 | // check for unchanged data | ||
55 | if btype != t_btype { | ||
56 | t.Fatal("btype mismatch") | ||
57 | } | ||
58 | if flags != t_flags { | ||
59 | t.Fatal("flags mismatch") | ||
60 | } | ||
61 | if name != t_name { | ||
62 | t.Fatal("name mismatch") | ||
63 | } | ||
64 | if !bytes.Equal(data, t_data) { | ||
65 | t.Fatal("data mismatch") | ||
66 | } | ||
67 | } | ||
diff --git a/src/gnunet/service/dht/blocks/gns.go b/src/gnunet/service/dht/blocks/gns.go index 2085677..0225003 100644 --- a/src/gnunet/service/dht/blocks/gns.go +++ b/src/gnunet/service/dht/blocks/gns.go | |||
@@ -22,14 +22,19 @@ import ( | |||
22 | "errors" | 22 | "errors" |
23 | "fmt" | 23 | "fmt" |
24 | "gnunet/crypto" | 24 | "gnunet/crypto" |
25 | "gnunet/enums" | ||
25 | "gnunet/util" | 26 | "gnunet/util" |
26 | 27 | ||
27 | "github.com/bfix/gospel/data" | 28 | "github.com/bfix/gospel/data" |
29 | "github.com/bfix/gospel/logger" | ||
28 | ) | 30 | ) |
29 | 31 | ||
30 | // Error messages | 32 | // Error messages |
31 | var ( | 33 | var ( |
32 | ErrBlockNotDecrypted = fmt.Errorf("GNS block not decrypted") | 34 | ErrBlockNotDecrypted = errors.New("GNS block not decrypted") |
35 | ErrBlockInvalidSig = errors.New("invalid signature key for GNS Block") | ||
36 | ErrBlockTypeNotVerified = errors.New("can't verify block type") | ||
37 | ErrBlockCantDecrypt = errors.New("can't decrypt block type") | ||
33 | ) | 38 | ) |
34 | 39 | ||
35 | //---------------------------------------------------------------------- | 40 | //---------------------------------------------------------------------- |
@@ -54,9 +59,13 @@ func (q *GNSQuery) Verify(b Block) (err error) { | |||
54 | 59 | ||
55 | // verify derived key | 60 | // verify derived key |
56 | dkey := blk.DerivedKeySig.ZoneKey | 61 | dkey := blk.DerivedKeySig.ZoneKey |
57 | dkey2, _ := q.Zone.Derive(q.Label, "gns") | 62 | var dkey2 *crypto.ZoneKey |
63 | if dkey2, _, err = q.Zone.Derive(q.Label, "gns"); err != nil { | ||
64 | return | ||
65 | } | ||
58 | if !dkey.Equal(dkey2) { | 66 | if !dkey.Equal(dkey2) { |
59 | return fmt.Errorf("invalid signature key for GNS Block") | 67 | err = ErrBlockInvalidSig |
68 | return | ||
60 | } | 69 | } |
61 | // verify signature | 70 | // verify signature |
62 | var buf []byte | 71 | var buf []byte |
@@ -66,7 +75,7 @@ func (q *GNSQuery) Verify(b Block) (err error) { | |||
66 | blk.verified, err = blk.DerivedKeySig.Verify(buf) | 75 | blk.verified, err = blk.DerivedKeySig.Verify(buf) |
67 | 76 | ||
68 | default: | 77 | default: |
69 | err = errors.New("can't verify block type") | 78 | err = ErrBlockTypeNotVerified |
70 | } | 79 | } |
71 | return | 80 | return |
72 | } | 81 | } |
@@ -81,7 +90,7 @@ func (q *GNSQuery) Decrypt(b Block) (err error) { | |||
81 | return | 90 | return |
82 | 91 | ||
83 | default: | 92 | default: |
84 | err = errors.New("can't decrypt block type") | 93 | err = ErrBlockCantDecrypt |
85 | } | 94 | } |
86 | return | 95 | return |
87 | } | 96 | } |
@@ -91,10 +100,13 @@ func NewGNSQuery(zkey *crypto.ZoneKey, label string) *GNSQuery { | |||
91 | // derive a public key from (pkey,label) and set the repository | 100 | // derive a public key from (pkey,label) and set the repository |
92 | // key as the SHA512 hash of the binary key representation. | 101 | // key as the SHA512 hash of the binary key representation. |
93 | // (key blinding) | 102 | // (key blinding) |
94 | pd, _ := zkey.Derive(label, "gns") | 103 | pd, _, err := zkey.Derive(label, "gns") |
104 | if err != nil { | ||
105 | logger.Printf(logger.ERROR, "[NewGNSQuery] failed: %s", err.Error()) | ||
106 | } | ||
95 | gq := crypto.Hash(pd.Bytes()).Bits | 107 | gq := crypto.Hash(pd.Bytes()).Bits |
96 | return &GNSQuery{ | 108 | return &GNSQuery{ |
97 | GenericQuery: *NewGenericQuery(gq), | 109 | GenericQuery: *NewGenericQuery(gq, enums.BLOCK_TYPE_GNS_NAMERECORD, 0), |
98 | Zone: zkey, | 110 | Zone: zkey, |
99 | Label: label, | 111 | Label: label, |
100 | derived: pd, | 112 | derived: pd, |
@@ -161,12 +173,11 @@ func NewBlock() *GNSBlock { | |||
161 | // Only the cryptographic signature is verified; the formal correctness of | 173 | // Only the cryptographic signature is verified; the formal correctness of |
162 | // the association between the block and a GNS label in a GNS zone can't | 174 | // the association between the block and a GNS label in a GNS zone can't |
163 | // be verified. This is only possible in Query.Verify(). | 175 | // be verified. This is only possible in Query.Verify(). |
164 | func (b *GNSBlock) Verify() (err error) { | 176 | func (b *GNSBlock) Verify() (ok bool, err error) { |
165 | // verify signature | 177 | // verify signature |
166 | var buf []byte | 178 | var buf []byte |
167 | if buf, err = data.Marshal(b.Body); err != nil { | 179 | if buf, err = data.Marshal(b.Body); err != nil { |
168 | return | 180 | return |
169 | } | 181 | } |
170 | _, err = b.DerivedKeySig.Verify(buf) | 182 | return b.DerivedKeySig.Verify(buf) |
171 | return | ||
172 | } | 183 | } |
diff --git a/src/gnunet/service/dht/blocks/handlers.go b/src/gnunet/service/dht/blocks/handlers.go new file mode 100644 index 0000000..9df3867 --- /dev/null +++ b/src/gnunet/service/dht/blocks/handlers.go | |||
@@ -0,0 +1,80 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | import ( | ||
22 | "gnunet/crypto" | ||
23 | "gnunet/enums" | ||
24 | ) | ||
25 | |||
26 | // BlockHandler interface defines methods specific to block types. | ||
27 | type BlockHandler interface { | ||
28 | |||
29 | // Parse a block instance from binary data | ||
30 | ParseBlock(buf []byte) (Block, error) | ||
31 | |||
32 | // ValidateBlockQuery is used to evaluate the request for a block as part of | ||
33 | // DHT-P2P-GET processing. Here, the block payload is unknown, but if possible | ||
34 | // the XQuery and Key SHOULD be verified. | ||
35 | ValidateBlockQuery(key *crypto.HashCode, xquery []byte) bool | ||
36 | |||
37 | // ValidateBlockKey returns true if the block key is the same as the | ||
38 | // query key used to access the block. | ||
39 | ValidateBlockKey(b Block, key *crypto.HashCode) bool | ||
40 | |||
41 | // ValidateBlockStoreRequest is used to evaluate a block payload as part of | ||
42 | // PutMessage and ResultMessage processing. | ||
43 | ValidateBlockStoreRequest(b Block) bool | ||
44 | |||
45 | // SetupResultFilter is used to setup an empty result filter. The arguments | ||
46 | // are the set of results that must be filtered at the initiator, and a | ||
47 | // MUTATOR value which MAY be used to deterministically re-randomize | ||
48 | // probabilistic data structures. | ||
49 | SetupResultFilter(filterSize int, mutator uint32) ResultFilter | ||
50 | |||
51 | // ParseResultFilter from binary data | ||
52 | ParseResultFilter(data []byte) ResultFilter | ||
53 | |||
54 | // FilterResult is used to filter results against specific queries. This | ||
55 | // function does not check the validity of the block itself or that it | ||
56 | // matches the given key, as this must have been checked earlier. Thus, | ||
57 | // locally stored blocks from previously observed ResultMessages and | ||
58 | // PutMessages use this function to perform filtering based on the request | ||
59 | // parameters of a particular GET operation. Possible values for the | ||
60 | // FilterEvaluationResult are defined above. If the main evaluation result | ||
61 | // is RF_MORE, the function also returns and updated result filter where | ||
62 | // the block is added to the set of filtered replies. An implementation is | ||
63 | // not expected to actually differentiate between the RF_DUPLICATE and | ||
64 | // RF_IRRELEVANT return values: in both cases the block is ignored for | ||
65 | // this query. | ||
66 | FilterResult(b Block, key *crypto.HashCode, rf ResultFilter, xQuery []byte) int | ||
67 | } | ||
68 | |||
69 | // BlockHandlers is a map of block query validation implementations | ||
70 | // for supported block types. | ||
71 | var BlockHandlers map[enums.BlockType]BlockHandler | ||
72 | |||
73 | // initializer function | ||
74 | func init() { | ||
75 | // create map instance | ||
76 | BlockHandlers = make(map[enums.BlockType]BlockHandler) | ||
77 | |||
78 | // add validation functions | ||
79 | BlockHandlers[enums.BLOCK_TYPE_DHT_URL_HELLO] = new(HelloBlockHandler) | ||
80 | } | ||
diff --git a/src/gnunet/service/dht/blocks/hello.go b/src/gnunet/service/dht/blocks/hello.go index a5ccf8c..c884fd2 100644 --- a/src/gnunet/service/dht/blocks/hello.go +++ b/src/gnunet/service/dht/blocks/hello.go | |||
@@ -24,6 +24,7 @@ import ( | |||
24 | "encoding/binary" | 24 | "encoding/binary" |
25 | "errors" | 25 | "errors" |
26 | "fmt" | 26 | "fmt" |
27 | "gnunet/crypto" | ||
27 | "gnunet/enums" | 28 | "gnunet/enums" |
28 | "gnunet/util" | 29 | "gnunet/util" |
29 | "net/url" | 30 | "net/url" |
@@ -32,6 +33,7 @@ import ( | |||
32 | 33 | ||
33 | "github.com/bfix/gospel/crypto/ed25519" | 34 | "github.com/bfix/gospel/crypto/ed25519" |
34 | "github.com/bfix/gospel/data" | 35 | "github.com/bfix/gospel/data" |
36 | "github.com/bfix/gospel/logger" | ||
35 | ) | 37 | ) |
36 | 38 | ||
37 | // HELLO-related errors | 39 | // HELLO-related errors |
@@ -50,12 +52,12 @@ const helloPrefix = "gnunet://hello/" | |||
50 | // HelloBlock is the DHT-managed block type for HELLO information. | 52 | // HelloBlock is the DHT-managed block type for HELLO information. |
51 | // It is used to create and parse HELLO URLs. | 53 | // It is used to create and parse HELLO URLs. |
52 | // All addresses expire at the same time /this different from HELLO | 54 | // All addresses expire at the same time /this different from HELLO |
53 | // messages (see message.HeeloMsg). | 55 | // messages (see message.HelloMsg). |
54 | type HelloBlock struct { | 56 | type HelloBlock struct { |
55 | PeerID *util.PeerID `` // peer identifier | 57 | PeerID *util.PeerID `` // peer identifier |
56 | Signature []byte `size:"64"` // signature | 58 | Signature *util.PeerSignature `` // signature |
57 | Expire util.AbsoluteTime `` // Expiration date | 59 | Expires util.AbsoluteTime `` // Expiration date |
58 | AddrBin []byte `size:"*"` // raw address data | 60 | AddrBin []byte `size:"*"` // raw address data |
59 | 61 | ||
60 | // transient attributes | 62 | // transient attributes |
61 | addrs []*util.Address // cooked address data | 63 | addrs []*util.Address // cooked address data |
@@ -64,7 +66,9 @@ type HelloBlock struct { | |||
64 | // SetAddresses adds a bulk of addresses for this HELLO block. | 66 | // SetAddresses adds a bulk of addresses for this HELLO block. |
65 | func (h *HelloBlock) SetAddresses(a []*util.Address) { | 67 | func (h *HelloBlock) SetAddresses(a []*util.Address) { |
66 | h.addrs = util.Clone(a) | 68 | h.addrs = util.Clone(a) |
67 | h.finalize() | 69 | if err := h.finalize(); err != nil { |
70 | logger.Printf(logger.ERROR, "[HelloBlock.SetAddresses] failed: %s", err.Error()) | ||
71 | } | ||
68 | } | 72 | } |
69 | 73 | ||
70 | // Addresses returns the list of addresses | 74 | // Addresses returns the list of addresses |
@@ -101,9 +105,10 @@ func ParseHelloURL(u string, checkExpiry bool) (h *HelloBlock, err error) { | |||
101 | h.PeerID = util.NewPeerID(buf) | 105 | h.PeerID = util.NewPeerID(buf) |
102 | 106 | ||
103 | // (2) parse signature | 107 | // (2) parse signature |
104 | if h.Signature, err = util.DecodeStringToBinary(p[1], 64); err != nil { | 108 | if buf, err = util.DecodeStringToBinary(p[1], 64); err != nil { |
105 | return | 109 | return |
106 | } | 110 | } |
111 | h.Signature = util.NewPeerSignature(buf) | ||
107 | 112 | ||
108 | // (3) split last element into parts | 113 | // (3) split last element into parts |
109 | q := strings.SplitN(p[2], "?", 2) | 114 | q := strings.SplitN(p[2], "?", 2) |
@@ -113,8 +118,8 @@ func ParseHelloURL(u string, checkExpiry bool) (h *HelloBlock, err error) { | |||
113 | if exp, err = strconv.ParseUint(q[0], 10, 64); err != nil { | 118 | if exp, err = strconv.ParseUint(q[0], 10, 64); err != nil { |
114 | return | 119 | return |
115 | } | 120 | } |
116 | h.Expire = util.NewAbsoluteTimeEpoch(exp) | 121 | h.Expires = util.NewAbsoluteTimeEpoch(exp) |
117 | if checkExpiry && h.Expire.Expired() { | 122 | if checkExpiry && h.Expires.Expired() { |
118 | err = ErrHelloExpired | 123 | err = ErrHelloExpired |
119 | return | 124 | return |
120 | } | 125 | } |
@@ -138,7 +143,9 @@ func ParseHelloURL(u string, checkExpiry bool) (h *HelloBlock, err error) { | |||
138 | } | 143 | } |
139 | 144 | ||
140 | // (6) generate raw address data so block is complete | 145 | // (6) generate raw address data so block is complete |
141 | h.finalize() | 146 | if err = h.finalize(); err != nil { |
147 | return | ||
148 | } | ||
142 | 149 | ||
143 | // check signature | 150 | // check signature |
144 | var ok bool | 151 | var ok bool |
@@ -190,24 +197,39 @@ func (h *HelloBlock) finalize() (err error) { | |||
190 | return | 197 | return |
191 | } | 198 | } |
192 | 199 | ||
193 | /* | 200 | // Return the block type |
194 | // Message returns the corresponding HELLO message to be sent to peers. | 201 | func (h *HelloBlock) Type() uint16 { |
195 | func (h *HelloBlock) Message() *message.HelloMsg { | 202 | return uint16(enums.BLOCK_TYPE_DHT_URL_HELLO) |
196 | msg := message.NewHelloMsg(h.PeerID) | 203 | } |
197 | for _, a := range h.addrs { | 204 | |
198 | msg.AddAddress(message.NewHelloAddress(a, h.Expire)) | 205 | // Data returns the raw block data |
206 | func (h *HelloBlock) Data() []byte { | ||
207 | buf, err := data.Marshal(h) | ||
208 | if err != nil { | ||
209 | logger.Println(logger.ERROR, "[hello] Failed to serialize HELLO block: "+err.Error()) | ||
210 | buf = nil | ||
199 | } | 211 | } |
200 | return msg | 212 | return buf |
213 | } | ||
214 | |||
215 | // Expire returns the block expiration | ||
216 | func (h *HelloBlock) Expire() util.AbsoluteTime { | ||
217 | return h.Expires | ||
218 | } | ||
219 | |||
220 | // String returns the human-readable representation of a block | ||
221 | func (h *HelloBlock) String() string { | ||
222 | return fmt.Sprintf("HelloBlock{peer=%s,expires=%s,addrs=[%d]}", | ||
223 | h.PeerID, h.Expires, len(h.Addresses())) | ||
201 | } | 224 | } |
202 | */ | ||
203 | 225 | ||
204 | // URL returns the HELLO URL for the data. | 226 | // URL returns the HELLO URL for the data. |
205 | func (h *HelloBlock) URL() string { | 227 | func (h *HelloBlock) URL() string { |
206 | u := fmt.Sprintf("%s%s/%s/%d?", | 228 | u := fmt.Sprintf("%s%s/%s/%d?", |
207 | helloPrefix, | 229 | helloPrefix, |
208 | h.PeerID.String(), | 230 | h.PeerID.String(), |
209 | util.EncodeBinaryToString(h.Signature), | 231 | util.EncodeBinaryToString(h.Signature.Data), |
210 | h.Expire.Epoch(), | 232 | h.Expires.Epoch(), |
211 | ) | 233 | ) |
212 | for i, a := range h.addrs { | 234 | for i, a := range h.addrs { |
213 | if i > 0 { | 235 | if i > 0 { |
@@ -221,10 +243,10 @@ func (h *HelloBlock) URL() string { | |||
221 | } | 243 | } |
222 | 244 | ||
223 | // Equals returns true if two HELLOs are the same. The expiration | 245 | // Equals returns true if two HELLOs are the same. The expiration |
224 | // timestamp is ignored in the comparision. | 246 | // timestamp is ignored in the comparison. |
225 | func (h *HelloBlock) Equals(g *HelloBlock) bool { | 247 | func (h *HelloBlock) Equals(g *HelloBlock) bool { |
226 | if !h.PeerID.Equals(g.PeerID) || | 248 | if !h.PeerID.Equals(g.PeerID) || |
227 | !util.Equals(h.Signature, g.Signature) || | 249 | !util.Equals(h.Signature.Data, g.Signature.Data) || |
228 | len(h.addrs) != len(g.addrs) { | 250 | len(h.addrs) != len(g.addrs) { |
229 | return false | 251 | return false |
230 | } | 252 | } |
@@ -239,29 +261,23 @@ func (h *HelloBlock) Equals(g *HelloBlock) bool { | |||
239 | // Verify the integrity of the HELLO data | 261 | // Verify the integrity of the HELLO data |
240 | func (h *HelloBlock) Verify() (bool, error) { | 262 | func (h *HelloBlock) Verify() (bool, error) { |
241 | // assemble signed data and public key | 263 | // assemble signed data and public key |
242 | sd := h.signedData() | 264 | sd := h.SignedData() |
243 | pub := h.PeerID.PublicKey() | 265 | pub := ed25519.NewPublicKeyFromBytes(h.PeerID.Data) |
244 | sig, err := ed25519.NewEdSignatureFromBytes(h.Signature) | 266 | sig, err := ed25519.NewEdSignatureFromBytes(h.Signature.Data) |
245 | if err != nil { | 267 | if err != nil { |
246 | return false, err | 268 | return false, err |
247 | } | 269 | } |
248 | return pub.EdVerify(sd, sig) | 270 | return pub.EdVerify(sd, sig) |
249 | } | 271 | } |
250 | 272 | ||
251 | // Sign the HELLO data with private key | 273 | // SetSignature stores a signature in the the HELLO block |
252 | func (h *HelloBlock) Sign(prv *ed25519.PrivateKey) error { | 274 | func (h *HelloBlock) SetSignature(sig *util.PeerSignature) error { |
253 | // assemble signed data | 275 | h.Signature = sig |
254 | sd := h.signedData() | ||
255 | sig, err := prv.EdSign(sd) | ||
256 | if err != nil { | ||
257 | return err | ||
258 | } | ||
259 | h.Signature = sig.Bytes() | ||
260 | return nil | 276 | return nil |
261 | } | 277 | } |
262 | 278 | ||
263 | // signedData assembles a data block for sign and verify operations. | 279 | // SignedData assembles a data block for sign and verify operations. |
264 | func (h *HelloBlock) signedData() []byte { | 280 | func (h *HelloBlock) SignedData() []byte { |
265 | // hash address block | 281 | // hash address block |
266 | hAddr := sha512.Sum512(h.AddrBin) | 282 | hAddr := sha512.Sum512(h.AddrBin) |
267 | var size uint32 = 80 | 283 | var size uint32 = 80 |
@@ -269,9 +285,166 @@ func (h *HelloBlock) signedData() []byte { | |||
269 | 285 | ||
270 | // assemble signed data | 286 | // assemble signed data |
271 | buf := new(bytes.Buffer) | 287 | buf := new(bytes.Buffer) |
272 | binary.Write(buf, binary.BigEndian, size) | 288 | var n int |
273 | binary.Write(buf, binary.BigEndian, purpose) | 289 | err := binary.Write(buf, binary.BigEndian, size) |
274 | binary.Write(buf, binary.BigEndian, h.Expire.Epoch()*1000000) | 290 | if err == nil { |
275 | buf.Write(hAddr[:]) | 291 | if err = binary.Write(buf, binary.BigEndian, purpose); err == nil { |
292 | if err = binary.Write(buf, binary.BigEndian, h.Expires.Epoch()*1000000); err == nil { | ||
293 | if n, err = buf.Write(hAddr[:]); err == nil { | ||
294 | if n != len(hAddr[:]) { | ||
295 | err = errors.New("signed data size mismatch") | ||
296 | } | ||
297 | } | ||
298 | } | ||
299 | } | ||
300 | } | ||
301 | if err != nil { | ||
302 | logger.Printf(logger.ERROR, "[HelloBlock.SignedData] failed: %s", err.Error()) | ||
303 | } | ||
276 | return buf.Bytes() | 304 | return buf.Bytes() |
277 | } | 305 | } |
306 | |||
307 | //---------------------------------------------------------------------- | ||
308 | // HELLO block handler | ||
309 | //---------------------------------------------------------------------- | ||
310 | |||
311 | // HelloBlockHandler methods related to HELLO blocks | ||
312 | type HelloBlockHandler struct{} | ||
313 | |||
314 | // Parse a block instance from binary data | ||
315 | func (bh *HelloBlockHandler) ParseBlock(buf []byte) (Block, error) { | ||
316 | return ParseHelloFromBytes(buf) | ||
317 | } | ||
318 | |||
319 | // ValidateHelloBlockQuery validates query parameters for a | ||
320 | // DHT-GET request for HELLO blocks. | ||
321 | func (bh *HelloBlockHandler) ValidateBlockQuery(key *crypto.HashCode, xquery []byte) bool { | ||
322 | // no xquery parameters allowed. | ||
323 | return len(xquery) == 0 | ||
324 | } | ||
325 | |||
326 | // ValidateBlockKey returns true if the block key is the same as the | ||
327 | // query key used to access the block. | ||
328 | func (bh *HelloBlockHandler) ValidateBlockKey(b Block, key *crypto.HashCode) bool { | ||
329 | hb, ok := b.(*HelloBlock) | ||
330 | if !ok { | ||
331 | return false | ||
332 | } | ||
333 | // key must be the hash of the peer id | ||
334 | bkey := crypto.Hash(hb.PeerID.Bytes()) | ||
335 | return key.Equals(bkey) | ||
336 | } | ||
337 | |||
338 | // ValidateBlockStoreRequest is used to evaluate a block payload as part of | ||
339 | // PutMessage and ResultMessage processing. | ||
340 | func (bh *HelloBlockHandler) ValidateBlockStoreRequest(b Block) bool { | ||
341 | // TODO: verify block payload | ||
342 | return true | ||
343 | } | ||
344 | |||
345 | // SetupResultFilter is used to setup an empty result filter. The arguments | ||
346 | // are the set of results that must be filtered at the initiator, and a | ||
347 | // MUTATOR value which MAY be used to deterministically re-randomize | ||
348 | // probabilistic data structures. | ||
349 | func (bh *HelloBlockHandler) SetupResultFilter(filterSize int, mutator uint32) ResultFilter { | ||
350 | return NewHelloResultFilter(filterSize, mutator) | ||
351 | } | ||
352 | |||
353 | // ParseResultFilter from binary data | ||
354 | func (bh *HelloBlockHandler) ParseResultFilter(data []byte) ResultFilter { | ||
355 | return NewHelloResultFilterFromBytes(data) | ||
356 | } | ||
357 | |||
358 | // FilterResult is used to filter results against specific queries. This | ||
359 | // function does not check the validity of the block itself or that it | ||
360 | // matches the given key, as this must have been checked earlier. Thus, | ||
361 | // locally stored blocks from previously observed ResultMessages and | ||
362 | // PutMessages use this function to perform filtering based on the request | ||
363 | // parameters of a particular GET operation. Possible values for the | ||
364 | // FilterEvaluationResult are defined above. If the main evaluation result | ||
365 | // is RF_MORE, the function also returns and updated result filter where | ||
366 | // the block is added to the set of filtered replies. An implementation is | ||
367 | // not expected to actually differentiate between the RF_DUPLICATE and | ||
368 | // RF_IRRELEVANT return values: in both cases the block is ignored for | ||
369 | // this query. | ||
370 | func (bh *HelloBlockHandler) FilterResult(b Block, key *crypto.HashCode, rf ResultFilter, xQuery []byte) int { | ||
371 | if rf.Contains(b) { | ||
372 | return RF_DUPLICATE | ||
373 | } | ||
374 | rf.Add(b) | ||
375 | return RF_LAST | ||
376 | } | ||
377 | |||
378 | //---------------------------------------------------------------------- | ||
379 | |||
380 | // HelloResultFilter is a result filter implementation for HELLO blocks | ||
381 | type HelloResultFilter struct { | ||
382 | bf *BloomFilter | ||
383 | } | ||
384 | |||
385 | // NewHelloResultFilter initializes an empty resut filter | ||
386 | func NewHelloResultFilter(filterSize int, mutator uint32) *HelloResultFilter { | ||
387 | // HELLO result filters are BloomFilters with a mutator | ||
388 | rf := new(HelloResultFilter) | ||
389 | rf.bf = NewBloomFilter(filterSize) | ||
390 | rf.bf.SetMutator(mutator) | ||
391 | return rf | ||
392 | } | ||
393 | |||
394 | // NewHelloResultFilterFromBytes creates a new result filter from a binary | ||
395 | // representation: 'data' is the concatenaion 'mutator|bloomfilter'. | ||
396 | // If 'withMutator' is false, no mutator is used. | ||
397 | func NewHelloResultFilterFromBytes(data []byte) *HelloResultFilter { | ||
398 | //logger.Printf(logger.DBG, "[filter] FromBytes = %d:%s (mutator: %v)",len(data), hex.EncodeToString(data), withMutator) | ||
399 | |||
400 | // handle mutator input | ||
401 | mSize := 4 | ||
402 | rf := new(HelloResultFilter) | ||
403 | rf.bf = &BloomFilter{ | ||
404 | Bits: util.Clone(data[mSize:]), | ||
405 | } | ||
406 | if mSize > 0 { | ||
407 | rf.bf.SetMutator(data[:mSize]) | ||
408 | } | ||
409 | return rf | ||
410 | } | ||
411 | |||
412 | // Add a HELLO block to th result filter | ||
413 | func (rf *HelloResultFilter) Add(b Block) { | ||
414 | if hb, ok := b.(*HelloBlock); ok { | ||
415 | hAddr := sha512.Sum512(hb.AddrBin) | ||
416 | rf.bf.Add(hAddr[:]) | ||
417 | } | ||
418 | } | ||
419 | |||
420 | // Contains checks if a block is contained in the result filter | ||
421 | func (rf *HelloResultFilter) Contains(b Block) bool { | ||
422 | if hb, ok := b.(*HelloBlock); ok { | ||
423 | hAddr := sha512.Sum512(hb.AddrBin) | ||
424 | return rf.bf.Contains(hAddr[:]) | ||
425 | } | ||
426 | return false | ||
427 | } | ||
428 | |||
429 | // Bytes returns a binary representation of a HELLO result filter | ||
430 | func (rf *HelloResultFilter) Bytes() []byte { | ||
431 | return rf.bf.Bytes() | ||
432 | } | ||
433 | |||
434 | // Compare two HELLO result filters | ||
435 | func (rf *HelloResultFilter) Compare(t ResultFilter) int { | ||
436 | trf, ok := t.(*HelloResultFilter) | ||
437 | if !ok { | ||
438 | return CMP_DIFFER | ||
439 | } | ||
440 | return rf.bf.Compare(trf.bf) | ||
441 | } | ||
442 | |||
443 | // Merge two HELLO result filters | ||
444 | func (rf *HelloResultFilter) Merge(t ResultFilter) bool { | ||
445 | trf, ok := t.(*HelloResultFilter) | ||
446 | if !ok { | ||
447 | return false | ||
448 | } | ||
449 | return rf.bf.Merge(trf.bf) | ||
450 | } | ||
diff --git a/src/gnunet/service/dht/blocks/types.go b/src/gnunet/service/dht/blocks/types.go deleted file mode 100644 index 04edb6e..0000000 --- a/src/gnunet/service/dht/blocks/types.go +++ /dev/null | |||
@@ -1,26 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | // DHT Block types | ||
22 | const ( | ||
23 | DHT_BLOCK_ANY = 0 | ||
24 | DHT_BLOCK_HELLO = 7 // Type of a block that contains a HELLO for a peer | ||
25 | DHT_BLOCK_GNS = 11 // Block for storing record data | ||
26 | ) | ||
diff --git a/src/gnunet/service/dht/bloomfilter.go b/src/gnunet/service/dht/bloomfilter.go deleted file mode 100644 index dcfd935..0000000 --- a/src/gnunet/service/dht/bloomfilter.go +++ /dev/null | |||
@@ -1,123 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package dht | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "crypto/sha512" | ||
24 | "encoding/binary" | ||
25 | ) | ||
26 | |||
27 | //====================================================================== | ||
28 | // Generic BloomFilter | ||
29 | //====================================================================== | ||
30 | |||
31 | // BloomFilter parameter | ||
32 | var ( | ||
33 | bfNumBits = 128 | ||
34 | bfHash = sha512.New | ||
35 | ) | ||
36 | |||
37 | // BloomFilter is a space-efficient probabilistic datastructure to test if | ||
38 | // an element is part of a set of elementsis defined as a string of bits | ||
39 | // always initially empty. | ||
40 | type BloomFilter struct { | ||
41 | data []byte // filter bits | ||
42 | salt []byte // salt for hashing | ||
43 | } | ||
44 | |||
45 | // NewBloomFilter cretes a new filter using the specified salt. An unused | ||
46 | // salt is set to nil. | ||
47 | func NewBloomFilter(salt []byte) *BloomFilter { | ||
48 | return &BloomFilter{ | ||
49 | data: make([]byte, (bfNumBits+7)/8), | ||
50 | salt: salt, | ||
51 | } | ||
52 | } | ||
53 | |||
54 | // Add entry (binary representation): | ||
55 | // When adding an element to the Bloom filter bf using BF-SET(bf,e), each | ||
56 | // integer n of the mapping M(e) is interpreted as a bit offset n mod L | ||
57 | // within bf and set to 1. | ||
58 | func (bf *BloomFilter) Add(e []byte) { | ||
59 | for _, idx := range bf.indices(e) { | ||
60 | bf.data[idx/8] |= (1 << (idx % 7)) | ||
61 | } | ||
62 | } | ||
63 | |||
64 | // Contains returns true if the entry is most likely to be included: | ||
65 | // When testing if an element may be in the Bloom filter bf using | ||
66 | // BF-TEST(bf,e), each bit offset n mod L within bf MUST have been set to 1. | ||
67 | // Otherwise, the element is not considered to be in the Bloom filter. | ||
68 | func (bf *BloomFilter) Contains(e []byte) bool { | ||
69 | for _, idx := range bf.indices(e) { | ||
70 | if bf.data[idx/8]&(1<<(idx%7)) == 0 { | ||
71 | return false | ||
72 | } | ||
73 | } | ||
74 | return true | ||
75 | } | ||
76 | |||
77 | // indices returns the list of bit indices for antry e: | ||
78 | // The element e is prepended with a salt (pütional) and hashed using SHA-512. | ||
79 | // The resulting byte string is interpreted as a list of 16 32-bit integers | ||
80 | // in network byte order. | ||
81 | func (bf *BloomFilter) indices(e []byte) []int { | ||
82 | // hash the entry (with optional salt prepended) | ||
83 | hsh := bfHash() | ||
84 | if bf.salt != nil { | ||
85 | hsh.Write(bf.salt) | ||
86 | } | ||
87 | hsh.Write(e) | ||
88 | h := hsh.Sum(nil) | ||
89 | |||
90 | // compute the indices for the entry | ||
91 | idx := make([]int, len(h)/2) | ||
92 | buf := bytes.NewReader(h) | ||
93 | for i := range idx { | ||
94 | binary.Read(buf, binary.BigEndian, &idx[i]) | ||
95 | } | ||
96 | return idx | ||
97 | } | ||
98 | |||
99 | //====================================================================== | ||
100 | // BloomFilter for peer addresses | ||
101 | //====================================================================== | ||
102 | |||
103 | // PeerBloomFilter implements specific Add/Contains functions. | ||
104 | type PeerBloomFilter struct { | ||
105 | BloomFilter | ||
106 | } | ||
107 | |||
108 | // NewPeerBloomFilter creates a new filter for peer addresses. | ||
109 | func NewPeerBloomFilter() *PeerBloomFilter { | ||
110 | return &PeerBloomFilter{ | ||
111 | BloomFilter: *NewBloomFilter(nil), | ||
112 | } | ||
113 | } | ||
114 | |||
115 | // Add peer address to the filter. | ||
116 | func (bf *PeerBloomFilter) Add(p *PeerAddress) { | ||
117 | bf.BloomFilter.Add(p.addr[:]) | ||
118 | } | ||
119 | |||
120 | // Contains returns true if the peer address is most likely to be included. | ||
121 | func (bf *PeerBloomFilter) Contains(p *PeerAddress) bool { | ||
122 | return bf.BloomFilter.Contains(p.addr[:]) | ||
123 | } | ||
diff --git a/src/gnunet/service/dht/messages.go b/src/gnunet/service/dht/messages.go new file mode 100644 index 0000000..38f0753 --- /dev/null +++ b/src/gnunet/service/dht/messages.go | |||
@@ -0,0 +1,380 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package dht | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "gnunet/enums" | ||
24 | "gnunet/message" | ||
25 | "gnunet/service/dht/blocks" | ||
26 | "gnunet/transport" | ||
27 | "gnunet/util" | ||
28 | |||
29 | "github.com/bfix/gospel/logger" | ||
30 | "github.com/bfix/gospel/math" | ||
31 | ) | ||
32 | |||
33 | //---------------------------------------------------------------------- | ||
34 | // Handle DHT messages from the network | ||
35 | //---------------------------------------------------------------------- | ||
36 | |||
37 | // HandleMessage handles a DHT request/response message. Responses are sent | ||
38 | // to the specified responder. | ||
39 | func (m *Module) HandleMessage(ctx context.Context, sender *util.PeerID, msgIn message.Message, back transport.Responder) bool { | ||
40 | // assemble log label | ||
41 | label := "dht" | ||
42 | if v := ctx.Value("label"); v != nil { | ||
43 | if s, _ := v.(string); len(s) > 0 { | ||
44 | label = "dht-" + s | ||
45 | } | ||
46 | } | ||
47 | logger.Printf(logger.INFO, "[%s] message received from %s", label, sender) | ||
48 | |||
49 | // process message | ||
50 | switch msg := msgIn.(type) { | ||
51 | |||
52 | case *message.DHTP2PGetMsg: | ||
53 | //-------------------------------------------------------------- | ||
54 | // DHT-P2P GET | ||
55 | //-------------------------------------------------------------- | ||
56 | logger.Printf(logger.INFO, "[%s] Handling DHT-P2P-GET message", label) | ||
57 | query := blocks.NewGenericQuery(msg.Query.Bits, enums.BlockType(msg.BType), msg.Flags) | ||
58 | |||
59 | var block blocks.Block | ||
60 | var dist *math.Int | ||
61 | var err error | ||
62 | |||
63 | //-------------------------------------------------------------- | ||
64 | // validate query (based on block type requested) (9.4.3.1) | ||
65 | btype := enums.BlockType(msg.BType) | ||
66 | blockHdlr, ok := blocks.BlockHandlers[btype] | ||
67 | if ok { | ||
68 | // validate block query | ||
69 | if !blockHdlr.ValidateBlockQuery(msg.Query, msg.XQuery) { | ||
70 | logger.Printf(logger.WARN, "[%s] DHT-P2P-GET invalid query -- discarded", label) | ||
71 | return false | ||
72 | } | ||
73 | } else { | ||
74 | logger.Printf(logger.INFO, "[%s] no handler defined for block type %s", label, btype.String()) | ||
75 | blockHdlr = nil | ||
76 | } | ||
77 | //---------------------------------------------------------- | ||
78 | // check if sender is in peer filter (9.4.3.2) | ||
79 | if !msg.PeerFilter.Contains(sender) { | ||
80 | logger.Printf(logger.WARN, "[%s] sender not in peer filter", label) | ||
81 | } | ||
82 | // parse result filter | ||
83 | var rf blocks.ResultFilter = new(blocks.PassResultFilter) | ||
84 | if msg.ResFilter != nil && len(msg.ResFilter) > 0 { | ||
85 | if blockHdlr != nil { | ||
86 | rf = blockHdlr.ParseResultFilter(msg.ResFilter) | ||
87 | } else { | ||
88 | logger.Printf(logger.WARN, "[%s] unknown result filter implementation -- skipped", label) | ||
89 | } | ||
90 | } | ||
91 | // clone peer filter | ||
92 | pf := msg.PeerFilter.Clone() | ||
93 | |||
94 | //---------------------------------------------------------- | ||
95 | // check if we need to respond (and how) (9.4.3.3) | ||
96 | addr := NewQueryAddress(msg.Query) | ||
97 | closest := m.rtable.IsClosestPeer(nil, addr, msg.PeerFilter) | ||
98 | demux := int(msg.Flags)&enums.DHT_RO_DEMULTIPLEX_EVERYWHERE != 0 | ||
99 | approx := int(msg.Flags)&enums.DHT_RO_FIND_APPROXIMATE != 0 | ||
100 | // actions | ||
101 | doResult := closest || (demux && approx) | ||
102 | doForward := !closest || (demux && !approx) | ||
103 | logger.Printf(logger.DBG, "[dht] GET message: closest=%v, demux=%v, approx=%v --> result=%v, forward=%v", | ||
104 | closest, demux, approx, doResult, doForward) | ||
105 | |||
106 | //------------------------------------------------------ | ||
107 | // query for a HELLO? (9.4.3.3a) | ||
108 | if msg.BType == uint32(enums.BLOCK_TYPE_DHT_URL_HELLO) { | ||
109 | logger.Println(logger.DBG, "[dht] GET message for HELLO: check cache") | ||
110 | // find best cached HELLO | ||
111 | block, dist = m.rtable.BestHello(addr, rf) | ||
112 | } | ||
113 | //-------------------------------------------------------------- | ||
114 | // find the closest block that has that is not filtered/ by the result | ||
115 | // filter (in case we did not find an appropriate block in cache). | ||
116 | if doResult { | ||
117 | // save best-match values from cache | ||
118 | blockCache := block | ||
119 | distCache := dist | ||
120 | |||
121 | // query DHT store for exact match (9.4.3.3c) | ||
122 | if block, err = m.Get(ctx, query); err != nil { | ||
123 | logger.Printf(logger.ERROR, "[%s] Failed to get DHT block from storage: %s", label, err.Error()) | ||
124 | return true | ||
125 | } | ||
126 | // if block is filtered, skip it | ||
127 | if rf.Contains(block) { | ||
128 | logger.Println(logger.DBG, "[dht] GET message for HELLO: matching DHT block is filtered") | ||
129 | block = nil | ||
130 | } | ||
131 | // if we have no exact match, find approximate block if requested | ||
132 | if block == nil || approx { | ||
133 | // no exact match: find approximate (9.4.3.3b) | ||
134 | match := func(b blocks.Block) bool { | ||
135 | return rf.Contains(b) | ||
136 | } | ||
137 | block, dist, err = m.GetApprox(ctx, query, match) | ||
138 | if err != nil { | ||
139 | logger.Printf(logger.ERROR, "[%s] Failed to get (approx.) DHT block from storage: %s", label, err.Error()) | ||
140 | return true | ||
141 | } | ||
142 | } | ||
143 | // if we have a block from cache, check if it is better than the | ||
144 | // block found in the DHT | ||
145 | if blockCache != nil && distCache.Cmp(dist) < 0 { | ||
146 | block = blockCache | ||
147 | } | ||
148 | // if we have a block, send it as response | ||
149 | if block != nil { | ||
150 | logger.Printf(logger.INFO, "[%s] sending DHT result message to caller", label) | ||
151 | if err := m.sendResult(ctx, query, block, back); err != nil { | ||
152 | logger.Printf(logger.ERROR, "[%s] Failed to send DHT result message: %s", label, err.Error()) | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | // check if we need to forward message based on filter result | ||
157 | if block != nil && blockHdlr != nil { | ||
158 | switch blockHdlr.FilterResult(block, query.Key(), rf, msg.XQuery) { | ||
159 | case blocks.RF_LAST: | ||
160 | // no need for further results | ||
161 | case blocks.RF_MORE: | ||
162 | // possibly more results | ||
163 | doForward = true | ||
164 | case blocks.RF_DUPLICATE, blocks.RF_IRRELEVANT: | ||
165 | // do not forward | ||
166 | } | ||
167 | } | ||
168 | if doForward { | ||
169 | // build updated GET message | ||
170 | pf.Add(m.core.PeerID()) | ||
171 | msgOut := msg.Update(pf, rf, msg.HopCount+1) | ||
172 | |||
173 | // forward to number of peers | ||
174 | numForward := m.rtable.ComputeOutDegree(msg.ReplLevel, msg.HopCount) | ||
175 | key := NewQueryAddress(query.Key()) | ||
176 | for n := 0; n < numForward; n++ { | ||
177 | if p := m.rtable.SelectClosestPeer(key, pf); p != nil { | ||
178 | // forward message to peer | ||
179 | logger.Printf(logger.INFO, "[%s] forward DHT get message to %s", label, p.String()) | ||
180 | if err := back.Send(ctx, msgOut); err != nil { | ||
181 | logger.Printf(logger.ERROR, "[%s] Failed to forward DHT get message: %s", label, err.Error()) | ||
182 | } | ||
183 | pf.Add(p.Peer) | ||
184 | // create open get-forward result handler | ||
185 | rh := NewForwardResultHandler(msg, rf, back) | ||
186 | logger.Printf(logger.INFO, "[%s] DHT-P2P-GET task #%d started", label, rh.ID()) | ||
187 | m.reshdlrs.Add(rh) | ||
188 | } else { | ||
189 | break | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | logger.Printf(logger.INFO, "[%s] Handling DHT-P2P-GET message done", label) | ||
194 | |||
195 | case *message.DHTP2PPutMsg: | ||
196 | //---------------------------------------------------------- | ||
197 | // DHT-P2P PUT | ||
198 | //---------------------------------------------------------- | ||
199 | logger.Printf(logger.INFO, "[%s] Handling DHT-P2P-PUT message", label) | ||
200 | |||
201 | //-------------------------------------------------------------- | ||
202 | // check if request is expired (9.3.2.1) | ||
203 | if msg.Expiration.Expired() { | ||
204 | logger.Printf(logger.WARN, "[%s] DHT-P2P-PUT message expired (%s)", label, msg.Expiration.String()) | ||
205 | return false | ||
206 | } | ||
207 | btype := enums.BlockType(msg.BType) | ||
208 | blockHdlr, ok := blocks.BlockHandlers[btype] | ||
209 | if ok { // (9.3.2.2) | ||
210 | // reconstruct block instance | ||
211 | if block, err := blockHdlr.ParseBlock(msg.Block); err == nil { | ||
212 | |||
213 | // validate block key (9.3.2.3) | ||
214 | if !blockHdlr.ValidateBlockKey(block, msg.Key) { | ||
215 | logger.Printf(logger.WARN, "[%s] DHT-P2P-PUT invalid key -- discarded", label) | ||
216 | return false | ||
217 | } | ||
218 | |||
219 | // validate block payload (9.3.2.4) | ||
220 | if !blockHdlr.ValidateBlockStoreRequest(block) { | ||
221 | logger.Printf(logger.WARN, "[%s] DHT-P2P-PUT invalid payload -- discarded", label) | ||
222 | return false | ||
223 | } | ||
224 | } | ||
225 | } else { | ||
226 | logger.Printf(logger.INFO, "[%s] No validator defined for block type %s", label, btype.String()) | ||
227 | blockHdlr = nil | ||
228 | } | ||
229 | //-------------------------------------------------------------- | ||
230 | // check if sender is in peer filter (9.3.2.5) | ||
231 | if !msg.PeerFilter.Contains(sender) { | ||
232 | logger.Printf(logger.WARN, "[%s] Sender not in peer filter", label) | ||
233 | } | ||
234 | //-------------------------------------------------------------- | ||
235 | // check if route is recorded (9.3.2.6) | ||
236 | /* | ||
237 | withPath := msg.Flags&enums.DHT_RO_RECORD_ROUTE != 0 | ||
238 | if withPath { | ||
239 | spe := message.NewPathElement(msg.Key, sender, p) | ||
240 | m.core.Sign(spe) | ||
241 | msg.AppendPutPath(spe) | ||
242 | } | ||
243 | */ | ||
244 | //-------------------------------------------------------------- | ||
245 | // verify PUT path (9.3.2.7) | ||
246 | if msg.PathL > 0 { | ||
247 | |||
248 | } | ||
249 | |||
250 | logger.Printf(logger.INFO, "[%s] Handling DHT-P2P-PUT message done", label) | ||
251 | |||
252 | case *message.DHTP2PResultMsg: | ||
253 | //---------------------------------------------------------- | ||
254 | // DHT-P2P RESULT | ||
255 | //---------------------------------------------------------- | ||
256 | logger.Printf(logger.INFO, "[%s] Handling DHT-P2P-RESULT message", label) | ||
257 | |||
258 | // check task list for handler | ||
259 | key := msg.Query.String() | ||
260 | handled := false | ||
261 | if list, ok := m.reshdlrs.Get(key); ok { | ||
262 | for _, rh := range list { | ||
263 | logger.Printf(logger.DBG, "[%s] Task #%d for DHT-P2P-RESULT found", label, rh.ID()) | ||
264 | // handle the message | ||
265 | go rh.Handle(ctx, msg) | ||
266 | } | ||
267 | return true | ||
268 | } | ||
269 | if !handled { | ||
270 | logger.Printf(logger.WARN, "[%s] DHT-P2P-RESULT not processed (no handler)", label) | ||
271 | } | ||
272 | return handled | ||
273 | |||
274 | case *message.DHTP2PHelloMsg: | ||
275 | //---------------------------------------------------------- | ||
276 | // DHT-P2P HELLO | ||
277 | //---------------------------------------------------------- | ||
278 | logger.Printf(logger.INFO, "[%s] Handling DHT-P2P-HELLO message", label) | ||
279 | |||
280 | // verify integrity of message | ||
281 | if ok, err := msg.Verify(sender); !ok || err != nil { | ||
282 | logger.Println(logger.WARN, "[dht] Received invalid DHT_P2P_HELLO message") | ||
283 | if err != nil { | ||
284 | logger.Println(logger.ERROR, "[dht] --> "+err.Error()) | ||
285 | } | ||
286 | return false | ||
287 | } | ||
288 | // keep peer addresses in core for transport | ||
289 | aList, err := msg.Addresses() | ||
290 | if err != nil { | ||
291 | logger.Println(logger.ERROR, "[dht] Failed to parse addresses from DHT_P2P_HELLO message") | ||
292 | return false | ||
293 | } | ||
294 | if newPeer := m.core.Learn(ctx, sender, aList); newPeer { | ||
295 | // we added a previously unknown peer: send a HELLO | ||
296 | var msgOut *message.DHTP2PHelloMsg | ||
297 | if msgOut, err = m.getHello(); err != nil { | ||
298 | return false | ||
299 | } | ||
300 | logger.Printf(logger.INFO, "[dht] Sending HELLO to %s: %s", sender, msgOut) | ||
301 | err = m.core.Send(ctx, sender, msgOut) | ||
302 | // no error if the message might have been sent | ||
303 | if err == transport.ErrEndpMaybeSent { | ||
304 | err = nil | ||
305 | } | ||
306 | } | ||
307 | |||
308 | // cache HELLO block if applicable | ||
309 | k := sender.String() | ||
310 | isNew := true | ||
311 | if hb, ok := m.rtable.GetHello(k); ok { | ||
312 | // cache entry exists: is the HELLO message more recent? | ||
313 | _, isNew = hb.Expires.Diff(msg.Expires) | ||
314 | } | ||
315 | // we need to cache a new(er) HELLO | ||
316 | if isNew { | ||
317 | m.rtable.CacheHello(&blocks.HelloBlock{ | ||
318 | PeerID: sender, | ||
319 | Signature: msg.Signature, | ||
320 | Expires: msg.Expires, | ||
321 | AddrBin: util.Clone(msg.AddrList), | ||
322 | }) | ||
323 | } | ||
324 | |||
325 | //-------------------------------------------------------------- | ||
326 | // Legacy message types (not implemented) | ||
327 | //-------------------------------------------------------------- | ||
328 | |||
329 | case *message.DHTClientPutMsg: | ||
330 | //---------------------------------------------------------- | ||
331 | // DHT PUT | ||
332 | //---------------------------------------------------------- | ||
333 | logger.Printf(logger.INFO, "[%s] Handling DHTClientPut message", label) | ||
334 | |||
335 | case *message.DHTClientGetMsg: | ||
336 | //---------------------------------------------------------- | ||
337 | // DHT GET | ||
338 | //---------------------------------------------------------- | ||
339 | logger.Printf(logger.INFO, "[%s] Handling DHTClientGet message", label) | ||
340 | |||
341 | case *message.DHTClientGetResultsKnownMsg: | ||
342 | //---------------------------------------------------------- | ||
343 | // DHT GET-RESULTS-KNOWN | ||
344 | //---------------------------------------------------------- | ||
345 | logger.Printf(logger.INFO, "[%s] Handling DHTClientGetResultsKnown message", label) | ||
346 | |||
347 | case *message.DHTClientGetStopMsg: | ||
348 | //---------------------------------------------------------- | ||
349 | // DHT GET-STOP | ||
350 | //---------------------------------------------------------- | ||
351 | logger.Printf(logger.INFO, "[%s] Handling DHTClientGetStop message", label) | ||
352 | |||
353 | case *message.DHTClientResultMsg: | ||
354 | //---------------------------------------------------------- | ||
355 | // DHT RESULT | ||
356 | //---------------------------------------------------------- | ||
357 | logger.Printf(logger.INFO, "[%s] Handling DHTClientResult message", label) | ||
358 | |||
359 | default: | ||
360 | //---------------------------------------------------------- | ||
361 | // UNKNOWN message type received | ||
362 | //---------------------------------------------------------- | ||
363 | logger.Printf(logger.ERROR, "[%s] Unhandled message of type (%d)\n", label, msgIn.Header().MsgType) | ||
364 | return false | ||
365 | } | ||
366 | return true | ||
367 | } | ||
368 | |||
369 | // send a result back to caller | ||
370 | func (m *Module) sendResult(ctx context.Context, query blocks.Query, blk blocks.Block, back transport.Responder) error { | ||
371 | // assemble result message | ||
372 | out := message.NewDHTP2PResultMsg() | ||
373 | out.BType = uint32(query.Type()) | ||
374 | out.Expires = blk.Expire() | ||
375 | out.Query = query.Key() | ||
376 | out.Block = blk.Data() | ||
377 | out.MsgSize += uint16(len(out.Block)) | ||
378 | // send message | ||
379 | return back.Send(ctx, out) | ||
380 | } | ||
diff --git a/src/gnunet/service/dht/module.go b/src/gnunet/service/dht/module.go index 612d0ec..ed28827 100644 --- a/src/gnunet/service/dht/module.go +++ b/src/gnunet/service/dht/module.go | |||
@@ -20,14 +20,20 @@ package dht | |||
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | 22 | "context" |
23 | "errors" | ||
23 | "gnunet/config" | 24 | "gnunet/config" |
24 | "gnunet/core" | 25 | "gnunet/core" |
25 | "gnunet/message" | 26 | "gnunet/message" |
26 | "gnunet/service" | 27 | "gnunet/service" |
27 | "gnunet/service/dht/blocks" | 28 | "gnunet/service/dht/blocks" |
29 | "gnunet/service/store" | ||
30 | "gnunet/transport" | ||
31 | "gnunet/util" | ||
32 | gmath "math" | ||
28 | "time" | 33 | "time" |
29 | 34 | ||
30 | "github.com/bfix/gospel/logger" | 35 | "github.com/bfix/gospel/logger" |
36 | "github.com/bfix/gospel/math" | ||
31 | ) | 37 | ) |
32 | 38 | ||
33 | //====================================================================== | 39 | //====================================================================== |
@@ -42,34 +48,36 @@ import ( | |||
42 | type Module struct { | 48 | type Module struct { |
43 | service.ModuleImpl | 49 | service.ModuleImpl |
44 | 50 | ||
45 | store service.DHTStore // reference to the block storage mechanism | 51 | store store.DHTStore // reference to the block storage mechanism |
46 | cache service.DHTStore // transient block cache | 52 | core *core.Core // reference to core services |
47 | core *core.Core // reference to core services | ||
48 | 53 | ||
49 | rtable *RoutingTable // routing table | 54 | rtable *RoutingTable // routing table |
55 | lastHello *message.DHTP2PHelloMsg // last own HELLO message used; re-create if expired | ||
56 | reshdlrs *ResultHandlerList // list of open tasks | ||
50 | } | 57 | } |
51 | 58 | ||
52 | // NewModule returns a new module instance. It initializes the storage | 59 | // NewModule returns a new module instance. It initializes the storage |
53 | // mechanism for persistence. | 60 | // mechanism for persistence. |
54 | func NewModule(ctx context.Context, c *core.Core) (m *Module, err error) { | 61 | func NewModule(ctx context.Context, c *core.Core, cfg *config.DHTConfig) (m *Module, err error) { |
55 | // create permanent storage handler | 62 | // create permanent storage handler |
56 | var store, cache service.DHTStore | 63 | var storage store.DHTStore |
57 | if store, err = service.NewDHTStore(config.Cfg.DHT.Storage); err != nil { | 64 | if storage, err = store.NewDHTStore(cfg.Storage); err != nil { |
58 | return | 65 | return |
59 | } | 66 | } |
60 | // create routing table | 67 | // create routing table |
61 | rt := NewRoutingTable(NewPeerAddress(c.PeerID())) | 68 | rt := NewRoutingTable(NewPeerAddress(c.PeerID()), cfg.Routing) |
62 | 69 | ||
63 | // return module instance | 70 | // return module instance |
64 | m = &Module{ | 71 | m = &Module{ |
65 | ModuleImpl: *service.NewModuleImpl(), | 72 | ModuleImpl: *service.NewModuleImpl(), |
66 | store: store, | 73 | store: storage, |
67 | cache: cache, | ||
68 | core: c, | 74 | core: c, |
69 | rtable: rt, | 75 | rtable: rt, |
76 | reshdlrs: NewResultHandlerList(), | ||
70 | } | 77 | } |
71 | // register as listener for core events | 78 | // register as listener for core events |
72 | listener := m.Run(ctx, m.event, m.Filter(), 15*time.Minute, m.heartbeat) | 79 | pulse := time.Duration(cfg.Heartbeat) * time.Second |
80 | listener := m.Run(ctx, m.event, m.Filter(), pulse, m.heartbeat) | ||
73 | c.Register("dht", listener) | 81 | c.Register("dht", listener) |
74 | return | 82 | return |
75 | } | 83 | } |
@@ -78,26 +86,20 @@ func NewModule(ctx context.Context, c *core.Core) (m *Module, err error) { | |||
78 | 86 | ||
79 | // Get a block from the DHT ["dht:get"] | 87 | // Get a block from the DHT ["dht:get"] |
80 | func (m *Module) Get(ctx context.Context, query blocks.Query) (block blocks.Block, err error) { | 88 | func (m *Module) Get(ctx context.Context, query blocks.Query) (block blocks.Block, err error) { |
89 | return m.store.Get(query) | ||
90 | } | ||
81 | 91 | ||
82 | // check if we have the requested block in cache or permanent storage. | 92 | // GetApprox returns the first block not excluded ["dht:getapprox"] |
83 | block, err = m.cache.Get(query) | 93 | func (m *Module) GetApprox(ctx context.Context, query blocks.Query, excl func(blocks.Block) bool) (block blocks.Block, dist *math.Int, err error) { |
84 | if err == nil { | 94 | var d any |
85 | // yes: we are done | 95 | block, d, err = m.store.GetApprox(query, excl) |
86 | return | 96 | dist, _ = d.(*math.Int) |
87 | } | 97 | return |
88 | block, err = m.store.Get(query) | ||
89 | if err == nil { | ||
90 | // yes: we are done | ||
91 | return | ||
92 | } | ||
93 | // retrieve the block from the DHT | ||
94 | |||
95 | return nil, nil | ||
96 | } | 98 | } |
97 | 99 | ||
98 | // Put a block into the DHT ["dht:put"] | 100 | // Put a block into the DHT ["dht:put"] |
99 | func (m *Module) Put(ctx context.Context, key blocks.Query, block blocks.Block) error { | 101 | func (m *Module) Put(ctx context.Context, key blocks.Query, block blocks.Block) error { |
100 | return nil | 102 | return m.store.Put(key, block) |
101 | } | 103 | } |
102 | 104 | ||
103 | //---------------------------------------------------------------------- | 105 | //---------------------------------------------------------------------- |
@@ -110,17 +112,17 @@ func (m *Module) Filter() *core.EventFilter { | |||
110 | f.AddEvent(core.EV_DISCONNECT) | 112 | f.AddEvent(core.EV_DISCONNECT) |
111 | 113 | ||
112 | // messages we are interested in: | 114 | // messages we are interested in: |
113 | // (1) DHT messages | 115 | // (1) DHT_P2P messages |
116 | f.AddMsgType(message.DHT_P2P_PUT) | ||
117 | f.AddMsgType(message.DHT_P2P_GET) | ||
118 | f.AddMsgType(message.DHT_P2P_RESULT) | ||
119 | f.AddMsgType(message.DHT_P2P_HELLO) | ||
120 | // (2) DHT messages (legacy, not implemented) | ||
114 | f.AddMsgType(message.DHT_CLIENT_GET) | 121 | f.AddMsgType(message.DHT_CLIENT_GET) |
115 | f.AddMsgType(message.DHT_CLIENT_GET_RESULTS_KNOWN) | 122 | f.AddMsgType(message.DHT_CLIENT_GET_RESULTS_KNOWN) |
116 | f.AddMsgType(message.DHT_CLIENT_GET_STOP) | 123 | f.AddMsgType(message.DHT_CLIENT_GET_STOP) |
117 | f.AddMsgType(message.DHT_CLIENT_PUT) | 124 | f.AddMsgType(message.DHT_CLIENT_PUT) |
118 | f.AddMsgType(message.DHT_CLIENT_RESULT) | 125 | f.AddMsgType(message.DHT_CLIENT_RESULT) |
119 | // (2) DHT_P2P messages | ||
120 | f.AddMsgType(message.DHT_P2P_PUT) | ||
121 | f.AddMsgType(message.DHT_P2P_GET) | ||
122 | f.AddMsgType(message.DHT_P2P_RESULT) | ||
123 | f.AddMsgType(message.DHT_P2P_HELLO) | ||
124 | 126 | ||
125 | return f | 127 | return f |
126 | } | 128 | } |
@@ -143,32 +145,105 @@ func (m *Module) event(ctx context.Context, ev *core.Event) { | |||
143 | // Message received. | 145 | // Message received. |
144 | case core.EV_MESSAGE: | 146 | case core.EV_MESSAGE: |
145 | logger.Printf(logger.INFO, "[dht] Message received: %s", ev.Msg.String()) | 147 | logger.Printf(logger.INFO, "[dht] Message received: %s", ev.Msg.String()) |
146 | // process message (if applicable) | 148 | |
147 | if m.ProcessFcn != nil { | 149 | // check if peer is in routing table (connected peer) |
148 | m.ProcessFcn(ctx, ev.Msg, ev.Resp) | 150 | if !m.rtable.Contains(NewPeerAddress(ev.Peer)) { |
151 | logger.Printf(logger.WARN, "[dht] message %d from unregistered peer -- discarded", ev.Msg.Header().MsgType) | ||
152 | return | ||
153 | } | ||
154 | // process message | ||
155 | if !m.HandleMessage(ctx, ev.Peer, ev.Msg, ev.Resp) { | ||
156 | logger.Println(logger.WARN, "[dht] Message NOT handled!") | ||
149 | } | 157 | } |
150 | } | 158 | } |
151 | } | 159 | } |
152 | 160 | ||
153 | // Heartbeat handler for periodic tasks | 161 | // Heartbeat handler for periodic tasks |
154 | func (m *Module) heartbeat(ctx context.Context) { | 162 | func (m *Module) heartbeat(ctx context.Context) { |
155 | // update the estimated network size | ||
156 | m.rtable.l2nse = m.core.L2NSE() | ||
157 | |||
158 | // run heartbeat for routing table | 163 | // run heartbeat for routing table |
159 | m.rtable.heartbeat(ctx) | 164 | m.rtable.heartbeat(ctx) |
165 | |||
166 | // clean-up task list | ||
167 | m.reshdlrs.Cleanup() | ||
160 | } | 168 | } |
161 | 169 | ||
170 | // Send the currently active HELLO to given network address | ||
171 | func (m *Module) SendHello(ctx context.Context, addr *util.Address) (err error) { | ||
172 | // get (buffered) HELLO | ||
173 | var msg *message.DHTP2PHelloMsg | ||
174 | if msg, err = m.getHello(); err != nil { | ||
175 | return | ||
176 | } | ||
177 | logger.Printf(logger.INFO, "[core] Sending HELLO to %s: %s", addr.URI(), msg) | ||
178 | return m.core.SendToAddr(ctx, addr, msg) | ||
179 | } | ||
180 | |||
181 | // get the recent HELLO if it is defined and not expired; | ||
182 | // create a new HELLO otherwise. | ||
183 | func (m *Module) getHello() (msg *message.DHTP2PHelloMsg, err error) { | ||
184 | if m.lastHello == nil || m.lastHello.Expires.Expired() { | ||
185 | // assemble new (signed) HELLO block | ||
186 | var addrList []*util.Address | ||
187 | if addrList, err = m.core.Addresses(); err != nil { | ||
188 | return | ||
189 | } | ||
190 | // assemble HELLO data | ||
191 | hb := new(blocks.HelloBlock) | ||
192 | hb.PeerID = m.core.PeerID() | ||
193 | hb.Expires = util.NewAbsoluteTime(time.Now().Add(message.HelloAddressExpiration)) | ||
194 | hb.SetAddresses(addrList) | ||
195 | |||
196 | // sign HELLO block | ||
197 | if err = m.core.Sign(hb); err != nil { | ||
198 | return | ||
199 | } | ||
200 | // assemble HELLO message | ||
201 | msg = message.NewDHTP2PHelloMsg() | ||
202 | msg.Expires = hb.Expires | ||
203 | msg.SetAddresses(hb.Addresses()) | ||
204 | if err = m.core.Sign(msg); err != nil { | ||
205 | return | ||
206 | } | ||
207 | |||
208 | // save for later use | ||
209 | m.lastHello = msg | ||
210 | |||
211 | // DEBUG | ||
212 | var ok bool | ||
213 | if ok, err = msg.Verify(m.core.PeerID()); !ok || err != nil { | ||
214 | if !ok { | ||
215 | err = errors.New("failed to verify own HELLO") | ||
216 | } | ||
217 | logger.Println(logger.ERROR, err.Error()) | ||
218 | return | ||
219 | } | ||
220 | logger.Println(logger.DBG, "[dht] New HELLO: "+transport.Dump(msg, "hex")) | ||
221 | return | ||
222 | } | ||
223 | // we have a valid HELLO for re-use. | ||
224 | return m.lastHello, nil | ||
225 | } | ||
226 | |||
227 | //---------------------------------------------------------------------- | ||
228 | // Inter-module linkage helpers | ||
162 | //---------------------------------------------------------------------- | 229 | //---------------------------------------------------------------------- |
163 | 230 | ||
164 | // Export functions | 231 | // Export functions |
165 | func (m *Module) Export(fcn map[string]any) { | 232 | func (m *Module) Export(fcn map[string]any) { |
166 | // add exported functions from module | 233 | // add exported functions from module |
167 | fcn["dht:get"] = m.Get | 234 | fcn["dht:get"] = m.Get |
235 | fcn["dht:getapprox"] = m.GetApprox | ||
168 | fcn["dht:put"] = m.Put | 236 | fcn["dht:put"] = m.Put |
169 | } | 237 | } |
170 | 238 | ||
171 | // Import functions | 239 | // Import functions |
172 | func (m *Module) Import(fcm map[string]any) { | 240 | func (m *Module) Import(fcn map[string]any) { |
173 | // nothing to import now. | 241 | // nothing to import for now. |
242 | } | ||
243 | |||
244 | //---------------------------------------------------------------------- | ||
245 | |||
246 | // SetNetworkSize sets a fixed number of peers in the network | ||
247 | func (m *Module) SetNetworkSize(numPeers int) { | ||
248 | m.rtable.l2nse = gmath.Log2(float64(numPeers)) | ||
174 | } | 249 | } |
diff --git a/src/gnunet/service/dht/resulthandler.go b/src/gnunet/service/dht/resulthandler.go new file mode 100644 index 0000000..6564abd --- /dev/null +++ b/src/gnunet/service/dht/resulthandler.go | |||
@@ -0,0 +1,351 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package dht | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "context" | ||
24 | "gnunet/crypto" | ||
25 | "gnunet/message" | ||
26 | "gnunet/service/dht/blocks" | ||
27 | "gnunet/transport" | ||
28 | "gnunet/util" | ||
29 | "time" | ||
30 | |||
31 | "github.com/bfix/gospel/logger" | ||
32 | ) | ||
33 | |||
34 | //====================================================================== | ||
35 | // DHT GET requests send to neighbours result in DHT RESULT messages | ||
36 | // being returned that need to be handled. The sequence of incoming | ||
37 | // results is undetermined and usually not terminated (that is, there | ||
38 | // is no mechanism to determine the end of results). | ||
39 | // ResultHandlers handle DHT RESULT messages. The appropriate handler | ||
40 | // is selected by the DHT query/store key associated with a GET/RESULT | ||
41 | // message; there can be multiple handlers for the same key (serving | ||
42 | // different GET requests and/or differnent originators). | ||
43 | //====================================================================== | ||
44 | |||
45 | // ResultHandler interface | ||
46 | type ResultHandler interface { | ||
47 | |||
48 | // ID returna the handler id | ||
49 | ID() int | ||
50 | |||
51 | // Done returns true if handler can be removed | ||
52 | Done() bool | ||
53 | |||
54 | // Key returns the query/store key as string | ||
55 | Key() string | ||
56 | |||
57 | // Compare two result handlers | ||
58 | Compare(ResultHandler) int | ||
59 | |||
60 | // Merge two result handlers that are the same except for result filter | ||
61 | Merge(ResultHandler) bool | ||
62 | |||
63 | // Handle result message | ||
64 | Handle(context.Context, *message.DHTP2PResultMsg) bool | ||
65 | } | ||
66 | |||
67 | // Compare return values | ||
68 | //nolint:stylecheck // allow non-camel-case in constants | ||
69 | const ( | ||
70 | RHC_SAME = blocks.CMP_SAME // the two result handlers are the same | ||
71 | RHC_MERGE = blocks.CMP_MERGE // the two result handlers can be merged | ||
72 | RHC_DIFFER = blocks.CMP_DIFFER // the two result handlers are different | ||
73 | RHC_SIBL = blocks.CMP_1 // the two result handlers are siblings | ||
74 | ) | ||
75 | |||
76 | //---------------------------------------------------------------------- | ||
77 | |||
78 | // Generic (shared) result handler data structure | ||
79 | type GenericResultHandler struct { | ||
80 | id int // task identifier | ||
81 | key *crypto.HashCode // GET query key | ||
82 | btype uint32 // content type of the payload | ||
83 | flags uint16 // processing flags | ||
84 | resFilter blocks.ResultFilter // result filter | ||
85 | xQuery []byte // extended query | ||
86 | started util.AbsoluteTime // Timestamp of session start | ||
87 | active bool // is the task active? | ||
88 | } | ||
89 | |||
90 | // NewGenericResultHandler creates an instance from a DHT-GET message and a | ||
91 | // result filter instance. | ||
92 | func NewGenericResultHandler(msg *message.DHTP2PGetMsg, rf blocks.ResultFilter) *GenericResultHandler { | ||
93 | return &GenericResultHandler{ | ||
94 | id: util.NextID(), | ||
95 | key: msg.Query.Clone(), | ||
96 | btype: msg.BType, | ||
97 | flags: msg.Flags, | ||
98 | resFilter: rf, | ||
99 | xQuery: util.Clone(msg.XQuery), | ||
100 | started: util.AbsoluteTimeNow(), | ||
101 | active: true, | ||
102 | } | ||
103 | } | ||
104 | |||
105 | // ID returns the result handler identifier | ||
106 | func (t *GenericResultHandler) ID() int { | ||
107 | return t.id | ||
108 | } | ||
109 | |||
110 | // Key returns the key string | ||
111 | func (t *GenericResultHandler) Key() string { | ||
112 | return t.key.String() | ||
113 | } | ||
114 | |||
115 | // Done returns true if the result handler is no longer active. | ||
116 | func (t *GenericResultHandler) Done() bool { | ||
117 | return !t.active || t.started.Add(time.Hour).Expired() | ||
118 | } | ||
119 | |||
120 | // Compare two handlers | ||
121 | func (t *GenericResultHandler) Compare(h *GenericResultHandler) int { | ||
122 | if t.key.Equals(h.key) || | ||
123 | t.btype != h.btype || | ||
124 | t.flags != h.flags || | ||
125 | !bytes.Equal(t.xQuery, h.xQuery) { | ||
126 | return RHC_DIFFER | ||
127 | } | ||
128 | return t.resFilter.Compare(h.resFilter) | ||
129 | } | ||
130 | |||
131 | // Merge two result handlers that are the same except for result filter | ||
132 | func (t *GenericResultHandler) Merge(a *GenericResultHandler) bool { | ||
133 | return t.resFilter.Merge(a.resFilter) | ||
134 | } | ||
135 | |||
136 | //---------------------------------------------------------------------- | ||
137 | // Result handler for forwarded GET requests | ||
138 | //---------------------------------------------------------------------- | ||
139 | |||
140 | // ForwardResultHandler data structure | ||
141 | type ForwardResultHandler struct { | ||
142 | GenericResultHandler | ||
143 | |||
144 | resp transport.Responder // responder for communicating back to originator | ||
145 | } | ||
146 | |||
147 | // NewForwardResultHandler derived from DHT-GET message | ||
148 | func NewForwardResultHandler(msgIn message.Message, rf blocks.ResultFilter, back transport.Responder) *ForwardResultHandler { | ||
149 | // check for correct message type and handler function | ||
150 | msg, ok := msgIn.(*message.DHTP2PGetMsg) | ||
151 | if ok { | ||
152 | return &ForwardResultHandler{ | ||
153 | GenericResultHandler: *NewGenericResultHandler(msg, rf), | ||
154 | resp: back, | ||
155 | } | ||
156 | } | ||
157 | return nil | ||
158 | } | ||
159 | |||
160 | // Handle incoming DHT-P2P-RESULT message | ||
161 | func (t *ForwardResultHandler) Handle(ctx context.Context, msg *message.DHTP2PResultMsg) bool { | ||
162 | // send result message back to originator (result forwarding). | ||
163 | logger.Printf(logger.INFO, "[dht-task-%d] sending result back to originator", t.id) | ||
164 | if err := t.resp.Send(ctx, msg); err != nil && err != transport.ErrEndpMaybeSent { | ||
165 | logger.Printf(logger.ERROR, "[dht-task-%d] sending result back to originator failed: %s", t.id, err.Error()) | ||
166 | return false | ||
167 | } | ||
168 | return true | ||
169 | } | ||
170 | |||
171 | // Compare two forward result filters | ||
172 | func (t *ForwardResultHandler) Compare(h ResultHandler) int { | ||
173 | // check for correct handler type | ||
174 | ht, ok := h.(*ForwardResultHandler) | ||
175 | if !ok { | ||
176 | return RHC_DIFFER | ||
177 | } | ||
178 | // check for same recipient | ||
179 | if ht.resp.Receiver() != t.resp.Receiver() { | ||
180 | return RHC_DIFFER | ||
181 | } | ||
182 | // check generic handler data | ||
183 | return t.GenericResultHandler.Compare(&ht.GenericResultHandler) | ||
184 | } | ||
185 | |||
186 | // Merge two forward result handlers | ||
187 | func (t *ForwardResultHandler) Merge(h ResultHandler) bool { | ||
188 | // check for correct handler type | ||
189 | ht, ok := h.(*ForwardResultHandler) | ||
190 | if !ok { | ||
191 | return false | ||
192 | } | ||
193 | return t.GenericResultHandler.Merge(&ht.GenericResultHandler) | ||
194 | } | ||
195 | |||
196 | //---------------------------------------------------------------------- | ||
197 | // Result handler for locally-initiated GET requests: | ||
198 | // | ||
199 | // Before sending the GET request a handler is added for the request: | ||
200 | // | ||
201 | // rc := make(chan any) | ||
202 | // myRH := NewDirectResultHandler(msg, rf, MyCustomHandler, rc) | ||
203 | // m.reshdlrs.Add(myRH) | ||
204 | // | ||
205 | // If a matching response is received, the custom handler is executed | ||
206 | // in a separate go-routine. A custom handler returns a result (or error) on | ||
207 | // a back channel and should be context-sensitive (termination). | ||
208 | // | ||
209 | // If an asynchronous behaviour is required, use 'ret := <-rc' to wait for | ||
210 | // completion; synchronous execution does not require 'rc' (which can be set | ||
211 | // to nil). | ||
212 | //---------------------------------------------------------------------- | ||
213 | |||
214 | // ResultHandlerFcn is the function prototype for custom handlers: | ||
215 | type ResultHandlerFcn func(context.Context, *message.DHTP2PResultMsg, chan<- any) bool | ||
216 | |||
217 | // DirectResultHandler for local DHT-P2P-GET requests | ||
218 | type DirectResultHandler struct { | ||
219 | GenericResultHandler | ||
220 | |||
221 | hdlr ResultHandlerFcn // Hdlr is a custom message handler | ||
222 | rc chan any // handler result channel | ||
223 | } | ||
224 | |||
225 | // NewDirectResultHandler create a new GET handler instance | ||
226 | func NewDirectResultHandler(msgIn message.Message, rf blocks.ResultFilter, hdlr ResultHandlerFcn, rc chan any) *DirectResultHandler { | ||
227 | // check for correct message type and handler function | ||
228 | msg, ok := msgIn.(*message.DHTP2PGetMsg) | ||
229 | if ok { | ||
230 | return &DirectResultHandler{ | ||
231 | GenericResultHandler: *NewGenericResultHandler(msg, rf), | ||
232 | hdlr: hdlr, | ||
233 | rc: rc, | ||
234 | } | ||
235 | } | ||
236 | return nil | ||
237 | } | ||
238 | |||
239 | // Handle incoming DHT-P2P-RESULT message | ||
240 | func (t *DirectResultHandler) Handle(ctx context.Context, msg *message.DHTP2PResultMsg) bool { | ||
241 | // check for correct message type and handler function | ||
242 | if t.hdlr != nil { | ||
243 | logger.Printf(logger.INFO, "[dht-task-%d] handling result message", t.id) | ||
244 | return t.hdlr(ctx, msg, t.rc) | ||
245 | } | ||
246 | return false | ||
247 | } | ||
248 | |||
249 | // Compare two direct result handlers | ||
250 | func (t *DirectResultHandler) Compare(h ResultHandler) int { | ||
251 | // check for correct handler type | ||
252 | ht, ok := h.(*DirectResultHandler) | ||
253 | if !ok { | ||
254 | return RHC_DIFFER | ||
255 | } | ||
256 | // check generic handler data | ||
257 | return t.GenericResultHandler.Compare(&ht.GenericResultHandler) | ||
258 | } | ||
259 | |||
260 | // Merge two direct result handlers | ||
261 | func (t *DirectResultHandler) Merge(h ResultHandler) bool { | ||
262 | // check for correct handler type | ||
263 | ht, ok := h.(*DirectResultHandler) | ||
264 | if !ok { | ||
265 | return false | ||
266 | } | ||
267 | // check generic handler data | ||
268 | return t.GenericResultHandler.Merge(&ht.GenericResultHandler) | ||
269 | } | ||
270 | |||
271 | //---------------------------------------------------------------------- | ||
272 | // Handler list for book-keeping: | ||
273 | // * For each query/store key there can be multiple result handlers. | ||
274 | //---------------------------------------------------------------------- | ||
275 | |||
276 | // ResultHandlerList holds the currently active tasks | ||
277 | type ResultHandlerList struct { | ||
278 | list *util.Map[string, []ResultHandler] // map of handlers | ||
279 | } | ||
280 | |||
281 | // NewResultHandlerList creates a new task list | ||
282 | func NewResultHandlerList() *ResultHandlerList { | ||
283 | return &ResultHandlerList{ | ||
284 | list: util.NewMap[string, []ResultHandler](), | ||
285 | } | ||
286 | } | ||
287 | |||
288 | // Add handler to list | ||
289 | func (t *ResultHandlerList) Add(hdlr ResultHandler) bool { | ||
290 | // get current list of handlers for key | ||
291 | key := hdlr.Key() | ||
292 | list, ok := t.list.Get(key) | ||
293 | modified := false | ||
294 | if !ok { | ||
295 | list = make([]ResultHandler, 0) | ||
296 | } else { | ||
297 | // check if handler is already available | ||
298 | loop: | ||
299 | for i, h := range list { | ||
300 | switch h.Compare(hdlr) { | ||
301 | case RHC_SAME: | ||
302 | // already in list; no need to add again | ||
303 | return false | ||
304 | case RHC_MERGE: | ||
305 | // merge the two result handlers | ||
306 | modified = h.Merge(hdlr) || modified | ||
307 | break loop | ||
308 | case RHC_SIBL: | ||
309 | // replace the old handler with the new one | ||
310 | list[i] = hdlr | ||
311 | modified = true | ||
312 | break loop | ||
313 | case RHC_DIFFER: | ||
314 | // try next | ||
315 | } | ||
316 | } | ||
317 | } | ||
318 | if !modified { | ||
319 | // append new handler to list | ||
320 | list = append(list, hdlr) | ||
321 | } | ||
322 | t.list.Put(key, list) | ||
323 | return true | ||
324 | } | ||
325 | |||
326 | // Get handler list for given key | ||
327 | func (t *ResultHandlerList) Get(key string) ([]ResultHandler, bool) { | ||
328 | return t.list.Get(key) | ||
329 | } | ||
330 | |||
331 | // Cleanup removes expired tasks from list | ||
332 | func (t *ResultHandlerList) Cleanup() { | ||
333 | err := t.list.ProcessRange(func(key string, list []ResultHandler) error { | ||
334 | var newList []ResultHandler | ||
335 | changed := false | ||
336 | for _, rh := range list { | ||
337 | if !rh.Done() { | ||
338 | newList = append(newList, rh) | ||
339 | } else { | ||
340 | changed = true | ||
341 | } | ||
342 | } | ||
343 | if changed { | ||
344 | t.list.Put(key, newList) | ||
345 | } | ||
346 | return nil | ||
347 | }, false) | ||
348 | if err != nil { | ||
349 | logger.Printf(logger.ERROR, "[ResultHandlerList] clean-up error: %s", err.Error()) | ||
350 | } | ||
351 | } | ||
diff --git a/src/gnunet/service/dht/routingtable.go b/src/gnunet/service/dht/routingtable.go index 2933e6a..fe9f956 100644 --- a/src/gnunet/service/dht/routingtable.go +++ b/src/gnunet/service/dht/routingtable.go | |||
@@ -21,10 +21,11 @@ package dht | |||
21 | import ( | 21 | import ( |
22 | "bytes" | 22 | "bytes" |
23 | "context" | 23 | "context" |
24 | "crypto/sha512" | ||
25 | "encoding/hex" | 24 | "encoding/hex" |
25 | "gnunet/config" | ||
26 | "gnunet/crypto" | ||
27 | "gnunet/service/dht/blocks" | ||
26 | "gnunet/util" | 28 | "gnunet/util" |
27 | "math/rand" | ||
28 | "sync" | 29 | "sync" |
29 | "time" | 30 | "time" |
30 | 31 | ||
@@ -32,61 +33,63 @@ import ( | |||
32 | "github.com/bfix/gospel/math" | 33 | "github.com/bfix/gospel/math" |
33 | ) | 34 | ) |
34 | 35 | ||
35 | var ( | 36 | // Routing table constants |
36 | // routing table hash function: defines number of | ||
37 | // buckets and size of peer addresses | ||
38 | rtHash = sha512.New | ||
39 | ) | ||
40 | |||
41 | // Routing table contants (adjust with changing hash function) | ||
42 | const ( | 37 | const ( |
43 | numBuckets = 512 // number of bits of hash function result | 38 | numK = 20 // number of entries per k-bucket |
44 | numK = 20 // number of entries per k-bucket | ||
45 | sizeAddr = 64 // size of peer address in bytes | ||
46 | ) | 39 | ) |
47 | 40 | ||
48 | //====================================================================== | 41 | //====================================================================== |
42 | // Peer address | ||
49 | //====================================================================== | 43 | //====================================================================== |
50 | 44 | ||
51 | // PeerAddress is the identifier for a peer in the DHT network. | 45 | // PeerAddress is the identifier for a peer in the DHT network. |
52 | // It is the SHA-512 hash of the PeerID (public Ed25519 key). | 46 | // It is the SHA-512 hash of the PeerID (public Ed25519 key). |
53 | type PeerAddress struct { | 47 | type PeerAddress struct { |
54 | addr [sizeAddr]byte // hash value as bytes | 48 | Peer *util.PeerID // peer identifier |
55 | connected bool // is peer connected? | 49 | Key *crypto.HashCode // address key is a sha512 hash |
56 | lastSeen util.AbsoluteTime // time the peer was last seen | 50 | lastSeen util.AbsoluteTime // time the peer was last seen |
57 | lastUsed util.AbsoluteTime // time the peer was last used | 51 | lastUsed util.AbsoluteTime // time the peer was last used |
58 | } | 52 | } |
59 | 53 | ||
60 | // NewPeerAddress returns the DHT address of a peer. | 54 | // NewPeerAddress returns the DHT address of a peer. |
61 | func NewPeerAddress(peer *util.PeerID) *PeerAddress { | 55 | func NewPeerAddress(peer *util.PeerID) *PeerAddress { |
62 | r := new(PeerAddress) | 56 | return &PeerAddress{ |
63 | h := rtHash() | 57 | Peer: peer, |
64 | h.Write(peer.Key) | 58 | Key: crypto.Hash(peer.Data), |
65 | copy(r.addr[:], h.Sum(nil)) | 59 | lastSeen: util.AbsoluteTimeNow(), |
66 | r.lastSeen = util.AbsoluteTimeNow() | 60 | lastUsed: util.AbsoluteTimeNow(), |
67 | r.lastUsed = util.AbsoluteTimeNow() | 61 | } |
68 | return r | 62 | } |
63 | |||
64 | // NewQueryAddress returns a wrapped peer address for a query key | ||
65 | func NewQueryAddress(key *crypto.HashCode) *PeerAddress { | ||
66 | return &PeerAddress{ | ||
67 | Peer: nil, | ||
68 | Key: crypto.NewHashCode(key.Bits), | ||
69 | lastSeen: util.AbsoluteTimeNow(), | ||
70 | lastUsed: util.AbsoluteTimeNow(), | ||
71 | } | ||
69 | } | 72 | } |
70 | 73 | ||
71 | // String returns a human-readble representation of an address. | 74 | // String returns a human-readble representation of an address. |
72 | func (addr *PeerAddress) String() string { | 75 | func (addr *PeerAddress) String() string { |
73 | return hex.EncodeToString(addr.addr[:]) | 76 | return hex.EncodeToString(addr.Key.Bits) |
74 | } | 77 | } |
75 | 78 | ||
76 | // Equals returns true if two peer addresses are the same. | 79 | // Equals returns true if two peer addresses are the same. |
77 | func (addr *PeerAddress) Equals(p *PeerAddress) bool { | 80 | func (addr *PeerAddress) Equals(p *PeerAddress) bool { |
78 | return bytes.Equal(addr.addr[:], p.addr[:]) | 81 | return bytes.Equal(addr.Key.Bits, p.Key.Bits) |
79 | } | 82 | } |
80 | 83 | ||
81 | // Distance between two addresses: returns a distance value and a | 84 | // Distance between two addresses: returns a distance value and a |
82 | // bucket index (smaller index = less distant). | 85 | // bucket index (smaller index = less distant). |
83 | func (addr *PeerAddress) Distance(p *PeerAddress) (*math.Int, int) { | 86 | func (addr *PeerAddress) Distance(p *PeerAddress) (*math.Int, int) { |
84 | var d PeerAddress | 87 | d := make([]byte, 64) |
85 | for i := range d.addr { | 88 | for i := range d { |
86 | d.addr[i] = addr.addr[i] ^ p.addr[i] | 89 | d[i] = addr.Key.Bits[i] ^ p.Key.Bits[i] |
87 | } | 90 | } |
88 | r := math.NewIntFromBytes(d.addr[:]) | 91 | r := math.NewIntFromBytes(d) |
89 | return r, numBuckets - r.BitLen() | 92 | return r, 512 - r.BitLen() |
90 | } | 93 | } |
91 | 94 | ||
92 | //====================================================================== | 95 | //====================================================================== |
@@ -98,23 +101,28 @@ func (addr *PeerAddress) Distance(p *PeerAddress) (*math.Int, int) { | |||
98 | // distance to the reference address, so smaller index means | 101 | // distance to the reference address, so smaller index means |
99 | // "nearer" to the reference address. | 102 | // "nearer" to the reference address. |
100 | type RoutingTable struct { | 103 | type RoutingTable struct { |
101 | ref *PeerAddress // reference address for distance | 104 | sync.RWMutex |
102 | buckets []*Bucket // list of buckets | 105 | |
103 | list map[*PeerAddress]struct{} // keep list of peers | 106 | ref *PeerAddress // reference address for distance |
104 | mtx sync.RWMutex // lock for write operations | 107 | buckets []*Bucket // list of buckets |
105 | l2nse float64 // log2 of estimated network size | 108 | list *util.Map[string, *PeerAddress] // keep list of peers |
106 | inProcess bool // flag if Process() is running | 109 | l2nse float64 // log2 of estimated network size |
110 | inProcess bool // flag if Process() is running | ||
111 | cfg *config.RoutingConfig // routing parameters | ||
112 | helloCache *util.Map[string, *blocks.HelloBlock] // HELLO block cache | ||
107 | } | 113 | } |
108 | 114 | ||
109 | // NewRoutingTable creates a new routing table for the reference address. | 115 | // NewRoutingTable creates a new routing table for the reference address. |
110 | func NewRoutingTable(ref *PeerAddress) *RoutingTable { | 116 | func NewRoutingTable(ref *PeerAddress, cfg *config.RoutingConfig) *RoutingTable { |
111 | // create routing table | 117 | // create routing table |
112 | rt := &RoutingTable{ | 118 | rt := &RoutingTable{ |
113 | ref: ref, | 119 | ref: ref, |
114 | list: make(map[*PeerAddress]struct{}), | 120 | list: util.NewMap[string, *PeerAddress](), |
115 | buckets: make([]*Bucket, numBuckets), | 121 | buckets: make([]*Bucket, 512), |
116 | l2nse: 0., | 122 | l2nse: -1, |
117 | inProcess: false, | 123 | inProcess: false, |
124 | cfg: cfg, | ||
125 | helloCache: util.NewMap[string, *blocks.HelloBlock](), | ||
118 | } | 126 | } |
119 | // fill buckets | 127 | // fill buckets |
120 | for i := range rt.buckets { | 128 | for i := range rt.buckets { |
@@ -130,41 +138,69 @@ func NewRoutingTable(ref *PeerAddress) *RoutingTable { | |||
130 | // Add new peer address to routing table. | 138 | // Add new peer address to routing table. |
131 | // Returns true if the entry was added, false otherwise. | 139 | // Returns true if the entry was added, false otherwise. |
132 | func (rt *RoutingTable) Add(p *PeerAddress) bool { | 140 | func (rt *RoutingTable) Add(p *PeerAddress) bool { |
133 | // ensure one write and no readers | 141 | k := p.String() |
134 | rt.lock(false) | 142 | logger.Printf(logger.DBG, "[RT] Add(%s)", k) |
135 | defer rt.unlock(false) | ||
136 | 143 | ||
137 | // check if peer is already known | 144 | // check if peer is already known |
138 | if _, ok := rt.list[p]; ok { | 145 | if px, ok := rt.list.Get(k); ok { |
146 | logger.Println(logger.DBG, "[RT] --> already known") | ||
147 | px.lastSeen = util.AbsoluteTimeNow() | ||
139 | return false | 148 | return false |
140 | } | 149 | } |
141 | 150 | ||
142 | // compute distance (bucket index) and insert address. | 151 | // compute distance (bucket index) and insert address. |
143 | _, idx := p.Distance(rt.ref) | 152 | _, idx := p.Distance(rt.ref) |
144 | if rt.buckets[idx].Add(p) { | 153 | if rt.buckets[idx].Add(p) { |
145 | rt.list[p] = struct{}{} | 154 | logger.Println(logger.DBG, "[RT] --> entry added") |
155 | p.lastUsed = util.AbsoluteTimeNow() | ||
156 | rt.list.Put(k, p) | ||
146 | return true | 157 | return true |
147 | } | 158 | } |
148 | // Full bucket: we did not add the address to the routing table. | 159 | // Full bucket: we did not add the address to the routing table. |
160 | logger.Println(logger.DBG, "[RT] --> bucket full -- discarded") | ||
149 | return false | 161 | return false |
150 | } | 162 | } |
151 | 163 | ||
152 | // Remove peer address from routing table. | 164 | // Remove peer address from routing table. |
153 | // Returns true if the entry was removed, false otherwise. | 165 | // Returns true if the entry was removed, false otherwise. |
154 | func (rt *RoutingTable) Remove(p *PeerAddress) bool { | 166 | func (rt *RoutingTable) Remove(p *PeerAddress) bool { |
155 | // ensure one write and no readers | 167 | k := p.String() |
156 | rt.lock(false) | 168 | logger.Printf(logger.DBG, "[RT] Remove(%s)", k) |
157 | defer rt.unlock(false) | ||
158 | 169 | ||
159 | // compute distance (bucket index) and remove entry from bucket | 170 | // compute distance (bucket index) and remove entry from bucket |
171 | rc := false | ||
160 | _, idx := p.Distance(rt.ref) | 172 | _, idx := p.Distance(rt.ref) |
161 | if rt.buckets[idx].Remove(p) { | 173 | if rt.buckets[idx].Remove(p) { |
162 | delete(rt.list, p) | 174 | logger.Println(logger.DBG, "[RT] --> entry removed from bucket and internal lists") |
163 | return true | 175 | rc = true |
176 | } else { | ||
177 | // remove from internal list | ||
178 | logger.Println(logger.DBG, "[RT] --> entry removed from internal lists only") | ||
164 | } | 179 | } |
165 | // remove from internal list | 180 | rt.list.Delete(k) |
166 | delete(rt.list, p) | 181 | // delete from HELLO cache |
167 | return false | 182 | rt.helloCache.Delete(p.Peer.String()) |
183 | return rc | ||
184 | } | ||
185 | |||
186 | // Contains checks if a peer is available in the routing table | ||
187 | func (rt *RoutingTable) Contains(p *PeerAddress) bool { | ||
188 | k := p.String() | ||
189 | logger.Printf(logger.DBG, "[RT] Contains(%s)?", k) | ||
190 | |||
191 | // check for peer in internal list | ||
192 | px, ok := rt.list.Get(k) | ||
193 | if !ok { | ||
194 | logger.Println(logger.DBG, "[RT] --> NOT found in current list:") | ||
195 | _ = rt.list.ProcessRange(func(key string, val *PeerAddress) error { | ||
196 | logger.Printf(logger.DBG, "[RT] * %s", val) | ||
197 | return nil | ||
198 | }, true) | ||
199 | } else { | ||
200 | logger.Println(logger.DBG, "[RT] --> found in current list") | ||
201 | px.lastSeen = util.AbsoluteTimeNow() | ||
202 | } | ||
203 | return ok | ||
168 | } | 204 | } |
169 | 205 | ||
170 | //---------------------------------------------------------------------- | 206 | //---------------------------------------------------------------------- |
@@ -186,51 +222,53 @@ func (rt *RoutingTable) Process(f func() error, readonly bool) error { | |||
186 | // Routing functions | 222 | // Routing functions |
187 | //---------------------------------------------------------------------- | 223 | //---------------------------------------------------------------------- |
188 | 224 | ||
189 | // SelectClosestPeer for a given peer address and bloomfilter. | 225 | // SelectClosestPeer for a given peer address and peer filter. |
190 | func (rt *RoutingTable) SelectClosestPeer(p *PeerAddress, bf *PeerBloomFilter) (n *PeerAddress) { | 226 | func (rt *RoutingTable) SelectClosestPeer(p *PeerAddress, pf *blocks.PeerFilter) (n *PeerAddress) { |
191 | // no writer allowed | 227 | // no writer allowed |
192 | rt.mtx.RLock() | 228 | rt.RLock() |
193 | defer rt.mtx.RUnlock() | 229 | defer rt.RUnlock() |
194 | 230 | ||
195 | // find closest address | 231 | // find closest peer in routing table |
196 | var dist *math.Int | 232 | var dist *math.Int |
197 | for _, b := range rt.buckets { | 233 | for _, b := range rt.buckets { |
198 | if k, d := b.SelectClosestPeer(p, bf); n == nil || (d != nil && d.Cmp(dist) < 0) { | 234 | if k, d := b.SelectClosestPeer(p, pf); n == nil || (d != nil && d.Cmp(dist) < 0) { |
199 | dist = d | 235 | dist = d |
200 | n = k | 236 | n = k |
201 | } | 237 | } |
202 | } | 238 | } |
203 | // mark peer as used | 239 | // mark peer as used |
204 | n.lastUsed = util.AbsoluteTimeNow() | 240 | if n != nil { |
241 | n.lastUsed = util.AbsoluteTimeNow() | ||
242 | } | ||
205 | return | 243 | return |
206 | } | 244 | } |
207 | 245 | ||
208 | // SelectRandomPeer returns a random address from table (that is not | 246 | // SelectRandomPeer returns a random address from table (that is not |
209 | // included in the bloomfilter) | 247 | // included in the bloomfilter) |
210 | func (rt *RoutingTable) SelectRandomPeer(bf *PeerBloomFilter) *PeerAddress { | 248 | func (rt *RoutingTable) SelectRandomPeer(pf *blocks.PeerFilter) (p *PeerAddress) { |
211 | // no writer allowed | 249 | // no writer allowed |
212 | rt.mtx.RLock() | 250 | rt.RLock() |
213 | defer rt.mtx.RUnlock() | 251 | defer rt.RUnlock() |
214 | 252 | ||
215 | // select random entry from list | 253 | // select random entry from list |
216 | if size := len(rt.list); size > 0 { | 254 | var ok bool |
217 | idx := rand.Intn(size) | 255 | for { |
218 | for k := range rt.list { | 256 | if _, p, ok = rt.list.GetRandom(); !ok { |
219 | if idx == 0 { | 257 | return nil |
220 | // mark peer as used | 258 | } |
221 | k.lastUsed = util.AbsoluteTimeNow() | 259 | if !pf.Contains(p.Peer) { |
222 | return k | 260 | break |
223 | } | ||
224 | idx-- | ||
225 | } | 261 | } |
226 | } | 262 | } |
227 | return nil | 263 | // mark peer as used |
264 | p.lastUsed = util.AbsoluteTimeNow() | ||
265 | return | ||
228 | } | 266 | } |
229 | 267 | ||
230 | // SelectPeer selects a neighbor depending on the number of hops parameter. | 268 | // SelectPeer selects a neighbor depending on the number of hops parameter. |
231 | // If hops < NSE this function MUST return SelectRandomPeer() and | 269 | // If hops < NSE this function MUST return SelectRandomPeer() and |
232 | // SelectClosestpeer() otherwise. | 270 | // SelectClosestpeer() otherwise. |
233 | func (rt *RoutingTable) SelectPeer(p *PeerAddress, hops int, bf *PeerBloomFilter) *PeerAddress { | 271 | func (rt *RoutingTable) SelectPeer(p *PeerAddress, hops int, bf *blocks.PeerFilter) *PeerAddress { |
234 | if float64(hops) < rt.l2nse { | 272 | if float64(hops) < rt.l2nse { |
235 | return rt.SelectRandomPeer(bf) | 273 | return rt.SelectRandomPeer(bf) |
236 | } | 274 | } |
@@ -238,9 +276,24 @@ func (rt *RoutingTable) SelectPeer(p *PeerAddress, hops int, bf *PeerBloomFilter | |||
238 | } | 276 | } |
239 | 277 | ||
240 | // IsClosestPeer returns true if p is the closest peer for k. Peers with a | 278 | // IsClosestPeer returns true if p is the closest peer for k. Peers with a |
241 | // positive test in the Bloom filter are not considered. | 279 | // positive test in the Bloom filter are not considered. If p is nil, our |
242 | func (rt *RoutingTable) IsClosestPeer(p, k *PeerAddress, bf *PeerBloomFilter) bool { | 280 | // reference address is used. |
243 | n := rt.SelectClosestPeer(k, bf) | 281 | func (rt *RoutingTable) IsClosestPeer(p, k *PeerAddress, pf *blocks.PeerFilter) bool { |
282 | // get closest peer in routing table | ||
283 | n := rt.SelectClosestPeer(k, pf) | ||
284 | // check SELF? | ||
285 | if p == nil { | ||
286 | // if no peer in routing table found | ||
287 | if n == nil { | ||
288 | // local peer is closest | ||
289 | return true | ||
290 | } | ||
291 | // check if local distance is smaller than for best peer in routing table | ||
292 | d0, _ := n.Distance(k) | ||
293 | d1, _ := rt.ref.Distance(k) | ||
294 | return d1.Cmp(d0) < 0 | ||
295 | } | ||
296 | // check if p is closest peer | ||
244 | return n.Equals(p) | 297 | return n.Equals(p) |
245 | } | 298 | } |
246 | 299 | ||
@@ -248,7 +301,7 @@ func (rt *RoutingTable) IsClosestPeer(p, k *PeerAddress, bf *PeerBloomFilter) bo | |||
248 | // The arguments are the desired replication level, the hop count of the message so far, | 301 | // The arguments are the desired replication level, the hop count of the message so far, |
249 | // and the base-2 logarithm of the current network size estimate (L2NSE) as provided by the | 302 | // and the base-2 logarithm of the current network size estimate (L2NSE) as provided by the |
250 | // underlay. The result is the non-negative number of next hops to select. | 303 | // underlay. The result is the non-negative number of next hops to select. |
251 | func (rt *RoutingTable) ComputeOutDegree(repl, hop int) int { | 304 | func (rt *RoutingTable) ComputeOutDegree(repl, hop uint16) int { |
252 | hf := float64(hop) | 305 | hf := float64(hop) |
253 | if hf > 4*rt.l2nse { | 306 | if hf > 4*rt.l2nse { |
254 | return 0 | 307 | return 0 |
@@ -271,22 +324,62 @@ func (rt *RoutingTable) ComputeOutDegree(repl, hop int) int { | |||
271 | func (rt *RoutingTable) heartbeat(ctx context.Context) { | 324 | func (rt *RoutingTable) heartbeat(ctx context.Context) { |
272 | 325 | ||
273 | // check for dead or expired peers | 326 | // check for dead or expired peers |
274 | timeout := util.NewRelativeTime(3 * time.Hour) | 327 | logger.Println(logger.DBG, "[dht] RT heartbeat...") |
328 | timeout := util.NewRelativeTime(time.Duration(rt.cfg.PeerTTL) * time.Second) | ||
275 | if err := rt.Process(func() error { | 329 | if err := rt.Process(func() error { |
276 | for addr := range rt.list { | 330 | return rt.list.ProcessRange(func(k string, p *PeerAddress) error { |
277 | if addr.connected { | ||
278 | continue | ||
279 | } | ||
280 | // check if we can/need to drop a peer | 331 | // check if we can/need to drop a peer |
281 | drop := timeout.Compare(addr.lastSeen.Elapsed()) < 0 | 332 | drop := timeout.Compare(p.lastSeen.Elapsed()) < 0 |
282 | if drop || timeout.Compare(addr.lastUsed.Elapsed()) < 0 { | 333 | if drop || timeout.Compare(p.lastUsed.Elapsed()) < 0 { |
283 | rt.Remove(addr) | 334 | logger.Printf(logger.DBG, "[RT] removing %v: %v, %v", p, p.lastSeen.Elapsed(), p.lastUsed.Elapsed()) |
335 | rt.Remove(p) | ||
284 | } | 336 | } |
285 | } | 337 | return nil |
286 | return nil | 338 | }, false) |
287 | }, false); err != nil { | 339 | }, false); err != nil { |
288 | logger.Println(logger.ERROR, "[dht] RT heartbeat: "+err.Error()) | 340 | logger.Println(logger.ERROR, "[dht] RT heartbeat failed: "+err.Error()) |
289 | } | 341 | } |
342 | |||
343 | // drop expired entries from the HELLO cache | ||
344 | _ = rt.helloCache.ProcessRange(func(key string, val *blocks.HelloBlock) error { | ||
345 | if val.Expires.Expired() { | ||
346 | rt.helloCache.Delete(key) | ||
347 | } | ||
348 | return nil | ||
349 | }, false) | ||
350 | |||
351 | // update the estimated network size | ||
352 | // rt.l2nse = ... | ||
353 | } | ||
354 | |||
355 | //---------------------------------------------------------------------- | ||
356 | |||
357 | func (rt *RoutingTable) BestHello(addr *PeerAddress, rf blocks.ResultFilter) (hb *blocks.HelloBlock, dist *math.Int) { | ||
358 | // iterate over cached HELLOs to find (best) match first | ||
359 | _ = rt.helloCache.ProcessRange(func(key string, val *blocks.HelloBlock) error { | ||
360 | // check if block is excluded by result filter | ||
361 | if !rf.Contains(val) { | ||
362 | // check for better match | ||
363 | p := NewPeerAddress(val.PeerID) | ||
364 | d, _ := addr.Distance(p) | ||
365 | if hb == nil || d.Cmp(dist) < 0 { | ||
366 | hb = val | ||
367 | dist = d | ||
368 | } | ||
369 | } | ||
370 | return nil | ||
371 | }, true) | ||
372 | return | ||
373 | } | ||
374 | |||
375 | // CacheHello adds a HELLO block to the list of cached entries. | ||
376 | func (rt *RoutingTable) CacheHello(hb *blocks.HelloBlock) { | ||
377 | rt.helloCache.Put(hb.PeerID.String(), hb) | ||
378 | } | ||
379 | |||
380 | // GetHello returns a HELLO block for key k (if available) | ||
381 | func (rt *RoutingTable) GetHello(k string) (*blocks.HelloBlock, bool) { | ||
382 | return rt.helloCache.Get(k) | ||
290 | } | 383 | } |
291 | 384 | ||
292 | //---------------------------------------------------------------------- | 385 | //---------------------------------------------------------------------- |
@@ -295,9 +388,9 @@ func (rt *RoutingTable) heartbeat(ctx context.Context) { | |||
295 | func (rt *RoutingTable) lock(readonly bool) { | 388 | func (rt *RoutingTable) lock(readonly bool) { |
296 | if !rt.inProcess { | 389 | if !rt.inProcess { |
297 | if readonly { | 390 | if readonly { |
298 | rt.mtx.RLock() | 391 | rt.RLock() |
299 | } else { | 392 | } else { |
300 | rt.mtx.Lock() | 393 | rt.Lock() |
301 | } | 394 | } |
302 | } | 395 | } |
303 | } | 396 | } |
@@ -306,9 +399,9 @@ func (rt *RoutingTable) lock(readonly bool) { | |||
306 | func (rt *RoutingTable) unlock(readonly bool) { | 399 | func (rt *RoutingTable) unlock(readonly bool) { |
307 | if !rt.inProcess { | 400 | if !rt.inProcess { |
308 | if readonly { | 401 | if readonly { |
309 | rt.mtx.RUnlock() | 402 | rt.RUnlock() |
310 | } else { | 403 | } else { |
311 | rt.mtx.Unlock() | 404 | rt.Unlock() |
312 | } | 405 | } |
313 | } | 406 | } |
314 | } | 407 | } |
@@ -319,8 +412,9 @@ func (rt *RoutingTable) unlock(readonly bool) { | |||
319 | 412 | ||
320 | // Bucket holds peer entries with approx. same distance from node | 413 | // Bucket holds peer entries with approx. same distance from node |
321 | type Bucket struct { | 414 | type Bucket struct { |
322 | list []*PeerAddress | 415 | sync.RWMutex |
323 | rwlock sync.RWMutex | 416 | |
417 | list []*PeerAddress // list of peer addresses in bucket. | ||
324 | } | 418 | } |
325 | 419 | ||
326 | // NewBucket creates a new entry list of given size | 420 | // NewBucket creates a new entry list of given size |
@@ -334,8 +428,8 @@ func NewBucket(n int) *Bucket { | |||
334 | // Returns true if entry is added, false otherwise. | 428 | // Returns true if entry is added, false otherwise. |
335 | func (b *Bucket) Add(p *PeerAddress) bool { | 429 | func (b *Bucket) Add(p *PeerAddress) bool { |
336 | // only one writer and no readers | 430 | // only one writer and no readers |
337 | b.rwlock.Lock() | 431 | b.Lock() |
338 | defer b.rwlock.Unlock() | 432 | defer b.Unlock() |
339 | 433 | ||
340 | // check for free space in bucket | 434 | // check for free space in bucket |
341 | if len(b.list) < numK { | 435 | if len(b.list) < numK { |
@@ -343,6 +437,7 @@ func (b *Bucket) Add(p *PeerAddress) bool { | |||
343 | b.list = append(b.list, p) | 437 | b.list = append(b.list, p) |
344 | return true | 438 | return true |
345 | } | 439 | } |
440 | // full bucket: no further additions | ||
346 | return false | 441 | return false |
347 | } | 442 | } |
348 | 443 | ||
@@ -350,8 +445,8 @@ func (b *Bucket) Add(p *PeerAddress) bool { | |||
350 | // Returns true if entry is removed (found), false otherwise. | 445 | // Returns true if entry is removed (found), false otherwise. |
351 | func (b *Bucket) Remove(p *PeerAddress) bool { | 446 | func (b *Bucket) Remove(p *PeerAddress) bool { |
352 | // only one writer and no readers | 447 | // only one writer and no readers |
353 | b.rwlock.Lock() | 448 | b.Lock() |
354 | defer b.rwlock.Unlock() | 449 | defer b.Unlock() |
355 | 450 | ||
356 | for i, pe := range b.list { | 451 | for i, pe := range b.list { |
357 | if pe.Equals(p) { | 452 | if pe.Equals(p) { |
@@ -365,14 +460,14 @@ func (b *Bucket) Remove(p *PeerAddress) bool { | |||
365 | 460 | ||
366 | // SelectClosestPeer returns the entry with minimal distance to the given | 461 | // SelectClosestPeer returns the entry with minimal distance to the given |
367 | // peer address; entries included in the bloom flter are ignored. | 462 | // peer address; entries included in the bloom flter are ignored. |
368 | func (b *Bucket) SelectClosestPeer(p *PeerAddress, bf *PeerBloomFilter) (n *PeerAddress, dist *math.Int) { | 463 | func (b *Bucket) SelectClosestPeer(p *PeerAddress, pf *blocks.PeerFilter) (n *PeerAddress, dist *math.Int) { |
369 | // no writer allowed | 464 | // no writer allowed |
370 | b.rwlock.RLock() | 465 | b.RLock() |
371 | defer b.rwlock.RUnlock() | 466 | defer b.RUnlock() |
372 | 467 | ||
373 | for _, addr := range b.list { | 468 | for _, addr := range b.list { |
374 | // skip addresses in bloomfilter | 469 | // skip addresses in bloomfilter |
375 | if bf.Contains(addr) { | 470 | if pf.Contains(addr.Peer) { |
376 | continue | 471 | continue |
377 | } | 472 | } |
378 | // check for shorter distance | 473 | // check for shorter distance |
diff --git a/src/gnunet/service/dht/routingtable_test.go b/src/gnunet/service/dht/routingtable_test.go index 33c4b7f..16f39de 100644 --- a/src/gnunet/service/dht/routingtable_test.go +++ b/src/gnunet/service/dht/routingtable_test.go | |||
@@ -19,8 +19,11 @@ | |||
19 | package dht | 19 | package dht |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "crypto/sha512" | ||
23 | "encoding/hex" | ||
22 | "gnunet/config" | 24 | "gnunet/config" |
23 | "gnunet/core" | 25 | "gnunet/core" |
26 | "gnunet/service/dht/blocks" | ||
24 | "gnunet/util" | 27 | "gnunet/util" |
25 | "math/rand" | 28 | "math/rand" |
26 | "testing" | 29 | "testing" |
@@ -43,7 +46,7 @@ type Entry struct { | |||
43 | 46 | ||
44 | // test data | 47 | // test data |
45 | var ( | 48 | var ( |
46 | cfg = &config.NodeConfig{ | 49 | nodeCfg = &config.NodeConfig{ |
47 | PrivateSeed: "YGoe6XFH3XdvFRl+agx9gIzPTvxA229WFdkazEMdcOs=", | 50 | PrivateSeed: "YGoe6XFH3XdvFRl+agx9gIzPTvxA229WFdkazEMdcOs=", |
48 | Endpoints: []*config.EndpointConfig{ | 51 | Endpoints: []*config.EndpointConfig{ |
49 | { | 52 | { |
@@ -53,6 +56,9 @@ var ( | |||
53 | }, | 56 | }, |
54 | }, | 57 | }, |
55 | } | 58 | } |
59 | rtCfg = &config.RoutingConfig{ | ||
60 | PeerTTL: 10800, | ||
61 | } | ||
56 | ) | 62 | ) |
57 | 63 | ||
58 | // TestRT connects and disconnects random peers to test the base | 64 | // TestRT connects and disconnects random peers to test the base |
@@ -64,18 +70,16 @@ func TestRT(t *testing.T) { | |||
64 | // helper functions | 70 | // helper functions |
65 | genRemotePeer := func() *PeerAddress { | 71 | genRemotePeer := func() *PeerAddress { |
66 | d := make([]byte, 32) | 72 | d := make([]byte, 32) |
67 | if _, err := rand.Read(d); err != nil { | 73 | _, _ = rand.Read(d) |
68 | panic(err) | ||
69 | } | ||
70 | return NewPeerAddress(util.NewPeerID(d)) | 74 | return NewPeerAddress(util.NewPeerID(d)) |
71 | } | 75 | } |
72 | 76 | ||
73 | // create routing table and start command handler | 77 | // create routing table and start command handler |
74 | local, err := core.NewLocalPeer(cfg) | 78 | local, err := core.NewLocalPeer(nodeCfg) |
75 | if err != nil { | 79 | if err != nil { |
76 | t.Fatal(err) | 80 | t.Fatal(err) |
77 | } | 81 | } |
78 | rt := NewRoutingTable(NewPeerAddress(local.GetID())) | 82 | rt := NewRoutingTable(NewPeerAddress(local.GetID()), rtCfg) |
79 | 83 | ||
80 | // create a task list | 84 | // create a task list |
81 | tasks := make([]*Entry, NUMP) | 85 | tasks := make([]*Entry, NUMP) |
@@ -91,7 +95,6 @@ func TestRT(t *testing.T) { | |||
91 | 95 | ||
92 | // actions: | 96 | // actions: |
93 | connected := func(task *Entry, e int64, msg string) { | 97 | connected := func(task *Entry, e int64, msg string) { |
94 | task.addr.connected = true | ||
95 | rt.Add(task.addr) | 98 | rt.Add(task.addr) |
96 | task.online = true | 99 | task.online = true |
97 | task.last = e | 100 | task.last = e |
@@ -136,10 +139,29 @@ func TestRT(t *testing.T) { | |||
136 | 139 | ||
137 | // execute some routing functions on remaining table | 140 | // execute some routing functions on remaining table |
138 | k := genRemotePeer() | 141 | k := genRemotePeer() |
139 | bf := NewPeerBloomFilter() | 142 | pf := blocks.NewPeerFilter() |
140 | n := rt.SelectClosestPeer(k, bf) | 143 | n := rt.SelectClosestPeer(k, pf) |
141 | t.Logf("Closest: %s -> %s\n", k, n) | 144 | t.Logf("Closest: %s -> %s\n", k, n) |
142 | 145 | ||
143 | n = rt.SelectRandomPeer(bf) | 146 | n = rt.SelectRandomPeer(pf) |
144 | t.Logf("Random: %s\n", n) | 147 | t.Logf("Random: %s\n", n) |
145 | } | 148 | } |
149 | |||
150 | func TestDistance(t *testing.T) { | ||
151 | pid1 := "4ER9C0GV4QC25GGQMXBBGXYFEB3ZVAYMXZVSRKDVEGCDTAS34E30" | ||
152 | pid2 := "V61ESQ96AFXZWDSA509HP11K5HJXXJ9ECM4NAMCQRX5YW4KN8XPG" | ||
153 | |||
154 | p1, _ := util.DecodeStringToBinary(pid1, 32) | ||
155 | p2, _ := util.DecodeStringToBinary(pid2, 32) | ||
156 | |||
157 | h1 := sha512.Sum512(p1) | ||
158 | h2 := sha512.Sum512(p2) | ||
159 | t.Logf("h1=%s\n", hex.EncodeToString(h1[:])) | ||
160 | t.Logf("h2=%s\n", hex.EncodeToString(h2[:])) | ||
161 | |||
162 | pa1 := NewPeerAddress(util.NewPeerID(p1)) | ||
163 | pa2 := NewPeerAddress(util.NewPeerID(p2)) | ||
164 | |||
165 | dist, idx := pa1.Distance(pa2) | ||
166 | t.Logf("dist=%v, idx=%d\n", dist, idx) | ||
167 | } | ||
diff --git a/src/gnunet/service/dht/rpc.go b/src/gnunet/service/dht/rpc.go index 3ec2b73..96ae26b 100644 --- a/src/gnunet/service/dht/rpc.go +++ b/src/gnunet/service/dht/rpc.go | |||
@@ -19,21 +19,49 @@ | |||
19 | package dht | 19 | package dht |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "net/rpc" | 22 | "gnunet/service" |
23 | "time" | 23 | "net/http" |
24 | |||
25 | "github.com/bfix/gospel/logger" | ||
24 | ) | 26 | ) |
25 | 27 | ||
26 | //---------------------------------------------------------------------- | 28 | //---------------------------------------------------------------------- |
27 | 29 | ||
28 | type DHTCommand struct{} | 30 | // RPCService is a type for DHT-related JSON-RPC requests |
31 | type RPCService struct{} | ||
32 | |||
33 | // local instance of service | ||
34 | var dhtRPC = &RPCService{} | ||
35 | |||
36 | //---------------------------------------------------------------------- | ||
37 | // Command "DHT.Status" | ||
38 | //---------------------------------------------------------------------- | ||
29 | 39 | ||
30 | type DHTStats struct { | 40 | // StatusRequest is a status request for specific information addressed |
31 | Started time.Time | 41 | // by topic(s) |
42 | type StatusRequest struct { | ||
43 | Topics []string `json:"topics"` | ||
32 | } | 44 | } |
33 | 45 | ||
34 | func (c *DHTCommand) Status(mode int, stats *DHTStats) error { | 46 | // StatusResponse is a response to a status request. It returns information |
35 | *stats = DHTStats{ | 47 | // on each topic requested. |
36 | Started: time.Now(), | 48 | type StatusResponse struct { |
49 | Messages map[string]string `json:"messages"` | ||
50 | } | ||
51 | |||
52 | // Status requests information by topic(s). | ||
53 | func (s *RPCService) Status(r *http.Request, req *StatusRequest, reply *StatusResponse) error { | ||
54 | // assemble information on topic(s) | ||
55 | out := make(map[string]string) | ||
56 | for _, topic := range req.Topics { | ||
57 | switch topic { | ||
58 | case "echo": | ||
59 | out[topic] = "echo test" | ||
60 | } | ||
61 | } | ||
62 | // set reply | ||
63 | *reply = StatusResponse{ | ||
64 | Messages: out, | ||
37 | } | 65 | } |
38 | return nil | 66 | return nil |
39 | } | 67 | } |
@@ -41,6 +69,8 @@ func (c *DHTCommand) Status(mode int, stats *DHTStats) error { | |||
41 | //---------------------------------------------------------------------- | 69 | //---------------------------------------------------------------------- |
42 | 70 | ||
43 | // InitRPC registers RPC commands for the module | 71 | // InitRPC registers RPC commands for the module |
44 | func (m *Module) InitRPC(srv *rpc.Server) { | 72 | func (m *Module) InitRPC(srv *service.JRPCServer) { |
45 | srv.Register(new(DHTCommand)) | 73 | if err := srv.RegisterService(dhtRPC, "DHT"); err != nil { |
74 | logger.Printf(logger.ERROR, "[dht] Failed to init RPC: %s", err.Error()) | ||
75 | } | ||
46 | } | 76 | } |
diff --git a/src/gnunet/service/dht/service.go b/src/gnunet/service/dht/service.go index 3cf216f..29e3804 100644 --- a/src/gnunet/service/dht/service.go +++ b/src/gnunet/service/dht/service.go | |||
@@ -23,10 +23,9 @@ import ( | |||
23 | "fmt" | 23 | "fmt" |
24 | "io" | 24 | "io" |
25 | 25 | ||
26 | "gnunet/config" | ||
26 | "gnunet/core" | 27 | "gnunet/core" |
27 | "gnunet/message" | ||
28 | "gnunet/service" | 28 | "gnunet/service" |
29 | "gnunet/transport" | ||
30 | 29 | ||
31 | "github.com/bfix/gospel/logger" | 30 | "github.com/bfix/gospel/logger" |
32 | ) | 31 | ) |
@@ -48,15 +47,14 @@ type Service struct { | |||
48 | } | 47 | } |
49 | 48 | ||
50 | // NewService creates a new DHT service instance | 49 | // NewService creates a new DHT service instance |
51 | func NewService(ctx context.Context, c *core.Core) (service.Service, error) { | 50 | func NewService(ctx context.Context, c *core.Core, cfg *config.DHTConfig) (*Service, error) { |
52 | mod, err := NewModule(ctx, c) | 51 | mod, err := NewModule(ctx, c, cfg) |
53 | if err != nil { | 52 | if err != nil { |
54 | return nil, err | 53 | return nil, err |
55 | } | 54 | } |
56 | srv := &Service{ | 55 | srv := &Service{ |
57 | Module: *mod, | 56 | Module: *mod, |
58 | } | 57 | } |
59 | srv.ProcessFcn = srv.HandleMessage | ||
60 | return srv, nil | 58 | return srv, nil |
61 | } | 59 | } |
62 | 60 | ||
@@ -85,7 +83,8 @@ loop: | |||
85 | logger.Printf(logger.INFO, "[dht:%d:%d] Received request: %v\n", id, reqID, msg) | 83 | logger.Printf(logger.INFO, "[dht:%d:%d] Received request: %v\n", id, reqID, msg) |
86 | 84 | ||
87 | // handle message | 85 | // handle message |
88 | s.HandleMessage(context.WithValue(ctx, "label", fmt.Sprintf(":%d:%d", id, reqID)), msg, mc) | 86 | valueCtx := context.WithValue(ctx, service.CtxKey("label"), fmt.Sprintf(":%d:%d", id, reqID)) |
87 | s.HandleMessage(valueCtx, nil, msg, mc) | ||
89 | } | 88 | } |
90 | // close client connection | 89 | // close client connection |
91 | mc.Close() | 90 | mc.Close() |
@@ -94,48 +93,3 @@ loop: | |||
94 | logger.Printf(logger.INFO, "[dht:%d] Start closing session...\n", id) | 93 | logger.Printf(logger.INFO, "[dht:%d] Start closing session...\n", id) |
95 | cancel() | 94 | cancel() |
96 | } | 95 | } |
97 | |||
98 | // HandleMessage handles a DHT request/response message. If the transport channel | ||
99 | // is nil, responses are send directly via the transport layer. | ||
100 | func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back transport.Responder) bool { | ||
101 | // assemble log label | ||
102 | label := "" | ||
103 | if v := ctx.Value("label"); v != nil { | ||
104 | label = v.(string) | ||
105 | } | ||
106 | // process message | ||
107 | switch msg.(type) { | ||
108 | case *message.DHTClientPutMsg: | ||
109 | //---------------------------------------------------------- | ||
110 | // DHT PUT | ||
111 | //---------------------------------------------------------- | ||
112 | |||
113 | case *message.DHTClientGetMsg: | ||
114 | //---------------------------------------------------------- | ||
115 | // DHT GET | ||
116 | //---------------------------------------------------------- | ||
117 | |||
118 | case *message.DHTClientGetResultsKnownMsg: | ||
119 | //---------------------------------------------------------- | ||
120 | // DHT GET-RESULTS-KNOWN | ||
121 | //---------------------------------------------------------- | ||
122 | |||
123 | case *message.DHTClientGetStopMsg: | ||
124 | //---------------------------------------------------------- | ||
125 | // DHT GET-STOP | ||
126 | //---------------------------------------------------------- | ||
127 | |||
128 | case *message.DHTClientResultMsg: | ||
129 | //---------------------------------------------------------- | ||
130 | // DHT RESULT | ||
131 | //---------------------------------------------------------- | ||
132 | |||
133 | default: | ||
134 | //---------------------------------------------------------- | ||
135 | // UNKNOWN message type received | ||
136 | //---------------------------------------------------------- | ||
137 | logger.Printf(logger.ERROR, "[dht-%s] Unhandled message of type (%d)\n", label, msg.Header().MsgType) | ||
138 | return false | ||
139 | } | ||
140 | return true | ||
141 | } | ||
diff --git a/src/gnunet/service/gns/block_handler.go b/src/gnunet/service/gns/block_handler.go index 4c49c99..ee87065 100644 --- a/src/gnunet/service/gns/block_handler.go +++ b/src/gnunet/service/gns/block_handler.go | |||
@@ -30,7 +30,7 @@ import ( | |||
30 | "github.com/bfix/gospel/logger" | 30 | "github.com/bfix/gospel/logger" |
31 | ) | 31 | ) |
32 | 32 | ||
33 | // HdlrInst is the type for functions that instanciate custom block handlers. | 33 | // HdlrInst is the type for functions that instantiate custom block handlers. |
34 | type HdlrInst func(*message.ResourceRecord, []string) (BlockHandler, error) | 34 | type HdlrInst func(*message.ResourceRecord, []string) (BlockHandler, error) |
35 | 35 | ||
36 | // Error codes | 36 | // Error codes |
@@ -157,7 +157,7 @@ func NewBlockHandlerList(records []*message.ResourceRecord, labels []string) (*B | |||
157 | hl.counts.Add(rrType) | 157 | hl.counts.Add(rrType) |
158 | 158 | ||
159 | // check for custom handler type | 159 | // check for custom handler type |
160 | if creat, ok := customHandler[enums.GNSType(rrType)]; ok { | 160 | if creat, ok := customHandler[rrType]; ok { |
161 | // check if a handler for given type already exists | 161 | // check if a handler for given type already exists |
162 | var ( | 162 | var ( |
163 | hdlr BlockHandler | 163 | hdlr BlockHandler |
@@ -192,7 +192,7 @@ func NewBlockHandlerList(records []*message.ResourceRecord, labels []string) (*B | |||
192 | } | 192 | } |
193 | 193 | ||
194 | // GetHandler returns a BlockHandler for the given GNS block type. | 194 | // GetHandler returns a BlockHandler for the given GNS block type. |
195 | // If more than one type is given, the first matching hanlder is | 195 | // If more than one type is given, the first matching handler is |
196 | // returned. | 196 | // returned. |
197 | func (hl *BlockHandlerList) GetHandler(types ...enums.GNSType) BlockHandler { | 197 | func (hl *BlockHandlerList) GetHandler(types ...enums.GNSType) BlockHandler { |
198 | for _, t := range types { | 198 | for _, t := range types { |
diff --git a/src/gnunet/service/gns/dns.go b/src/gnunet/service/gns/dns.go index 4422c44..7996460 100644 --- a/src/gnunet/service/gns/dns.go +++ b/src/gnunet/service/gns/dns.go | |||
@@ -204,7 +204,7 @@ func QueryDNS(id int, name string, server net.IP, kind RRTypeList) *message.Reco | |||
204 | // ResolveDNS resolves a name in DNS. Multiple DNS servers are queried in | 204 | // ResolveDNS resolves a name in DNS. Multiple DNS servers are queried in |
205 | // parallel; the first result delivered by any of the servers is returned | 205 | // parallel; the first result delivered by any of the servers is returned |
206 | // as the result list of matching resource records. | 206 | // as the result list of matching resource records. |
207 | func (gns *Module) ResolveDNS( | 207 | func (m *Module) ResolveDNS( |
208 | ctx context.Context, | 208 | ctx context.Context, |
209 | name string, | 209 | name string, |
210 | servers []string, | 210 | servers []string, |
@@ -223,7 +223,7 @@ func (gns *Module) ResolveDNS( | |||
223 | if addr == nil { | 223 | if addr == nil { |
224 | // no, it is a name... try to resolve an IP address from the name | 224 | // no, it is a name... try to resolve an IP address from the name |
225 | query := NewRRTypeList(enums.GNS_TYPE_DNS_A, enums.GNS_TYPE_DNS_AAAA) | 225 | query := NewRRTypeList(enums.GNS_TYPE_DNS_A, enums.GNS_TYPE_DNS_AAAA) |
226 | if set, err = gns.ResolveUnknown(ctx, srv, nil, zkey, query, depth+1); err != nil { | 226 | if set, err = m.ResolveUnknown(ctx, srv, nil, zkey, query, depth+1); err != nil { |
227 | logger.Printf(logger.ERROR, "[dns] Can't resolve NS server '%s': %s\n", srv, err.Error()) | 227 | logger.Printf(logger.ERROR, "[dns] Can't resolve NS server '%s': %s\n", srv, err.Error()) |
228 | continue | 228 | continue |
229 | } | 229 | } |
diff --git a/src/gnunet/service/gns/module.go b/src/gnunet/service/gns/module.go index 1129273..c431d48 100644 --- a/src/gnunet/service/gns/module.go +++ b/src/gnunet/service/gns/module.go | |||
@@ -103,7 +103,7 @@ func NewModule(ctx context.Context, c *core.Core) (m *Module) { | |||
103 | ModuleImpl: *service.NewModuleImpl(), | 103 | ModuleImpl: *service.NewModuleImpl(), |
104 | } | 104 | } |
105 | // register as listener for core events | 105 | // register as listener for core events |
106 | listener := m.Run(ctx, m.event, m.Filter(), 0, nil) | 106 | listener := m.ModuleImpl.Run(ctx, m.event, m.Filter(), 0, nil) |
107 | c.Register("gns", listener) | 107 | c.Register("gns", listener) |
108 | return | 108 | return |
109 | } | 109 | } |
@@ -135,11 +135,11 @@ func (m *Module) Export(fcn map[string]any) { | |||
135 | // Import functions | 135 | // Import functions |
136 | func (m *Module) Import(fcn map[string]any) { | 136 | func (m *Module) Import(fcn map[string]any) { |
137 | // resolve imports from other modules | 137 | // resolve imports from other modules |
138 | m.LookupLocal = fcn["namecache:get"].(func(ctx context.Context, query *blocks.GNSQuery) (*blocks.GNSBlock, error)) | 138 | m.LookupLocal, _ = fcn["namecache:get"].(func(ctx context.Context, query *blocks.GNSQuery) (*blocks.GNSBlock, error)) |
139 | m.StoreLocal = fcn["namecache:put"].(func(ctx context.Context, query *blocks.GNSQuery, block *blocks.GNSBlock) error) | 139 | m.StoreLocal, _ = fcn["namecache:put"].(func(ctx context.Context, query *blocks.GNSQuery, block *blocks.GNSBlock) error) |
140 | m.LookupRemote = fcn["dht:get"].(func(ctx context.Context, query blocks.Query) (blocks.Block, error)) | 140 | m.LookupRemote, _ = fcn["dht:get"].(func(ctx context.Context, query blocks.Query) (blocks.Block, error)) |
141 | m.RevocationQuery = fcn["rev:query"].(func(ctx context.Context, zkey *crypto.ZoneKey) (valid bool, err error)) | 141 | m.RevocationQuery, _ = fcn["rev:query"].(func(ctx context.Context, zkey *crypto.ZoneKey) (valid bool, err error)) |
142 | m.RevocationRevoke = fcn["rev:revoke"].(func(ctx context.Context, rd *revocation.RevData) (success bool, err error)) | 142 | m.RevocationRevoke, _ = fcn["rev:revoke"].(func(ctx context.Context, rd *revocation.RevData) (success bool, err error)) |
143 | } | 143 | } |
144 | 144 | ||
145 | //---------------------------------------------------------------------- | 145 | //---------------------------------------------------------------------- |
@@ -257,7 +257,7 @@ func (m *Module) ResolveRelative( | |||
257 | 257 | ||
258 | if hdlr := hdlrs.GetHandler(crypto.ZoneTypes...); hdlr != nil { | 258 | if hdlr := hdlrs.GetHandler(crypto.ZoneTypes...); hdlr != nil { |
259 | // (1) zone key record: | 259 | // (1) zone key record: |
260 | inst := hdlr.(*ZoneKeyHandler) | 260 | inst, _ := hdlr.(*ZoneKeyHandler) |
261 | // if labels are pending, set new zone and continue resolution; | 261 | // if labels are pending, set new zone and continue resolution; |
262 | // otherwise resolve "@" label for the zone if no zone key record | 262 | // otherwise resolve "@" label for the zone if no zone key record |
263 | // was requested. | 263 | // was requested. |
@@ -265,14 +265,15 @@ func (m *Module) ResolveRelative( | |||
265 | labels = append(labels, "@") | 265 | labels = append(labels, "@") |
266 | } | 266 | } |
267 | // check if zone key has been revoked | 267 | // check if zone key has been revoked |
268 | if valid, err := m.RevocationQuery(ctx, inst.zkey); err != nil || !valid { | 268 | var valid bool |
269 | if valid, err = m.RevocationQuery(ctx, inst.zkey); err != nil || !valid { | ||
269 | // revoked key -> no results! | 270 | // revoked key -> no results! |
270 | records = make([]*message.ResourceRecord, 0) | 271 | records = make([]*message.ResourceRecord, 0) |
271 | break | 272 | break |
272 | } | 273 | } |
273 | } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_GNS2DNS); hdlr != nil { | 274 | } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_GNS2DNS); hdlr != nil { |
274 | // (2) GNS2DNS records | 275 | // (2) GNS2DNS records |
275 | inst := hdlr.(*Gns2DnsHandler) | 276 | inst, _ := hdlr.(*Gns2DnsHandler) |
276 | // if we are at the end of the path and the requested type | 277 | // if we are at the end of the path and the requested type |
277 | // includes GNS_TYPE_GNS2DNS, the GNS2DNS records are returned... | 278 | // includes GNS_TYPE_GNS2DNS, the GNS2DNS records are returned... |
278 | if len(labels) == 1 && kind.HasType(enums.GNS_TYPE_GNS2DNS) && !kind.IsAny() { | 279 | if len(labels) == 1 && kind.HasType(enums.GNS_TYPE_GNS2DNS) && !kind.IsAny() { |
@@ -308,7 +309,7 @@ func (m *Module) ResolveRelative( | |||
308 | break | 309 | break |
309 | } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_BOX); hdlr != nil { | 310 | } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_BOX); hdlr != nil { |
310 | // (3) BOX records: | 311 | // (3) BOX records: |
311 | inst := hdlr.(*BoxHandler) | 312 | inst, _ := hdlr.(*BoxHandler) |
312 | newRecords := inst.Records(kind).Records | 313 | newRecords := inst.Records(kind).Records |
313 | if len(newRecords) > 0 { | 314 | if len(newRecords) > 0 { |
314 | records = newRecords | 315 | records = newRecords |
@@ -316,7 +317,7 @@ func (m *Module) ResolveRelative( | |||
316 | } | 317 | } |
317 | } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_DNS_CNAME); hdlr != nil { | 318 | } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_DNS_CNAME); hdlr != nil { |
318 | // (4) CNAME records: | 319 | // (4) CNAME records: |
319 | inst := hdlr.(*CnameHandler) | 320 | inst, _ := hdlr.(*CnameHandler) |
320 | // if we are at the end of the path and the requested type | 321 | // if we are at the end of the path and the requested type |
321 | // includes GNS_TYPE_DNS_CNAME, the records are returned... | 322 | // includes GNS_TYPE_DNS_CNAME, the records are returned... |
322 | if len(labels) == 1 && kind.HasType(enums.GNS_TYPE_DNS_CNAME) && !kind.IsAny() { | 323 | if len(labels) == 1 && kind.HasType(enums.GNS_TYPE_DNS_CNAME) && !kind.IsAny() { |
@@ -352,7 +353,7 @@ func (m *Module) ResolveRelative( | |||
352 | // check for VPN record | 353 | // check for VPN record |
353 | if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_VPN); hdlr != nil { | 354 | if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_VPN); hdlr != nil { |
354 | // add VPN record to result set | 355 | // add VPN record to result set |
355 | inst := hdlr.(*VpnHandler) | 356 | inst, _ := hdlr.(*VpnHandler) |
356 | set.AddRecord(inst.rec) | 357 | set.AddRecord(inst.rec) |
357 | } | 358 | } |
358 | } | 359 | } |
@@ -458,7 +459,10 @@ func (m *Module) Lookup( | |||
458 | return | 459 | return |
459 | } | 460 | } |
460 | // store RRs from remote locally. | 461 | // store RRs from remote locally. |
461 | m.StoreLocal(ctx, query, block) | 462 | if err = m.StoreLocal(ctx, query, block); err != nil { |
463 | logger.Printf(logger.DBG, "[gns] store local failed: %s", err.Error()) | ||
464 | return | ||
465 | } | ||
462 | } | 466 | } |
463 | } | 467 | } |
464 | return | 468 | return |
diff --git a/src/gnunet/service/gns/rpc.go b/src/gnunet/service/gns/rpc.go index 33682d3..042d72a 100644 --- a/src/gnunet/service/gns/rpc.go +++ b/src/gnunet/service/gns/rpc.go | |||
@@ -18,10 +18,10 @@ | |||
18 | 18 | ||
19 | package gns | 19 | package gns |
20 | 20 | ||
21 | import "net/rpc" | 21 | import "gnunet/service" |
22 | 22 | ||
23 | //---------------------------------------------------------------------- | 23 | //---------------------------------------------------------------------- |
24 | 24 | ||
25 | // InitRPC registers RPC commands for the module | 25 | // InitRPC registers RPC commands for the module |
26 | func (m *Module) InitRPC(srv *rpc.Server) { | 26 | func (m *Module) InitRPC(srv *service.JRPCServer) { |
27 | } | 27 | } |
diff --git a/src/gnunet/service/gns/service.go b/src/gnunet/service/gns/service.go index 19ddc14..45eb700 100644 --- a/src/gnunet/service/gns/service.go +++ b/src/gnunet/service/gns/service.go | |||
@@ -62,8 +62,6 @@ func NewService(ctx context.Context, c *core.Core) service.Service { | |||
62 | srv := &Service{ | 62 | srv := &Service{ |
63 | Module: *mod, | 63 | Module: *mod, |
64 | } | 64 | } |
65 | srv.ProcessFcn = srv.HandleMessage | ||
66 | |||
67 | // set external function references (external services) | 65 | // set external function references (external services) |
68 | srv.LookupLocal = srv.LookupNamecache | 66 | srv.LookupLocal = srv.LookupNamecache |
69 | srv.StoreLocal = srv.StoreNamecache | 67 | srv.StoreLocal = srv.StoreNamecache |
@@ -98,7 +96,8 @@ func (s *Service) ServeClient(ctx context.Context, id int, mc *service.Connectio | |||
98 | logger.Printf(logger.INFO, "[gns:%d:%d] Received request: %v\n", id, reqID, msg) | 96 | logger.Printf(logger.INFO, "[gns:%d:%d] Received request: %v\n", id, reqID, msg) |
99 | 97 | ||
100 | // handle message | 98 | // handle message |
101 | s.HandleMessage(context.WithValue(ctx, "label", fmt.Sprintf(":%d:%d", id, reqID)), msg, mc) | 99 | valueCtx := context.WithValue(ctx, service.CtxKey("label"), fmt.Sprintf(":%d:%d", id, reqID)) |
100 | s.HandleMessage(valueCtx, nil, msg, mc) | ||
102 | } | 101 | } |
103 | // close client connection | 102 | // close client connection |
104 | mc.Close() | 103 | mc.Close() |
@@ -109,11 +108,11 @@ func (s *Service) ServeClient(ctx context.Context, id int, mc *service.Connectio | |||
109 | } | 108 | } |
110 | 109 | ||
111 | // Handle a single incoming message | 110 | // Handle a single incoming message |
112 | func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back transport.Responder) bool { | 111 | func (s *Service) HandleMessage(ctx context.Context, sender *util.PeerID, msg message.Message, back transport.Responder) bool { |
113 | // assemble log label | 112 | // assemble log label |
114 | label := "" | 113 | label := "" |
115 | if v := ctx.Value("label"); v != nil { | 114 | if v := ctx.Value("label"); v != nil { |
116 | label = v.(string) | 115 | label, _ = v.(string) |
117 | } | 116 | } |
118 | // perform lookup | 117 | // perform lookup |
119 | switch m := msg.(type) { | 118 | switch m := msg.(type) { |
@@ -123,7 +122,7 @@ func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back t | |||
123 | //---------------------------------------------------------- | 122 | //---------------------------------------------------------- |
124 | 123 | ||
125 | // perform lookup on block (locally and remote) | 124 | // perform lookup on block (locally and remote) |
126 | go func(m *message.LookupMsg) { | 125 | go func(m *message.LookupMsg, label string) { |
127 | logger.Printf(logger.INFO, "[gns%s] Lookup request received.\n", label) | 126 | logger.Printf(logger.INFO, "[gns%s] Lookup request received.\n", label) |
128 | resp := message.NewGNSLookupResultMsg(m.ID) | 127 | resp := message.NewGNSLookupResultMsg(m.ID) |
129 | defer func() { | 128 | defer func() { |
@@ -137,7 +136,6 @@ func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back t | |||
137 | logger.Printf(logger.DBG, "[gns%s] Lookup request finished.\n", label) | 136 | logger.Printf(logger.DBG, "[gns%s] Lookup request finished.\n", label) |
138 | }() | 137 | }() |
139 | 138 | ||
140 | label := m.GetName() | ||
141 | kind := NewRRTypeList(enums.GNSType(m.Type)) | 139 | kind := NewRRTypeList(enums.GNSType(m.Type)) |
142 | recset, err := s.Resolve(ctx, label, m.Zone, kind, int(m.Options), 0) | 140 | recset, err := s.Resolve(ctx, label, m.Zone, kind, int(m.Options), 0) |
143 | if err != nil { | 141 | if err != nil { |
@@ -163,11 +161,13 @@ func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back t | |||
163 | // is this the record type we are looking for? | 161 | // is this the record type we are looking for? |
164 | if rec.Type == m.Type || enums.GNSType(m.Type) == enums.GNS_TYPE_ANY { | 162 | if rec.Type == m.Type || enums.GNSType(m.Type) == enums.GNS_TYPE_ANY { |
165 | // add it to the response message | 163 | // add it to the response message |
166 | resp.AddRecord(rec) | 164 | if err := resp.AddRecord(rec); err != nil { |
165 | logger.Printf(logger.ERROR, "[gns%s] failed: %sv", label, err.Error()) | ||
166 | } | ||
167 | } | 167 | } |
168 | } | 168 | } |
169 | } | 169 | } |
170 | }(m) | 170 | }(m, label) |
171 | 171 | ||
172 | default: | 172 | default: |
173 | //---------------------------------------------------------- | 173 | //---------------------------------------------------------- |
@@ -374,7 +374,7 @@ func (s *Service) LookupDHT(ctx context.Context, query blocks.Query) (block bloc | |||
374 | // send DHT GET request and wait for response | 374 | // send DHT GET request and wait for response |
375 | reqGet := message.NewDHTClientGetMsg(query.Key()) | 375 | reqGet := message.NewDHTClientGetMsg(query.Key()) |
376 | reqGet.ID = uint64(util.NextID()) | 376 | reqGet.ID = uint64(util.NextID()) |
377 | reqGet.ReplLevel = uint32(enums.DHT_GNS_REPLICATION_LEVEL) | 377 | reqGet.ReplLevel = uint32(enums.GNS_REPLICATION_LEVEL) |
378 | reqGet.Type = uint32(enums.BLOCK_TYPE_GNS_NAMERECORD) | 378 | reqGet.Type = uint32(enums.BLOCK_TYPE_GNS_NAMERECORD) |
379 | reqGet.Options = uint32(enums.DHT_RO_DEMULTIPLEX_EVERYWHERE) | 379 | reqGet.Options = uint32(enums.DHT_RO_DEMULTIPLEX_EVERYWHERE) |
380 | 380 | ||
@@ -419,7 +419,7 @@ func (s *Service) LookupDHT(ctx context.Context, query blocks.Query) (block bloc | |||
419 | } | 419 | } |
420 | 420 | ||
421 | // get GNSBlock from message | 421 | // get GNSBlock from message |
422 | qGNS := query.(*blocks.GNSQuery) | 422 | qGNS, _ := query.(*blocks.GNSQuery) |
423 | block = new(blocks.GNSBlock) | 423 | block = new(blocks.GNSBlock) |
424 | if err = data.Unmarshal(block, m.Data); err != nil { | 424 | if err = data.Unmarshal(block, m.Data); err != nil { |
425 | logger.Printf(logger.ERROR, "[gns] can't read GNS block: %s\n", err.Error()) | 425 | logger.Printf(logger.ERROR, "[gns] can't read GNS block: %s\n", err.Error()) |
diff --git a/src/gnunet/service/module.go b/src/gnunet/service/module.go index 65b49d8..7311c4f 100644 --- a/src/gnunet/service/module.go +++ b/src/gnunet/service/module.go | |||
@@ -21,9 +21,6 @@ package service | |||
21 | import ( | 21 | import ( |
22 | "context" | 22 | "context" |
23 | "gnunet/core" | 23 | "gnunet/core" |
24 | "gnunet/message" | ||
25 | "gnunet/transport" | ||
26 | "net/rpc" | ||
27 | "time" | 24 | "time" |
28 | ) | 25 | ) |
29 | 26 | ||
@@ -66,7 +63,7 @@ type Module interface { | |||
66 | Import(map[string]any) | 63 | Import(map[string]any) |
67 | 64 | ||
68 | // InitRPC registers RPC commands for the module | 65 | // InitRPC registers RPC commands for the module |
69 | InitRPC(*rpc.Server) | 66 | InitRPC(*JRPCServer) |
70 | 67 | ||
71 | // Filter returns the event filter for the module | 68 | // Filter returns the event filter for the module |
72 | Filter() *core.EventFilter | 69 | Filter() *core.EventFilter |
@@ -78,20 +75,19 @@ type EventHandler func(context.Context, *core.Event) | |||
78 | // Heartbeat is a function prototype for periodic tasks | 75 | // Heartbeat is a function prototype for periodic tasks |
79 | type Heartbeat func(context.Context) | 76 | type Heartbeat func(context.Context) |
80 | 77 | ||
78 | // CtxKey is a value-context key | ||
79 | type CtxKey string | ||
80 | |||
81 | // ModuleImpl is an event-handling type used by Module implementations. | 81 | // ModuleImpl is an event-handling type used by Module implementations. |
82 | type ModuleImpl struct { | 82 | type ModuleImpl struct { |
83 | // channel for core events. | 83 | // channel for core events. |
84 | ch chan *core.Event | 84 | ch chan *core.Event |
85 | |||
86 | // ProcessFcn message: function reference (implemented by service) | ||
87 | ProcessFcn func(ctx context.Context, msg message.Message, back transport.Responder) bool | ||
88 | } | 85 | } |
89 | 86 | ||
90 | // NewModuleImplementation returns a new base module and starts | 87 | // NewModuleImplementation returns a new base module and starts |
91 | func NewModuleImpl() (m *ModuleImpl) { | 88 | func NewModuleImpl() (m *ModuleImpl) { |
92 | return &ModuleImpl{ | 89 | return &ModuleImpl{ |
93 | ch: make(chan *core.Event), | 90 | ch: make(chan *core.Event), |
94 | ProcessFcn: nil, | ||
95 | } | 91 | } |
96 | } | 92 | } |
97 | 93 | ||
@@ -108,14 +104,14 @@ func (m *ModuleImpl) Run( | |||
108 | if heartbeat == nil { | 104 | if heartbeat == nil { |
109 | pulse = 365 * 24 * time.Hour // once a year | 105 | pulse = 365 * 24 * time.Hour // once a year |
110 | } | 106 | } |
111 | tick := time.Tick(pulse) | 107 | tick := time.NewTicker(pulse) |
112 | // run event loop | 108 | // run event loop |
113 | go func() { | 109 | go func() { |
114 | for { | 110 | for { |
115 | select { | 111 | select { |
116 | // Handle events | 112 | // Handle events |
117 | case event := <-m.ch: | 113 | case event := <-m.ch: |
118 | hCtx := context.WithValue(ctx, "label", event.Label) | 114 | hCtx := context.WithValue(ctx, CtxKey("label"), event.Label) |
119 | hdlr(hCtx, event) | 115 | hdlr(hCtx, event) |
120 | 116 | ||
121 | // wait for terminate signal | 117 | // wait for terminate signal |
@@ -123,7 +119,7 @@ func (m *ModuleImpl) Run( | |||
123 | return | 119 | return |
124 | 120 | ||
125 | // handle heartbeat | 121 | // handle heartbeat |
126 | case <-tick: | 122 | case <-tick.C: |
127 | // check for defined heartbeat handler | 123 | // check for defined heartbeat handler |
128 | if heartbeat != nil { | 124 | if heartbeat != nil { |
129 | heartbeat(ctx) | 125 | heartbeat(ctx) |
diff --git a/src/gnunet/service/namecache/module.go b/src/gnunet/service/namecache/module.go index 9251a58..42d38a2 100644 --- a/src/gnunet/service/namecache/module.go +++ b/src/gnunet/service/namecache/module.go | |||
@@ -24,6 +24,7 @@ import ( | |||
24 | "gnunet/core" | 24 | "gnunet/core" |
25 | "gnunet/service" | 25 | "gnunet/service" |
26 | "gnunet/service/dht/blocks" | 26 | "gnunet/service/dht/blocks" |
27 | "gnunet/service/store" | ||
27 | ) | 28 | ) |
28 | 29 | ||
29 | //====================================================================== | 30 | //====================================================================== |
@@ -38,7 +39,7 @@ import ( | |||
38 | type Module struct { | 39 | type Module struct { |
39 | service.ModuleImpl | 40 | service.ModuleImpl |
40 | 41 | ||
41 | cache service.DHTStore // transient block cache | 42 | cache store.DHTStore // transient block cache |
42 | } | 43 | } |
43 | 44 | ||
44 | // NewModule creates a new module instance. | 45 | // NewModule creates a new module instance. |
@@ -46,7 +47,7 @@ func NewModule(ctx context.Context, c *core.Core) (m *Module) { | |||
46 | m = &Module{ | 47 | m = &Module{ |
47 | ModuleImpl: *service.NewModuleImpl(), | 48 | ModuleImpl: *service.NewModuleImpl(), |
48 | } | 49 | } |
49 | m.cache, _ = service.NewDHTStore(config.Cfg.Namecache.Storage) | 50 | m.cache, _ = store.NewDHTStore(config.Cfg.Namecache.Storage) |
50 | return | 51 | return |
51 | } | 52 | } |
52 | 53 | ||
@@ -69,7 +70,9 @@ func (m *Module) Import(fcm map[string]any) { | |||
69 | // Get an entry from the cache if available. | 70 | // Get an entry from the cache if available. |
70 | func (m *Module) Get(ctx context.Context, query *blocks.GNSQuery) (block *blocks.GNSBlock, err error) { | 71 | func (m *Module) Get(ctx context.Context, query *blocks.GNSQuery) (block *blocks.GNSBlock, err error) { |
71 | var b blocks.Block | 72 | var b blocks.Block |
72 | b, err = m.cache.Get(query) | 73 | if b, err = m.cache.Get(query); err != nil { |
74 | return | ||
75 | } | ||
73 | err = blocks.Unwrap(b, block) | 76 | err = blocks.Unwrap(b, block) |
74 | return | 77 | return |
75 | } | 78 | } |
diff --git a/src/gnunet/service/revocation/module.go b/src/gnunet/service/revocation/module.go index 6997060..e435dd4 100644 --- a/src/gnunet/service/revocation/module.go +++ b/src/gnunet/service/revocation/module.go | |||
@@ -25,6 +25,7 @@ import ( | |||
25 | "gnunet/crypto" | 25 | "gnunet/crypto" |
26 | "gnunet/message" | 26 | "gnunet/message" |
27 | "gnunet/service" | 27 | "gnunet/service" |
28 | "gnunet/service/store" | ||
28 | "gnunet/util" | 29 | "gnunet/util" |
29 | "net/http" | 30 | "net/http" |
30 | 31 | ||
@@ -44,7 +45,7 @@ type Module struct { | |||
44 | service.ModuleImpl | 45 | service.ModuleImpl |
45 | 46 | ||
46 | bloomf *data.BloomFilter // bloomfilter for fast revocation check | 47 | bloomf *data.BloomFilter // bloomfilter for fast revocation check |
47 | kvs service.KVStore // storage for known revocations | 48 | kvs store.KVStore // storage for known revocations |
48 | } | 49 | } |
49 | 50 | ||
50 | // NewModule returns an initialized revocation module | 51 | // NewModule returns an initialized revocation module |
@@ -55,7 +56,7 @@ func NewModule(ctx context.Context, c *core.Core) (m *Module) { | |||
55 | } | 56 | } |
56 | init := func() (err error) { | 57 | init := func() (err error) { |
57 | // Initialize access to revocation data storage | 58 | // Initialize access to revocation data storage |
58 | if m.kvs, err = service.NewKVStore(config.Cfg.Revocation.Storage); err != nil { | 59 | if m.kvs, err = store.NewKVStore(config.Cfg.Revocation.Storage); err != nil { |
59 | return | 60 | return |
60 | } | 61 | } |
61 | // traverse the storage and build bloomfilter for all keys | 62 | // traverse the storage and build bloomfilter for all keys |
@@ -170,6 +171,6 @@ func (m *Module) Revoke(ctx context.Context, rd *RevData) (success bool, err err | |||
170 | // RPC returns the route and handler function for a JSON-RPC request | 171 | // RPC returns the route and handler function for a JSON-RPC request |
171 | func (m *Module) RPC() (string, func(http.ResponseWriter, *http.Request)) { | 172 | func (m *Module) RPC() (string, func(http.ResponseWriter, *http.Request)) { |
172 | return "/revocation/", func(wrt http.ResponseWriter, req *http.Request) { | 173 | return "/revocation/", func(wrt http.ResponseWriter, req *http.Request) { |
173 | wrt.Write([]byte(`{"msg": "This is REVOCATION" }`)) | 174 | _, _ = wrt.Write([]byte(`{"msg": "This is REVOCATION" }`)) |
174 | } | 175 | } |
175 | } | 176 | } |
diff --git a/src/gnunet/service/revocation/pow.go b/src/gnunet/service/revocation/pow.go index cb35532..5518749 100644 --- a/src/gnunet/service/revocation/pow.go +++ b/src/gnunet/service/revocation/pow.go | |||
@@ -22,7 +22,6 @@ import ( | |||
22 | "bytes" | 22 | "bytes" |
23 | "context" | 23 | "context" |
24 | "encoding/binary" | 24 | "encoding/binary" |
25 | "fmt" | ||
26 | "sort" | 25 | "sort" |
27 | "time" | 26 | "time" |
28 | 27 | ||
@@ -62,27 +61,21 @@ func NewPoWData(pow uint64, ts util.AbsoluteTime, zoneKey *crypto.ZoneKey) *PoWD | |||
62 | Timestamp: ts, | 61 | Timestamp: ts, |
63 | ZoneKey: zoneKey, | 62 | ZoneKey: zoneKey, |
64 | } | 63 | } |
65 | if rd.SetPoW(pow) != nil { | 64 | rd.SetPoW(pow) |
66 | return nil | ||
67 | } | ||
68 | return rd | 65 | return rd |
69 | } | 66 | } |
70 | 67 | ||
71 | // SetPoW sets a new PoW value in the data structure | 68 | // SetPoW sets a new PoW value in the data structure |
72 | func (p *PoWData) SetPoW(pow uint64) error { | 69 | func (p *PoWData) SetPoW(pow uint64) { |
73 | p.PoW = pow | 70 | p.PoW = pow |
74 | p.blob = p.Blob() | 71 | p.blob = p.Blob() |
75 | if p.blob == nil { | ||
76 | return fmt.Errorf("invalid PoW work unit") | ||
77 | } | ||
78 | return nil | ||
79 | } | 72 | } |
80 | 73 | ||
81 | // GetPoW returns the last checked PoW value | 74 | // GetPoW returns the last checked PoW value |
82 | func (p *PoWData) GetPoW() uint64 { | 75 | func (p *PoWData) GetPoW() uint64 { |
83 | if p.blob != nil { | 76 | if p.blob != nil { |
84 | var val uint64 | 77 | var val uint64 |
85 | binary.Read(bytes.NewReader(p.blob[:8]), binary.BigEndian, &val) | 78 | _ = binary.Read(bytes.NewReader(p.blob[:8]), binary.BigEndian, &val) |
86 | p.PoW = val | 79 | p.PoW = val |
87 | } | 80 | } |
88 | return p.PoW | 81 | return p.PoW |
@@ -138,14 +131,11 @@ type SignedRevData struct { | |||
138 | 131 | ||
139 | // NewRevDataFromMsg initializes a new RevData instance from a GNUnet message | 132 | // NewRevDataFromMsg initializes a new RevData instance from a GNUnet message |
140 | func NewRevDataFromMsg(m *message.RevocationRevokeMsg) *RevData { | 133 | func NewRevDataFromMsg(m *message.RevocationRevokeMsg) *RevData { |
141 | rd := &RevData{ | 134 | return &RevData{ |
142 | Timestamp: m.Timestamp, | 135 | Timestamp: m.Timestamp, |
143 | ZoneKeySig: m.ZoneKeySig, | 136 | ZoneKeySig: m.ZoneKeySig, |
137 | PoWs: util.Clone(m.PoWs), | ||
144 | } | 138 | } |
145 | for i, pow := range m.PoWs { | ||
146 | rd.PoWs[i] = pow | ||
147 | } | ||
148 | return rd | ||
149 | } | 139 | } |
150 | 140 | ||
151 | // Size of a serialized RevData object. | 141 | // Size of a serialized RevData object. |
@@ -174,7 +164,6 @@ func (rd *RevData) Sign(skey *crypto.ZonePrivate) (err error) { | |||
174 | // in this revocation and a verification status (-1=failed signature, -2= | 164 | // in this revocation and a verification status (-1=failed signature, -2= |
175 | // expired revocation, -3="out-of-order" PoW sequence). | 165 | // expired revocation, -3="out-of-order" PoW sequence). |
176 | func (rd *RevData) Verify(withSig bool) (zbits float64, rc int) { | 166 | func (rd *RevData) Verify(withSig bool) (zbits float64, rc int) { |
177 | |||
178 | // (1) check signature | 167 | // (1) check signature |
179 | if withSig { | 168 | if withSig { |
180 | sigBlock := &SignedRevData{ | 169 | sigBlock := &SignedRevData{ |
@@ -196,7 +185,7 @@ func (rd *RevData) Verify(withSig bool) (zbits float64, rc int) { | |||
196 | } | 185 | } |
197 | 186 | ||
198 | // (2) check PoWs | 187 | // (2) check PoWs |
199 | var last uint64 = 0 | 188 | var last uint64 |
200 | for _, pow := range rd.PoWs { | 189 | for _, pow := range rd.PoWs { |
201 | // check sequence order | 190 | // check sequence order |
202 | if pow <= last { | 191 | if pow <= last { |
@@ -252,7 +241,7 @@ func (rdc *RevDataCalc) Size() int { | |||
252 | 241 | ||
253 | // Average number of leading zero-bits in current list | 242 | // Average number of leading zero-bits in current list |
254 | func (rdc *RevDataCalc) Average() float64 { | 243 | func (rdc *RevDataCalc) Average() float64 { |
255 | var sum uint16 = 0 | 244 | var sum uint16 |
256 | for _, num := range rdc.Bits { | 245 | for _, num := range rdc.Bits { |
257 | sum += num | 246 | sum += num |
258 | } | 247 | } |
@@ -290,7 +279,7 @@ func (rdc *RevDataCalc) sortBits() { | |||
290 | func (rdc *RevDataCalc) Compute(ctx context.Context, bits int, last uint64, cb func(float64, uint64)) (float64, uint64) { | 279 | func (rdc *RevDataCalc) Compute(ctx context.Context, bits int, last uint64, cb func(float64, uint64)) (float64, uint64) { |
291 | // find the largest PoW value in current work unit | 280 | // find the largest PoW value in current work unit |
292 | work := NewPoWData(0, rdc.Timestamp, &rdc.ZoneKeySig.ZoneKey) | 281 | work := NewPoWData(0, rdc.Timestamp, &rdc.ZoneKeySig.ZoneKey) |
293 | var max uint64 = 0 | 282 | var max uint64 |
294 | for i, pow := range rdc.PoWs { | 283 | for i, pow := range rdc.PoWs { |
295 | if pow == 0 { | 284 | if pow == 0 { |
296 | max++ | 285 | max++ |
diff --git a/src/gnunet/service/revocation/pow_test.go b/src/gnunet/service/revocation/pow_test.go index 17eb695..5d31b2f 100644 --- a/src/gnunet/service/revocation/pow_test.go +++ b/src/gnunet/service/revocation/pow_test.go | |||
@@ -12,7 +12,6 @@ import ( | |||
12 | 12 | ||
13 | // Test revocation with test vector defined in the RFC draft. | 13 | // Test revocation with test vector defined in the RFC draft. |
14 | func TestRevocationRFC(t *testing.T) { | 14 | func TestRevocationRFC(t *testing.T) { |
15 | |||
16 | var ( | 15 | var ( |
17 | D = "6fea32c05af58bfa979553d188605fd57d8bf9cc263b78d5f7478c07b998ed70" | 16 | D = "6fea32c05af58bfa979553d188605fd57d8bf9cc263b78d5f7478c07b998ed70" |
18 | ZKEY = "000100002ca223e879ecc4bbdeb5da17319281d63b2e3b6955f1c3775c804a98d5f8ddaa" | 17 | ZKEY = "000100002ca223e879ecc4bbdeb5da17319281d63b2e3b6955f1c3775c804a98d5f8ddaa" |
diff --git a/src/gnunet/service/revocation/rpc.go b/src/gnunet/service/revocation/rpc.go index 1b8ea12..7473def 100644 --- a/src/gnunet/service/revocation/rpc.go +++ b/src/gnunet/service/revocation/rpc.go | |||
@@ -18,10 +18,10 @@ | |||
18 | 18 | ||
19 | package revocation | 19 | package revocation |
20 | 20 | ||
21 | import "net/rpc" | 21 | import "gnunet/service" |
22 | 22 | ||
23 | //---------------------------------------------------------------------- | 23 | //---------------------------------------------------------------------- |
24 | 24 | ||
25 | // InitRPC registers RPC commands for the module | 25 | // InitRPC registers RPC commands for the module |
26 | func (m *Module) InitRPC(srv *rpc.Server) { | 26 | func (m *Module) InitRPC(srv *service.JRPCServer) { |
27 | } | 27 | } |
diff --git a/src/gnunet/service/revocation/service.go b/src/gnunet/service/revocation/service.go index 3d579e8..99d9b4a 100644 --- a/src/gnunet/service/revocation/service.go +++ b/src/gnunet/service/revocation/service.go | |||
@@ -27,6 +27,7 @@ import ( | |||
27 | "gnunet/message" | 27 | "gnunet/message" |
28 | "gnunet/service" | 28 | "gnunet/service" |
29 | "gnunet/transport" | 29 | "gnunet/transport" |
30 | "gnunet/util" | ||
30 | 31 | ||
31 | "github.com/bfix/gospel/logger" | 32 | "github.com/bfix/gospel/logger" |
32 | ) | 33 | ) |
@@ -47,7 +48,6 @@ func NewService(ctx context.Context, c *core.Core) service.Service { | |||
47 | srv := &Service{ | 48 | srv := &Service{ |
48 | Module: *mod, | 49 | Module: *mod, |
49 | } | 50 | } |
50 | srv.ProcessFcn = srv.HandleMessage | ||
51 | return srv | 51 | return srv |
52 | } | 52 | } |
53 | 53 | ||
@@ -75,7 +75,8 @@ func (s *Service) ServeClient(ctx context.Context, id int, mc *service.Connectio | |||
75 | logger.Printf(logger.INFO, "[revocation:%d:%d] Received request: %v\n", id, reqID, msg) | 75 | logger.Printf(logger.INFO, "[revocation:%d:%d] Received request: %v\n", id, reqID, msg) |
76 | 76 | ||
77 | // handle message | 77 | // handle message |
78 | s.HandleMessage(context.WithValue(ctx, "label", fmt.Sprintf(":%d:%d", id, reqID)), msg, mc) | 78 | valueCtx := context.WithValue(ctx, service.CtxKey("label"), fmt.Sprintf(":%d:%d", id, reqID)) |
79 | s.HandleMessage(valueCtx, nil, msg, mc) | ||
79 | } | 80 | } |
80 | // close client connection | 81 | // close client connection |
81 | mc.Close() | 82 | mc.Close() |
@@ -86,11 +87,11 @@ func (s *Service) ServeClient(ctx context.Context, id int, mc *service.Connectio | |||
86 | } | 87 | } |
87 | 88 | ||
88 | // Handle a single incoming message | 89 | // Handle a single incoming message |
89 | func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back transport.Responder) bool { | 90 | func (s *Service) HandleMessage(ctx context.Context, sender *util.PeerID, msg message.Message, back transport.Responder) bool { |
90 | // assemble log label | 91 | // assemble log label |
91 | label := "" | 92 | label := "" |
92 | if v := ctx.Value("label"); v != nil { | 93 | if v := ctx.Value("label"); v != nil { |
93 | label = v.(string) | 94 | label, _ = v.(string) |
94 | } | 95 | } |
95 | switch m := msg.(type) { | 96 | switch m := msg.(type) { |
96 | case *message.RevocationQueryMsg: | 97 | case *message.RevocationQueryMsg: |
diff --git a/src/gnunet/service/rpc.go b/src/gnunet/service/rpc.go index de3a2c1..d5740fb 100644 --- a/src/gnunet/service/rpc.go +++ b/src/gnunet/service/rpc.go | |||
@@ -21,24 +21,35 @@ package service | |||
21 | import ( | 21 | import ( |
22 | "context" | 22 | "context" |
23 | "net/http" | 23 | "net/http" |
24 | "net/rpc" | ||
25 | "time" | 24 | "time" |
26 | 25 | ||
27 | "github.com/bfix/gospel/logger" | 26 | "github.com/bfix/gospel/logger" |
28 | "github.com/gorilla/mux" | 27 | "github.com/gorilla/mux" |
28 | "github.com/gorilla/rpc/v2" | ||
29 | "github.com/gorilla/rpc/v2/json2" | ||
29 | ) | 30 | ) |
30 | 31 | ||
31 | //---------------------------------------------------------------------- | 32 | //---------------------------------------------------------------------- |
33 | //---------------------------------------------------------------------- | ||
34 | |||
35 | // JRPCServer for JSON-RPC handling (wrapper to keep type in our package) | ||
36 | type JRPCServer struct { | ||
37 | *rpc.Server | ||
38 | } | ||
39 | |||
40 | //---------------------------------------------------------------------- | ||
32 | // JSON-RPC interface for services to be used as the primary client API | 41 | // JSON-RPC interface for services to be used as the primary client API |
33 | // for perform, manage and monitor GNUnet activities. | 42 | // for perform, manage and monitor GNUnet activities. |
34 | //---------------------------------------------------------------------- | 43 | //---------------------------------------------------------------------- |
35 | 44 | ||
36 | // StartRPC the JSON-RPC server. It can be terminated by context | 45 | // RunRPCServer runs the JSON-RPC server. It can be terminated by context only. |
37 | func StartRPC(ctx context.Context, endpoint string) (srvRPC *rpc.Server, err error) { | 46 | func RunRPCServer(ctx context.Context, endpoint string) (srvRPC *JRPCServer, err error) { |
47 | // instantiate RPC service | ||
48 | srvRPC = &JRPCServer{rpc.NewServer()} | ||
49 | srvRPC.RegisterCodec(json2.NewCodec(), "application/json") | ||
38 | 50 | ||
39 | // setup RPC request handler | 51 | // setup RPC request handler |
40 | router := mux.NewRouter() | 52 | router := mux.NewRouter() |
41 | srvRPC = rpc.NewServer() | ||
42 | router.HandleFunc("/", srvRPC.ServeHTTP) | 53 | router.HandleFunc("/", srvRPC.ServeHTTP) |
43 | 54 | ||
44 | // instantiate a server and run it | 55 | // instantiate a server and run it |
@@ -51,16 +62,14 @@ func StartRPC(ctx context.Context, endpoint string) (srvRPC *rpc.Server, err err | |||
51 | // start listening | 62 | // start listening |
52 | go func() { | 63 | go func() { |
53 | if err := srv.ListenAndServe(); err != http.ErrServerClosed { | 64 | if err := srv.ListenAndServe(); err != http.ErrServerClosed { |
54 | logger.Printf(logger.WARN, "[RPC] Server listen failed: %s", err.Error()) | 65 | logger.Printf(logger.WARN, "[rpc] server listen failed: %s", err.Error()) |
55 | } | 66 | } |
56 | }() | 67 | }() |
57 | // wait for shutdown | 68 | // wait for shutdown |
58 | go func() { | 69 | go func() { |
59 | select { | 70 | <-ctx.Done() |
60 | case <-ctx.Done(): | 71 | if err := srv.Shutdown(context.Background()); err != nil { |
61 | if err := srv.Shutdown(context.Background()); err != nil { | 72 | logger.Printf(logger.WARN, "[rpc] server shutdownn failed: %s", err.Error()) |
62 | logger.Printf(logger.WARN, "[RPC] Server shutdownn failed: %s", err.Error()) | ||
63 | } | ||
64 | } | 73 | } |
65 | }() | 74 | }() |
66 | return | 75 | return |
diff --git a/src/gnunet/service/service.go b/src/gnunet/service/service.go index c47ff5c..dd08282 100644 --- a/src/gnunet/service/service.go +++ b/src/gnunet/service/service.go | |||
@@ -42,7 +42,7 @@ type Service interface { | |||
42 | // Handle a single incoming message (either locally from a socket | 42 | // Handle a single incoming message (either locally from a socket |
43 | // connection or from Transport). Response messages can be send | 43 | // connection or from Transport). Response messages can be send |
44 | // via a Responder. Returns true if message was processed. | 44 | // via a Responder. Returns true if message was processed. |
45 | HandleMessage(ctx context.Context, msg message.Message, resp transport.Responder) bool | 45 | HandleMessage(ctx context.Context, sender *util.PeerID, msg message.Message, resp transport.Responder) bool |
46 | } | 46 | } |
47 | 47 | ||
48 | // SocketHandler handles incoming connections on the local service socket. | 48 | // SocketHandler handles incoming connections on the local service socket. |
@@ -85,7 +85,6 @@ func (h *SocketHandler) Start(ctx context.Context, path string, params map[strin | |||
85 | loop: | 85 | loop: |
86 | for { | 86 | for { |
87 | select { | 87 | select { |
88 | |||
89 | // handle incoming connection | 88 | // handle incoming connection |
90 | case conn := <-h.hdlr: | 89 | case conn := <-h.hdlr: |
91 | // run a new session with context | 90 | // run a new session with context |
diff --git a/src/gnunet/service/store.go b/src/gnunet/service/store.go deleted file mode 100644 index 5de5415..0000000 --- a/src/gnunet/service/store.go +++ /dev/null | |||
@@ -1,502 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package service | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "database/sql" | ||
24 | "encoding/binary" | ||
25 | "encoding/gob" | ||
26 | "encoding/hex" | ||
27 | "errors" | ||
28 | "fmt" | ||
29 | "gnunet/config" | ||
30 | "gnunet/crypto" | ||
31 | "gnunet/service/dht/blocks" | ||
32 | "gnunet/util" | ||
33 | "io" | ||
34 | "io/ioutil" | ||
35 | "os" | ||
36 | "sort" | ||
37 | "sync" | ||
38 | |||
39 | "github.com/bfix/gospel/logger" | ||
40 | redis "github.com/go-redis/redis/v8" | ||
41 | ) | ||
42 | |||
43 | // Error messages related to the key/value-store implementations | ||
44 | var ( | ||
45 | ErrStoreInvalidSpec = fmt.Errorf("Invalid Store specification") | ||
46 | ErrStoreUnknown = fmt.Errorf("Unknown Store type") | ||
47 | ErrStoreNotAvailable = fmt.Errorf("Store not available") | ||
48 | ) | ||
49 | |||
50 | //------------------------------------------------------------ | ||
51 | // Generic storage interface. Can be used for persistent or | ||
52 | // transient (caching) storage of key/value data. | ||
53 | //------------------------------------------------------------ | ||
54 | |||
55 | // Store is a key/value storage where the type of the key is either | ||
56 | // a SHA512 hash value or a string and the value is either a DHT | ||
57 | // block or a string. It is possiblle to mix any key/value types, | ||
58 | // but not used in this implementation. | ||
59 | type Store[K, V any] interface { | ||
60 | // Put value into storage under given key | ||
61 | Put(key K, val V) error | ||
62 | |||
63 | // Get value with given key from storage | ||
64 | Get(key K) (V, error) | ||
65 | |||
66 | // List all store keys | ||
67 | List() ([]K, error) | ||
68 | } | ||
69 | |||
70 | //------------------------------------------------------------ | ||
71 | // Types for custom store requirements | ||
72 | //------------------------------------------------------------ | ||
73 | |||
74 | // DHTStore for DHT queries and blocks | ||
75 | type DHTStore Store[blocks.Query, blocks.Block] | ||
76 | |||
77 | // KVStore for key/value string pairs | ||
78 | type KVStore Store[string, string] | ||
79 | |||
80 | //------------------------------------------------------------ | ||
81 | // NewDHTStore creates a new storage handler with given spec | ||
82 | // for use with DHT queries and blocks | ||
83 | func NewDHTStore(spec config.ParameterConfig) (DHTStore, error) { | ||
84 | // get the mode parameter | ||
85 | mode, ok := config.GetParam[string](spec, "mode") | ||
86 | if !ok { | ||
87 | return nil, ErrStoreInvalidSpec | ||
88 | } | ||
89 | switch mode { | ||
90 | //------------------------------------------------------------------ | ||
91 | // File-base storage | ||
92 | //------------------------------------------------------------------ | ||
93 | case "file": | ||
94 | return NewFileStore(spec) | ||
95 | } | ||
96 | return nil, ErrStoreUnknown | ||
97 | } | ||
98 | |||
99 | //------------------------------------------------------------ | ||
100 | // NewKVStore creates a new storage handler with given spec | ||
101 | // for use with key/value string pairs. | ||
102 | func NewKVStore(spec config.ParameterConfig) (KVStore, error) { | ||
103 | // get the mode parameter | ||
104 | mode, ok := config.GetParam[string](spec, "mode") | ||
105 | if !ok { | ||
106 | return nil, ErrStoreInvalidSpec | ||
107 | } | ||
108 | switch mode { | ||
109 | //-------------------------------------------------------------- | ||
110 | // Redis service | ||
111 | //-------------------------------------------------------------- | ||
112 | case "redis": | ||
113 | return NewRedisStore(spec) | ||
114 | |||
115 | //-------------------------------------------------------------- | ||
116 | // SQL database service | ||
117 | //-------------------------------------------------------------- | ||
118 | case "sql": | ||
119 | return NewSQLStore(spec) | ||
120 | } | ||
121 | return nil, errors.New("unknown storage mechanism") | ||
122 | } | ||
123 | |||
124 | //------------------------------------------------------------ | ||
125 | // Filesystem-based storage | ||
126 | //------------------------------------------------------------ | ||
127 | |||
128 | // FileHeader is the layout of a file managed by the storage handler. | ||
129 | // On start-up the file store recreates the list of file entries from | ||
130 | // traversing the actual filesystem. This is done in the background. | ||
131 | type FileHeader struct { | ||
132 | key string // storage key | ||
133 | size uint64 // size of file | ||
134 | btype uint16 // block type | ||
135 | stored util.AbsoluteTime // time added to store | ||
136 | expires util.AbsoluteTime // expiration time | ||
137 | lastUsed util.AbsoluteTime // time last used | ||
138 | usedCount uint64 // usage count | ||
139 | } | ||
140 | |||
141 | // FileStore implements a filesystem-based storage mechanism for | ||
142 | // DHT queries and blocks. | ||
143 | type FileStore struct { | ||
144 | path string // storage path | ||
145 | cache bool // storage works as cache | ||
146 | args config.ParameterConfig // arguments / settings | ||
147 | |||
148 | totalSize uint64 // total storage size (logical, not physical) | ||
149 | files map[string]*FileHeader // list of file headers | ||
150 | wrPos int // write position in cyclic list | ||
151 | mtx sync.Mutex // serialize operations (prune) | ||
152 | } | ||
153 | |||
154 | // NewFileStore instantiates a new file storage. | ||
155 | func NewFileStore(spec config.ParameterConfig) (DHTStore, error) { | ||
156 | // get path parameter | ||
157 | path, ok := config.GetParam[string](spec, "path") | ||
158 | if !ok { | ||
159 | return nil, ErrStoreInvalidSpec | ||
160 | } | ||
161 | isCache, ok := config.GetParam[bool](spec, "cache") | ||
162 | if !ok { | ||
163 | isCache = false | ||
164 | } | ||
165 | // remove old cache content | ||
166 | if isCache { | ||
167 | os.RemoveAll(path) | ||
168 | } | ||
169 | // create file store handler | ||
170 | fs := &FileStore{ | ||
171 | path: path, | ||
172 | args: spec, | ||
173 | cache: isCache, | ||
174 | files: make(map[string]*FileHeader), | ||
175 | } | ||
176 | // load file header list | ||
177 | if !isCache { | ||
178 | if fp, err := os.Open(path + "/files.db"); err == nil { | ||
179 | dec := gob.NewDecoder(fp) | ||
180 | for { | ||
181 | hdr := new(FileHeader) | ||
182 | if dec.Decode(hdr) != nil { | ||
183 | if err != io.EOF { | ||
184 | return nil, err | ||
185 | } | ||
186 | break | ||
187 | } | ||
188 | fs.files[hdr.key] = hdr | ||
189 | fs.totalSize += hdr.size | ||
190 | } | ||
191 | fp.Close() | ||
192 | } | ||
193 | } | ||
194 | return fs, nil | ||
195 | } | ||
196 | |||
197 | // Close file storage. write metadata to file | ||
198 | func (s *FileStore) Close() (err error) { | ||
199 | if !s.cache { | ||
200 | if fp, err := os.Create(s.path + "/files.db"); err == nil { | ||
201 | defer fp.Close() | ||
202 | enc := gob.NewEncoder(fp) | ||
203 | for _, hdr := range s.files { | ||
204 | if err = enc.Encode(hdr); err != nil { | ||
205 | break | ||
206 | } | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | return | ||
211 | } | ||
212 | |||
213 | // Put block into storage under given key | ||
214 | func (s *FileStore) Put(query blocks.Query, block blocks.Block) (err error) { | ||
215 | // check for free space | ||
216 | if s.cache { | ||
217 | // caching is limited by explicit number of files | ||
218 | num, ok := config.GetParam[int](s.args, "num") | ||
219 | if !ok { | ||
220 | num = 100 | ||
221 | } | ||
222 | if len(s.files) >= num { | ||
223 | // make space for at least one new entry | ||
224 | s.prune(1) | ||
225 | } | ||
226 | } else { | ||
227 | // normal storage is limited by quota (default: 10GB) | ||
228 | max, ok := config.GetParam[int](s.args, "maxGB") | ||
229 | if !ok { | ||
230 | max = 10 | ||
231 | } | ||
232 | if int(s.totalSize>>30) > max { | ||
233 | // drop a significant number of blocks | ||
234 | s.prune(20) | ||
235 | } | ||
236 | } | ||
237 | // get query parameters for entry | ||
238 | var btype uint16 // block type | ||
239 | query.Get("blkType", &btype) | ||
240 | var expire util.AbsoluteTime // block expiration | ||
241 | query.Get("expire", &expire) | ||
242 | |||
243 | // get path and filename from key | ||
244 | path, fname := s.expandPath(query.Key()) | ||
245 | // make sure the path exists | ||
246 | if err = os.MkdirAll(path, 0755); err != nil { | ||
247 | return | ||
248 | } | ||
249 | // write to file for storage | ||
250 | var fp *os.File | ||
251 | var fpSize int | ||
252 | if fp, err = os.Create(path + "/" + fname); err == nil { | ||
253 | defer fp.Close() | ||
254 | // write block data | ||
255 | if err = binary.Write(fp, binary.BigEndian, btype); err == nil { | ||
256 | if err = binary.Write(fp, binary.BigEndian, expire); err == nil { | ||
257 | _, err = fp.Write(block.Data()) | ||
258 | } | ||
259 | } | ||
260 | } | ||
261 | // add header to internal list | ||
262 | now := util.AbsoluteTimeNow() | ||
263 | hdr := &FileHeader{ | ||
264 | key: hex.EncodeToString(query.Key().Bits), | ||
265 | size: uint64(fpSize), | ||
266 | btype: btype, | ||
267 | expires: expire, | ||
268 | stored: now, | ||
269 | lastUsed: now, | ||
270 | usedCount: 1, | ||
271 | } | ||
272 | s.files[hdr.key] = hdr | ||
273 | return | ||
274 | } | ||
275 | |||
276 | // Get block with given key from storage | ||
277 | func (s *FileStore) Get(query blocks.Query) (block blocks.Block, err error) { | ||
278 | // get requested block type | ||
279 | var ( | ||
280 | btype uint16 = blocks.DHT_BLOCK_ANY | ||
281 | blkt uint16 // actual block type | ||
282 | expire util.AbsoluteTime // expiration date | ||
283 | data []byte // block data | ||
284 | ) | ||
285 | query.Get("blkType", &btype) | ||
286 | |||
287 | // get path and filename from key | ||
288 | path, fname := s.expandPath(query.Key()) | ||
289 | // read file content (block data) | ||
290 | var file *os.File | ||
291 | if file, err = os.Open(path + "/" + fname); err != nil { | ||
292 | return | ||
293 | } | ||
294 | // read block data | ||
295 | if err = binary.Read(file, binary.BigEndian, &blkt); err == nil { | ||
296 | if btype != blocks.DHT_BLOCK_ANY && btype != blkt { | ||
297 | // block types not matching | ||
298 | return | ||
299 | } | ||
300 | if err = binary.Read(file, binary.BigEndian, &expire); err == nil { | ||
301 | if data, err = ioutil.ReadAll(file); err == nil { | ||
302 | block = blocks.NewGenericBlock(data) | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | return | ||
307 | } | ||
308 | |||
309 | // Get a list of all stored block keys (generic query). | ||
310 | func (s *FileStore) List() ([]blocks.Query, error) { | ||
311 | return make([]blocks.Query, 0), nil | ||
312 | } | ||
313 | |||
314 | // expandPath returns the full path to the file for given key. | ||
315 | func (s *FileStore) expandPath(key *crypto.HashCode) (string, string) { | ||
316 | h := hex.EncodeToString(key.Bits) | ||
317 | return fmt.Sprintf("%s/%s/%s", s.path, h[:2], h[2:4]), h[4:] | ||
318 | } | ||
319 | |||
320 | // Prune list of file headers so we drop at least n entries. | ||
321 | // returns number of removed entries. | ||
322 | func (s *FileStore) prune(n int) (del int) { | ||
323 | // get list of headers; remove expired entries on the fly | ||
324 | list := make([]*FileHeader, 0) | ||
325 | for key, hdr := range s.files { | ||
326 | // remove expired entry | ||
327 | if hdr.expires.Expired() { | ||
328 | s.dropFile(key) | ||
329 | del++ | ||
330 | } | ||
331 | // append to list | ||
332 | list = append(list, hdr) | ||
333 | } | ||
334 | // check if we are already done. | ||
335 | if del >= n { | ||
336 | return | ||
337 | } | ||
338 | // sort list by decending rate "(lifetime * size) / usedCount" | ||
339 | sort.Slice(list, func(i, j int) bool { | ||
340 | ri := (list[i].stored.Elapsed().Val * list[i].size) / list[i].usedCount | ||
341 | rj := (list[j].stored.Elapsed().Val * list[j].size) / list[j].usedCount | ||
342 | return ri > rj | ||
343 | }) | ||
344 | // remove from start of list until prune limit is reached | ||
345 | for _, hdr := range list { | ||
346 | s.dropFile(hdr.key) | ||
347 | del++ | ||
348 | if del == n { | ||
349 | break | ||
350 | } | ||
351 | } | ||
352 | return | ||
353 | } | ||
354 | |||
355 | // drop file removes a file from the internal list and the physical storage. | ||
356 | func (s *FileStore) dropFile(key string) { | ||
357 | // remove for internal list | ||
358 | delete(s.files, key) | ||
359 | // remove from filesystem | ||
360 | path := fmt.Sprintf("%s/%s/%s/%s", s.path, key[:2], key[2:4], key[4:]) | ||
361 | if err := os.Remove(path); err != nil { | ||
362 | logger.Printf(logger.ERROR, "[store] can't remove file %s: %s", path, err.Error()) | ||
363 | return | ||
364 | } | ||
365 | } | ||
366 | |||
367 | //------------------------------------------------------------ | ||
368 | // Redis: only use for caching purposes on key/value strings | ||
369 | //------------------------------------------------------------ | ||
370 | |||
371 | // RedisStore uses a (local) Redis server for key/value storage | ||
372 | type RedisStore struct { | ||
373 | client *redis.Client // client connection | ||
374 | db int // index to database | ||
375 | } | ||
376 | |||
377 | // NewRedisStore creates a Redis service client instance. | ||
378 | func NewRedisStore(spec config.ParameterConfig) (s KVStore, err error) { | ||
379 | // get connection parameters | ||
380 | addr, ok := config.GetParam[string](spec, "addr") | ||
381 | if !ok { | ||
382 | return nil, ErrStoreInvalidSpec | ||
383 | } | ||
384 | passwd, ok := config.GetParam[string](spec, "passwd") | ||
385 | if !ok { | ||
386 | passwd = "" | ||
387 | } | ||
388 | db, ok := config.GetParam[int](spec, "db") | ||
389 | if !ok { | ||
390 | return nil, ErrStoreInvalidSpec | ||
391 | } | ||
392 | |||
393 | // create new Redis store | ||
394 | kvs := new(RedisStore) | ||
395 | kvs.db = db | ||
396 | kvs.client = redis.NewClient(&redis.Options{ | ||
397 | Addr: addr, | ||
398 | Password: passwd, | ||
399 | DB: db, | ||
400 | }) | ||
401 | if kvs.client == nil { | ||
402 | err = ErrStoreNotAvailable | ||
403 | } | ||
404 | s = kvs | ||
405 | return | ||
406 | } | ||
407 | |||
408 | // Put block into storage under given key | ||
409 | func (s *RedisStore) Put(key string, value string) (err error) { | ||
410 | return s.client.Set(context.TODO(), key, value, 0).Err() | ||
411 | } | ||
412 | |||
413 | // Get block with given key from storage | ||
414 | func (s *RedisStore) Get(key string) (value string, err error) { | ||
415 | return s.client.Get(context.TODO(), key).Result() | ||
416 | } | ||
417 | |||
418 | // List all keys in store | ||
419 | func (s *RedisStore) List() (keys []string, err error) { | ||
420 | var ( | ||
421 | crs uint64 | ||
422 | segm []string | ||
423 | ctx = context.TODO() | ||
424 | ) | ||
425 | keys = make([]string, 0) | ||
426 | for { | ||
427 | segm, crs, err = s.client.Scan(ctx, crs, "*", 10).Result() | ||
428 | if err != nil { | ||
429 | return | ||
430 | } | ||
431 | if crs == 0 { | ||
432 | break | ||
433 | } | ||
434 | keys = append(keys, segm...) | ||
435 | } | ||
436 | return | ||
437 | } | ||
438 | |||
439 | //------------------------------------------------------------ | ||
440 | // SQL-based key-value-store | ||
441 | //------------------------------------------------------------ | ||
442 | |||
443 | // SQLStore for generic SQL database handling | ||
444 | type SQLStore struct { | ||
445 | db *util.DbConn | ||
446 | } | ||
447 | |||
448 | // NewSQLStore creates a new SQL-based key/value store. | ||
449 | func NewSQLStore(spec config.ParameterConfig) (s KVStore, err error) { | ||
450 | // get connection parameters | ||
451 | connect, ok := config.GetParam[string](spec, "connect") | ||
452 | if !ok { | ||
453 | return nil, ErrStoreInvalidSpec | ||
454 | } | ||
455 | // create SQL store | ||
456 | kvs := new(SQLStore) | ||
457 | |||
458 | // connect to SQL database | ||
459 | kvs.db, err = util.DbPool.Connect(connect) | ||
460 | if err != nil { | ||
461 | return nil, err | ||
462 | } | ||
463 | // get number of key/value pairs (as a check for existing table) | ||
464 | row := kvs.db.QueryRow("select count(*) from store") | ||
465 | var num int | ||
466 | if row.Scan(&num) != nil { | ||
467 | return nil, ErrStoreNotAvailable | ||
468 | } | ||
469 | return kvs, nil | ||
470 | } | ||
471 | |||
472 | // Put a key/value pair into the store | ||
473 | func (s *SQLStore) Put(key string, value string) error { | ||
474 | _, err := s.db.Exec("insert into store(key,value) values(?,?)", key, value) | ||
475 | return err | ||
476 | } | ||
477 | |||
478 | // Get a value for a given key from store | ||
479 | func (s *SQLStore) Get(key string) (value string, err error) { | ||
480 | row := s.db.QueryRow("select value from store where key=?", key) | ||
481 | err = row.Scan(&value) | ||
482 | return | ||
483 | } | ||
484 | |||
485 | // List all keys in store | ||
486 | func (s *SQLStore) List() (keys []string, err error) { | ||
487 | var ( | ||
488 | rows *sql.Rows | ||
489 | key string | ||
490 | ) | ||
491 | keys = make([]string, 0) | ||
492 | rows, err = s.db.Query("select key from store") | ||
493 | if err == nil { | ||
494 | for rows.Next() { | ||
495 | if err = rows.Scan(&key); err != nil { | ||
496 | break | ||
497 | } | ||
498 | keys = append(keys, key) | ||
499 | } | ||
500 | } | ||
501 | return | ||
502 | } | ||
diff --git a/src/gnunet/util/database.go b/src/gnunet/service/store/database.go index 852862b..2b94122 100644 --- a/src/gnunet/util/database.go +++ b/src/gnunet/service/store/database.go | |||
@@ -16,12 +16,13 @@ | |||
16 | // | 16 | // |
17 | // SPDX-License-Identifier: AGPL3.0-or-later | 17 | // SPDX-License-Identifier: AGPL3.0-or-later |
18 | 18 | ||
19 | package util | 19 | package store |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | 22 | "context" |
23 | "database/sql" | 23 | "database/sql" |
24 | "fmt" | 24 | "fmt" |
25 | "gnunet/util" | ||
25 | "os" | 26 | "os" |
26 | "strings" | 27 | "strings" |
27 | 28 | ||
@@ -31,8 +32,8 @@ import ( | |||
31 | 32 | ||
32 | // Error messages related to databases | 33 | // Error messages related to databases |
33 | var ( | 34 | var ( |
34 | ErrSQLInvalidDatabaseSpec = fmt.Errorf("Invalid database specification") | 35 | ErrSQLInvalidDatabaseSpec = fmt.Errorf("invalid database specification") |
35 | ErrSQLNoDatabase = fmt.Errorf("Database not found") | 36 | ErrSQLNoDatabase = fmt.Errorf("database not found") |
36 | ) | 37 | ) |
37 | 38 | ||
38 | //---------------------------------------------------------------------- | 39 | //---------------------------------------------------------------------- |
@@ -40,35 +41,35 @@ var ( | |||
40 | // on the same instance, managed by the database pool. | 41 | // on the same instance, managed by the database pool. |
41 | //---------------------------------------------------------------------- | 42 | //---------------------------------------------------------------------- |
42 | 43 | ||
43 | // DbConn is a database connection suitable for executing SQL commands. | 44 | // DBConn is a database connection suitable for executing SQL commands. |
44 | type DbConn struct { | 45 | type DBConn struct { |
45 | conn *sql.Conn // connection to database instance | 46 | conn *sql.Conn // connection to database instance |
46 | pool *dbPool // reference to managng pool | 47 | key string // database connect string (identifier for pool) |
47 | key string // database identifier (connect string) | 48 | engine string // database engine |
48 | } | 49 | } |
49 | 50 | ||
50 | // Close database connection. | 51 | // Close database connection. |
51 | func (db *DbConn) Close() (err error) { | 52 | func (db *DBConn) Close() (err error) { |
52 | if err = db.conn.Close(); err != nil { | 53 | if err = db.conn.Close(); err != nil { |
53 | return | 54 | return |
54 | } | 55 | } |
55 | err = db.pool.remove(db.key) | 56 | err = DBPool.remove(db.key) |
56 | return | 57 | return |
57 | } | 58 | } |
58 | 59 | ||
59 | // QueryRow returns a single record for a query | 60 | // QueryRow returns a single record for a query |
60 | func (db *DbConn) QueryRow(query string, args ...any) *sql.Row { | 61 | func (db *DBConn) QueryRow(query string, args ...any) *sql.Row { |
61 | return db.conn.QueryRowContext(db.pool.ctx, query, args...) | 62 | return db.conn.QueryRowContext(DBPool.ctx, query, args...) |
62 | } | 63 | } |
63 | 64 | ||
64 | // Query returns all matching records for a query | 65 | // Query returns all matching records for a query |
65 | func (db *DbConn) Query(query string, args ...any) (*sql.Rows, error) { | 66 | func (db *DBConn) Query(query string, args ...any) (*sql.Rows, error) { |
66 | return db.conn.QueryContext(db.pool.ctx, query, args...) | 67 | return db.conn.QueryContext(DBPool.ctx, query, args...) |
67 | } | 68 | } |
68 | 69 | ||
69 | // Exec a SQL statement | 70 | // Exec a SQL statement |
70 | func (db *DbConn) Exec(query string, args ...any) (sql.Result, error) { | 71 | func (db *DBConn) Exec(query string, args ...any) (sql.Result, error) { |
71 | return db.conn.ExecContext(db.pool.ctx, query, args...) | 72 | return db.conn.ExecContext(DBPool.ctx, query, args...) |
72 | } | 73 | } |
73 | 74 | ||
74 | // TODO: add more SQL methods | 75 | // TODO: add more SQL methods |
@@ -80,11 +81,11 @@ func (db *DbConn) Exec(query string, args ...any) (sql.Result, error) { | |||
80 | 81 | ||
81 | // global instance for the database pool (singleton) | 82 | // global instance for the database pool (singleton) |
82 | var ( | 83 | var ( |
83 | DbPool *dbPool | 84 | DBPool *dbPool |
84 | ) | 85 | ) |
85 | 86 | ||
86 | // DbPoolEntry holds information about a database instance. | 87 | // DBPoolEntry holds information about a database instance. |
87 | type DbPoolEntry struct { | 88 | type DBPoolEntry struct { |
88 | db *sql.DB // reference to the database engine | 89 | db *sql.DB // reference to the database engine |
89 | refs int // number of open connections (reference count) | 90 | refs int // number of open connections (reference count) |
90 | connect string // SQL connect string | 91 | connect string // SQL connect string |
@@ -93,16 +94,16 @@ type DbPoolEntry struct { | |||
93 | // package initialization | 94 | // package initialization |
94 | func init() { | 95 | func init() { |
95 | // construct database pool | 96 | // construct database pool |
96 | DbPool = new(dbPool) | 97 | DBPool = new(dbPool) |
97 | DbPool.insts = NewMap[string, *DbPoolEntry]() | 98 | DBPool.insts = util.NewMap[string, *DBPoolEntry]() |
98 | DbPool.ctx, DbPool.cancel = context.WithCancel(context.Background()) | 99 | DBPool.ctx, DBPool.cancel = context.WithCancel(context.Background()) |
99 | } | 100 | } |
100 | 101 | ||
101 | // dbPool keeps a mapping between connect string and database instance | 102 | // dbPool keeps a mapping between connect string and database instance |
102 | type dbPool struct { | 103 | type dbPool struct { |
103 | ctx context.Context // connection context | 104 | ctx context.Context // connection context |
104 | cancel context.CancelFunc // cancel function | 105 | cancel context.CancelFunc // cancel function |
105 | insts *Map[string, *DbPoolEntry] // map of database instances | 106 | insts *util.Map[string, *DBPoolEntry] // map of database instances |
106 | } | 107 | } |
107 | 108 | ||
108 | // remove a database instance from the pool based on its connect string. | 109 | // remove a database instance from the pool based on its connect string. |
@@ -127,7 +128,7 @@ func (p *dbPool) remove(key string) error { | |||
127 | // Connect to a SQL database (various types and flavors): | 128 | // Connect to a SQL database (various types and flavors): |
128 | // The 'spec' option defines the arguments required to connect to a database; | 129 | // The 'spec' option defines the arguments required to connect to a database; |
129 | // the meaning and format of the arguments depends on the specific SQL database. | 130 | // the meaning and format of the arguments depends on the specific SQL database. |
130 | // The arguments are seperated by the '+' character; the first (and mandatory) | 131 | // The arguments are separated by the '+' character; the first (and mandatory) |
131 | // argument defines the SQL database type. Other arguments depend on the value | 132 | // argument defines the SQL database type. Other arguments depend on the value |
132 | // of this first argument. | 133 | // of this first argument. |
133 | // The following SQL types are implemented: | 134 | // The following SQL types are implemented: |
@@ -136,12 +137,13 @@ func (p *dbPool) remove(key string) error { | |||
136 | // * 'mysql': A MySQL-compatible database; the second argument specifies the | 137 | // * 'mysql': A MySQL-compatible database; the second argument specifies the |
137 | // information required to log into the database (e.g. | 138 | // information required to log into the database (e.g. |
138 | // "[user[:passwd]@][proto[(addr)]]/dbname[?param1=value1&...]"). | 139 | // "[user[:passwd]@][proto[(addr)]]/dbname[?param1=value1&...]"). |
139 | func (p *dbPool) Connect(spec string) (db *DbConn, err error) { | 140 | func (p *dbPool) Connect(spec string) (db *DBConn, err error) { |
140 | err = p.insts.Process(func() error { | 141 | err = p.insts.Process(func() error { |
141 | // check if we have a connection to this database. | 142 | // check if we have a connection to this database. |
143 | db = new(DBConn) | ||
142 | inst, ok := p.insts.Get(spec) | 144 | inst, ok := p.insts.Get(spec) |
143 | if !ok { | 145 | if !ok { |
144 | inst = new(DbPoolEntry) | 146 | inst = new(DBPoolEntry) |
145 | inst.refs = 0 | 147 | inst.refs = 0 |
146 | inst.connect = spec | 148 | inst.connect = spec |
147 | 149 | ||
@@ -152,7 +154,8 @@ func (p *dbPool) Connect(spec string) (db *DbConn, err error) { | |||
152 | return ErrSQLInvalidDatabaseSpec | 154 | return ErrSQLInvalidDatabaseSpec |
153 | } | 155 | } |
154 | // create database object | 156 | // create database object |
155 | switch specs[0] { | 157 | db.engine = specs[0] |
158 | switch db.engine { | ||
156 | case "sqlite3": | 159 | case "sqlite3": |
157 | // check if the database file exists | 160 | // check if the database file exists |
158 | var fi os.FileInfo | 161 | var fi os.FileInfo |
@@ -172,12 +175,10 @@ func (p *dbPool) Connect(spec string) (db *DbConn, err error) { | |||
172 | } | 175 | } |
173 | // save database in pool | 176 | // save database in pool |
174 | p.insts.Put(spec, inst) | 177 | p.insts.Put(spec, inst) |
175 | ok = true | ||
176 | } | 178 | } |
177 | // increment reference count | 179 | // increment reference count |
178 | inst.refs++ | 180 | inst.refs++ |
179 | // return a new connection to the database. | 181 | // return a new connection to the database. |
180 | db = new(DbConn) | ||
181 | db.conn, err = inst.db.Conn(p.ctx) | 182 | db.conn, err = inst.db.Conn(p.ctx) |
182 | return err | 183 | return err |
183 | }, false) | 184 | }, false) |
diff --git a/src/gnunet/service/dht/dhtstore_test.go b/src/gnunet/service/store/dhtstore_test.go index d9fc1d0..14da6ff 100644 --- a/src/gnunet/service/dht/dhtstore_test.go +++ b/src/gnunet/service/store/dhtstore_test.go | |||
@@ -16,37 +16,36 @@ | |||
16 | // | 16 | // |
17 | // SPDX-License-Identifier: AGPL3.0-or-later | 17 | // SPDX-License-Identifier: AGPL3.0-or-later |
18 | 18 | ||
19 | package dht | 19 | package store |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "encoding/hex" | 22 | "encoding/hex" |
23 | "gnunet/config" | ||
24 | "gnunet/crypto" | 23 | "gnunet/crypto" |
25 | "gnunet/service" | 24 | "gnunet/enums" |
26 | "gnunet/service/dht/blocks" | 25 | "gnunet/service/dht/blocks" |
26 | "gnunet/util" | ||
27 | "math/rand" | 27 | "math/rand" |
28 | "testing" | 28 | "testing" |
29 | ) | 29 | ) |
30 | 30 | ||
31 | // test constants | 31 | // test constants |
32 | const ( | 32 | const ( |
33 | fsNumBlocks = 5 | 33 | fsNumBlocks = 10 |
34 | ) | 34 | ) |
35 | 35 | ||
36 | // TestDHTFileStore generates 'fsNumBlocks' fully-random blocks | 36 | // TestDHTFileStore generates 'fsNumBlocks' fully-random blocks |
37 | // and stores them under their SHA512 key. It than retrieves | 37 | // and stores them under their SHA512 key. It than retrieves |
38 | // each block from storage and checks for matching hash. | 38 | // each block from storage and checks for matching hash. |
39 | func TestDHTFilesStore(t *testing.T) { | 39 | func TestDHTFilesStore(t *testing.T) { |
40 | |||
41 | // test configuration | 40 | // test configuration |
42 | cfg := make(config.ParameterConfig) | 41 | cfg := make(util.ParameterSet) |
43 | cfg["mode"] = "file" | 42 | cfg["mode"] = "file" |
44 | cfg["cache"] = false | 43 | cfg["cache"] = false |
45 | cfg["path"] = "/var/lib/gnunet/dht/store" | 44 | cfg["path"] = "/var/lib/gnunet/dht/store" |
46 | cfg["maxGB"] = 10 | 45 | cfg["maxGB"] = 10 |
47 | 46 | ||
48 | // create file store | 47 | // create file store |
49 | fs, err := service.NewFileStore(cfg) | 48 | fs, err := NewFileStore(cfg) |
50 | if err != nil { | 49 | if err != nil { |
51 | t.Fatal(err) | 50 | t.Fatal(err) |
52 | } | 51 | } |
@@ -62,7 +61,7 @@ func TestDHTFilesStore(t *testing.T) { | |||
62 | val := blocks.NewGenericBlock(buf) | 61 | val := blocks.NewGenericBlock(buf) |
63 | // generate associated key | 62 | // generate associated key |
64 | k := crypto.Hash(buf).Bits | 63 | k := crypto.Hash(buf).Bits |
65 | key := blocks.NewGenericQuery(k) | 64 | key := blocks.NewGenericQuery(k, enums.BLOCK_TYPE_ANY, 0) |
66 | 65 | ||
67 | // store block | 66 | // store block |
68 | if err := fs.Put(key, val); err != nil { | 67 | if err := fs.Put(key, val); err != nil { |
diff --git a/src/gnunet/service/store/store.go b/src/gnunet/service/store/store.go new file mode 100644 index 0000000..d5ef05d --- /dev/null +++ b/src/gnunet/service/store/store.go | |||
@@ -0,0 +1,278 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package store | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "database/sql" | ||
24 | _ "embed" // use embedded filesystem | ||
25 | "errors" | ||
26 | "fmt" | ||
27 | "gnunet/service/dht/blocks" | ||
28 | "gnunet/util" | ||
29 | |||
30 | redis "github.com/go-redis/redis/v8" | ||
31 | ) | ||
32 | |||
33 | // Error messages related to the key/value-store implementations | ||
34 | var ( | ||
35 | ErrStoreInvalidSpec = fmt.Errorf("invalid Store specification") | ||
36 | ErrStoreUnknown = fmt.Errorf("unknown Store type") | ||
37 | ErrStoreNotAvailable = fmt.Errorf("store not available") | ||
38 | ErrStoreNoApprox = fmt.Errorf("no approx search for store defined") | ||
39 | ErrStoreNoList = fmt.Errorf("no key listing for store defined") | ||
40 | ) | ||
41 | |||
42 | //------------------------------------------------------------ | ||
43 | // Generic storage interface. Can be used for persistent or | ||
44 | // transient (caching) storage of key/value data. | ||
45 | //------------------------------------------------------------ | ||
46 | |||
47 | // Store is a key/value storage where the type of the key is either | ||
48 | // a SHA512 hash value or a string and the value is either a DHT | ||
49 | // block or a string. It is possiblle to mix any key/value types, | ||
50 | // but not used in this implementation. | ||
51 | type Store[K, V any] interface { | ||
52 | // Put value into storage under given key | ||
53 | Put(key K, val V) error | ||
54 | |||
55 | // Get value with given key from storage | ||
56 | Get(key K) (V, error) | ||
57 | |||
58 | // GetApprox returns the best-matching value with given key from storage | ||
59 | // that is not excluded. | ||
60 | GetApprox(key K, excl func(V) bool) (V, any, error) | ||
61 | |||
62 | // List all store keys | ||
63 | List() ([]K, error) | ||
64 | |||
65 | // Close store | ||
66 | Close() error | ||
67 | } | ||
68 | |||
69 | //------------------------------------------------------------ | ||
70 | // Types for custom store requirements | ||
71 | //------------------------------------------------------------ | ||
72 | |||
73 | // DHTStore for DHT queries and blocks | ||
74 | type DHTStore Store[blocks.Query, blocks.Block] | ||
75 | |||
76 | // KVStore for key/value string pairs | ||
77 | type KVStore Store[string, string] | ||
78 | |||
79 | //------------------------------------------------------------ | ||
80 | // NewDHTStore creates a new storage handler with given spec | ||
81 | // for use with DHT queries and blocks | ||
82 | func NewDHTStore(spec util.ParameterSet) (DHTStore, error) { | ||
83 | // get the mode parameter | ||
84 | mode, ok := util.GetParam[string](spec, "mode") | ||
85 | if !ok { | ||
86 | return nil, ErrStoreInvalidSpec | ||
87 | } | ||
88 | switch mode { | ||
89 | //------------------------------------------------------------------ | ||
90 | // File-base storage | ||
91 | //------------------------------------------------------------------ | ||
92 | case "file": | ||
93 | return NewFileStore(spec) | ||
94 | } | ||
95 | return nil, ErrStoreUnknown | ||
96 | } | ||
97 | |||
98 | //------------------------------------------------------------ | ||
99 | // NewKVStore creates a new storage handler with given spec | ||
100 | // for use with key/value string pairs. | ||
101 | func NewKVStore(spec util.ParameterSet) (KVStore, error) { | ||
102 | // get the mode parameter | ||
103 | mode, ok := util.GetParam[string](spec, "mode") | ||
104 | if !ok { | ||
105 | return nil, ErrStoreInvalidSpec | ||
106 | } | ||
107 | switch mode { | ||
108 | //-------------------------------------------------------------- | ||
109 | // Redis service | ||
110 | //-------------------------------------------------------------- | ||
111 | case "redis": | ||
112 | return NewRedisStore(spec) | ||
113 | |||
114 | //-------------------------------------------------------------- | ||
115 | // SQL database service | ||
116 | //-------------------------------------------------------------- | ||
117 | case "sql": | ||
118 | return NewSQLStore(spec) | ||
119 | } | ||
120 | return nil, errors.New("unknown storage mechanism") | ||
121 | } | ||
122 | |||
123 | //------------------------------------------------------------ | ||
124 | // Redis: only use for caching purposes on key/value strings | ||
125 | //------------------------------------------------------------ | ||
126 | |||
127 | // RedisStore uses a (local) Redis server for key/value storage | ||
128 | type RedisStore struct { | ||
129 | client *redis.Client // client connection | ||
130 | db int // index to database | ||
131 | } | ||
132 | |||
133 | // NewRedisStore creates a Redis service client instance. | ||
134 | func NewRedisStore(spec util.ParameterSet) (s KVStore, err error) { | ||
135 | // get connection parameters | ||
136 | addr, ok := util.GetParam[string](spec, "addr") | ||
137 | if !ok { | ||
138 | return nil, ErrStoreInvalidSpec | ||
139 | } | ||
140 | passwd, ok := util.GetParam[string](spec, "passwd") | ||
141 | if !ok { | ||
142 | passwd = "" | ||
143 | } | ||
144 | db, ok := util.GetParam[int](spec, "db") | ||
145 | if !ok { | ||
146 | return nil, ErrStoreInvalidSpec | ||
147 | } | ||
148 | |||
149 | // create new Redis store | ||
150 | kvs := new(RedisStore) | ||
151 | kvs.db = db | ||
152 | kvs.client = redis.NewClient(&redis.Options{ | ||
153 | Addr: addr, | ||
154 | Password: passwd, | ||
155 | DB: db, | ||
156 | }) | ||
157 | if kvs.client == nil { | ||
158 | err = ErrStoreNotAvailable | ||
159 | } | ||
160 | s = kvs | ||
161 | return | ||
162 | } | ||
163 | |||
164 | // Put value into storage under given key | ||
165 | func (s *RedisStore) Put(key string, value string) (err error) { | ||
166 | return s.client.Set(context.TODO(), key, value, 0).Err() | ||
167 | } | ||
168 | |||
169 | // Get value with given key from storage | ||
170 | func (s *RedisStore) Get(key string) (value string, err error) { | ||
171 | return s.client.Get(context.TODO(), key).Result() | ||
172 | } | ||
173 | |||
174 | // GetApprox returns the best-matching value for given key from storage | ||
175 | func (s *RedisStore) GetApprox(key string, crit func(string) bool) (value string, vkey any, err error) { | ||
176 | return "", "", ErrStoreNoApprox | ||
177 | } | ||
178 | |||
179 | // List all keys in store | ||
180 | func (s *RedisStore) List() (keys []string, err error) { | ||
181 | var ( | ||
182 | crs uint64 | ||
183 | segm []string | ||
184 | ctx = context.TODO() | ||
185 | ) | ||
186 | keys = make([]string, 0) | ||
187 | for { | ||
188 | segm, crs, err = s.client.Scan(ctx, crs, "*", 10).Result() | ||
189 | if err != nil { | ||
190 | return | ||
191 | } | ||
192 | if crs == 0 { | ||
193 | break | ||
194 | } | ||
195 | keys = append(keys, segm...) | ||
196 | } | ||
197 | return | ||
198 | } | ||
199 | |||
200 | // Close redis connection | ||
201 | func (s *RedisStore) Close() error { | ||
202 | return s.client.Close() | ||
203 | } | ||
204 | |||
205 | //------------------------------------------------------------ | ||
206 | // SQL-based key-value-store | ||
207 | //------------------------------------------------------------ | ||
208 | |||
209 | // SQLStore for generic SQL database handling | ||
210 | type SQLStore struct { | ||
211 | db *DBConn | ||
212 | } | ||
213 | |||
214 | // NewSQLStore creates a new SQL-based key/value store. | ||
215 | func NewSQLStore(spec util.ParameterSet) (s KVStore, err error) { | ||
216 | // get connection parameters | ||
217 | connect, ok := util.GetParam[string](spec, "connect") | ||
218 | if !ok { | ||
219 | return nil, ErrStoreInvalidSpec | ||
220 | } | ||
221 | // create SQL store | ||
222 | kvs := new(SQLStore) | ||
223 | |||
224 | // connect to SQL database | ||
225 | kvs.db, err = DBPool.Connect(connect) | ||
226 | if err != nil { | ||
227 | return nil, err | ||
228 | } | ||
229 | // get number of key/value pairs (as a check for existing table) | ||
230 | row := kvs.db.QueryRow("select count(*) from store") | ||
231 | var num int | ||
232 | if row.Scan(&num) != nil { | ||
233 | return nil, ErrStoreNotAvailable | ||
234 | } | ||
235 | return kvs, nil | ||
236 | } | ||
237 | |||
238 | // Put a key/value pair into the store | ||
239 | func (s *SQLStore) Put(key string, value string) error { | ||
240 | _, err := s.db.Exec("insert into store(key,value) values(?,?)", key, value) | ||
241 | return err | ||
242 | } | ||
243 | |||
244 | // Get a value for a given key from store | ||
245 | func (s *SQLStore) Get(key string) (value string, err error) { | ||
246 | row := s.db.QueryRow("select value from store where key=?", key) | ||
247 | err = row.Scan(&value) | ||
248 | return | ||
249 | } | ||
250 | |||
251 | // GetApprox returns the best-matching value for given key from storage | ||
252 | func (s *SQLStore) GetApprox(key string, crit func(string) bool) (value string, vkey any, err error) { | ||
253 | return "", "", ErrStoreNoApprox | ||
254 | } | ||
255 | |||
256 | // List all keys in store | ||
257 | func (s *SQLStore) List() (keys []string, err error) { | ||
258 | var ( | ||
259 | rows *sql.Rows | ||
260 | key string | ||
261 | ) | ||
262 | keys = make([]string, 0) | ||
263 | rows, err = s.db.Query("select key from store") | ||
264 | if err == nil { | ||
265 | for rows.Next() { | ||
266 | if err = rows.Scan(&key); err != nil { | ||
267 | break | ||
268 | } | ||
269 | keys = append(keys, key) | ||
270 | } | ||
271 | } | ||
272 | return | ||
273 | } | ||
274 | |||
275 | // Close redis connection | ||
276 | func (s *SQLStore) Close() error { | ||
277 | return s.db.Close() | ||
278 | } | ||
diff --git a/src/gnunet/service/store/store_fs.go b/src/gnunet/service/store/store_fs.go new file mode 100644 index 0000000..a33c317 --- /dev/null +++ b/src/gnunet/service/store/store_fs.go | |||
@@ -0,0 +1,287 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package store | ||
20 | |||
21 | import ( | ||
22 | "encoding/hex" | ||
23 | "fmt" | ||
24 | "gnunet/service/dht/blocks" | ||
25 | "gnunet/util" | ||
26 | "io/ioutil" | ||
27 | "os" | ||
28 | |||
29 | "github.com/bfix/gospel/logger" | ||
30 | "github.com/bfix/gospel/math" | ||
31 | ) | ||
32 | |||
33 | //============================================================ | ||
34 | // Filesystem-based storage | ||
35 | //============================================================ | ||
36 | |||
37 | // FileStore implements a filesystem-based storage mechanism for | ||
38 | // DHT queries and blocks. | ||
39 | type FileStore struct { | ||
40 | path string // storage path | ||
41 | cache bool // storage works as cache | ||
42 | args util.ParameterSet // arguments / settings | ||
43 | totalSize uint64 // total storage size (logical, not physical) | ||
44 | |||
45 | // storage-mode metadata | ||
46 | meta *FileMetaDB // database for metadata | ||
47 | maxSpace int // max. storage space in GB | ||
48 | |||
49 | // cache-mode metadata | ||
50 | cacheMeta []*FileMetadata // cached metadata | ||
51 | wrPos int // write position in cyclic list | ||
52 | size int // size of cache (number of entries) | ||
53 | } | ||
54 | |||
55 | // NewFileStore instantiates a new file storage. | ||
56 | func NewFileStore(spec util.ParameterSet) (DHTStore, error) { | ||
57 | // create file store handler | ||
58 | fs := new(FileStore) | ||
59 | fs.args = spec | ||
60 | |||
61 | // get parameter | ||
62 | var ok bool | ||
63 | if fs.path, ok = util.GetParam[string](spec, "path"); !ok { | ||
64 | return nil, ErrStoreInvalidSpec | ||
65 | } | ||
66 | if fs.cache, ok = util.GetParam[bool](spec, "cache"); !ok { | ||
67 | fs.cache = false | ||
68 | } | ||
69 | |||
70 | // setup file store depending on mode (storage/cache) | ||
71 | if fs.cache { | ||
72 | // remove old cache content | ||
73 | os.RemoveAll(fs.path) | ||
74 | // get number of cache entries | ||
75 | if fs.size, ok = util.GetParam[int](spec, "num"); !ok { | ||
76 | // defaults to 1000 entries | ||
77 | fs.size = 1000 | ||
78 | } | ||
79 | fs.cacheMeta = make([]*FileMetadata, fs.size) | ||
80 | } else { | ||
81 | // connect to metadata database | ||
82 | var err error | ||
83 | if fs.meta, err = OpenMetaDB(fs.path); err != nil { | ||
84 | return nil, err | ||
85 | } | ||
86 | // normal storage is limited by quota (default: 10GB) | ||
87 | if fs.maxSpace, ok = util.GetParam[int](spec, "maxGB"); !ok { | ||
88 | fs.maxSpace = 10 | ||
89 | } | ||
90 | } | ||
91 | return fs, nil | ||
92 | } | ||
93 | |||
94 | // Close file storage. | ||
95 | func (s *FileStore) Close() (err error) { | ||
96 | if !s.cache { | ||
97 | // close database connection | ||
98 | err = s.meta.Close() | ||
99 | } | ||
100 | return | ||
101 | } | ||
102 | |||
103 | // Put block into storage under given key | ||
104 | func (s *FileStore) Put(query blocks.Query, block blocks.Block) (err error) { | ||
105 | // check for free space | ||
106 | if !s.cache { | ||
107 | if int(s.totalSize>>30) > s.maxSpace { | ||
108 | // drop a significant number of blocks | ||
109 | s.prune(20) | ||
110 | } | ||
111 | } | ||
112 | // get parameters | ||
113 | btype := query.Type() | ||
114 | expire := block.Expire() | ||
115 | |||
116 | // get path and filename from key | ||
117 | path, fname := s.expandPath(query.Key().Bits) | ||
118 | // make sure the path exists | ||
119 | if err = os.MkdirAll(path, 0755); err != nil { | ||
120 | return | ||
121 | } | ||
122 | // write to file for storage | ||
123 | var fp *os.File | ||
124 | bd := block.Data() | ||
125 | if fp, err = os.Create(path + "/" + fname); err == nil { | ||
126 | defer fp.Close() | ||
127 | // write block data | ||
128 | if _, err = fp.Write(bd); err != nil { | ||
129 | return | ||
130 | } | ||
131 | } | ||
132 | // compile metadata | ||
133 | now := util.AbsoluteTimeNow() | ||
134 | meta := &FileMetadata{ | ||
135 | key: query.Key().Bits, | ||
136 | size: uint64(len(bd)), | ||
137 | btype: btype, | ||
138 | expires: expire, | ||
139 | stored: now, | ||
140 | lastUsed: now, | ||
141 | usedCount: 1, | ||
142 | } | ||
143 | if s.cache { | ||
144 | // store in cyclic list | ||
145 | s.cacheMeta[s.wrPos] = meta | ||
146 | s.wrPos = (s.wrPos + 1) % s.size | ||
147 | } else { | ||
148 | // store metadata in database | ||
149 | if err = s.meta.Store(meta); err != nil { | ||
150 | return | ||
151 | } | ||
152 | // add to total storage size | ||
153 | s.totalSize += meta.size | ||
154 | } | ||
155 | return | ||
156 | } | ||
157 | |||
158 | // Get block with given key from storage | ||
159 | func (s *FileStore) Get(query blocks.Query) (block blocks.Block, err error) { | ||
160 | // check if we have metadata for the query | ||
161 | key := query.Key().Bits | ||
162 | btype := query.Type() | ||
163 | var md *FileMetadata | ||
164 | if md, err = s.meta.Get(key, btype); err != nil || md == nil { | ||
165 | return | ||
166 | } | ||
167 | // check for expired entry | ||
168 | if md.expires.Expired() { | ||
169 | err = s.dropFile(md) | ||
170 | return | ||
171 | } | ||
172 | // mark the block as newly used | ||
173 | if err = s.meta.Used(key, btype); err != nil { | ||
174 | return | ||
175 | } | ||
176 | return s.readBlock(query.Key().Bits) | ||
177 | } | ||
178 | |||
179 | // GetApprox returns the best-matching value with given key from storage | ||
180 | // that is not excluded | ||
181 | func (s *FileStore) GetApprox(query blocks.Query, excl func(blocks.Block) bool) (block blocks.Block, key any, err error) { | ||
182 | var bestKey []byte | ||
183 | var bestBlk blocks.Block | ||
184 | var bestDist *math.Int | ||
185 | // distance function | ||
186 | dist := func(a, b []byte) *math.Int { | ||
187 | c := make([]byte, len(a)) | ||
188 | for i := range a { | ||
189 | c[i] = a[i] ^ b[i] | ||
190 | } | ||
191 | return math.NewIntFromBytes(c) | ||
192 | } | ||
193 | // iterate over all keys | ||
194 | check := func(md *FileMetadata) { | ||
195 | // check for better match | ||
196 | d := dist(md.key, query.Key().Bits) | ||
197 | if bestKey == nil || d.Cmp(bestDist) < 0 { | ||
198 | // we might have a match. check block for exclusion | ||
199 | block, err = s.readBlock(md.key) | ||
200 | if err != nil { | ||
201 | logger.Printf(logger.ERROR, "[dhtstore] failed to retrieve blok for %s", hex.EncodeToString(md.key)) | ||
202 | return | ||
203 | } | ||
204 | if excl(block) { | ||
205 | return | ||
206 | } | ||
207 | // remember best match | ||
208 | bestKey = md.key | ||
209 | bestBlk = block | ||
210 | bestDist = d | ||
211 | } | ||
212 | } | ||
213 | if err = s.meta.Traverse(check); err != nil { | ||
214 | return | ||
215 | } | ||
216 | if bestBlk != nil { | ||
217 | // mark the block as newly used | ||
218 | if err = s.meta.Used(bestKey, bestBlk.Type()); err != nil { | ||
219 | return | ||
220 | } | ||
221 | } | ||
222 | return bestBlk, bestDist, nil | ||
223 | } | ||
224 | |||
225 | // Get a list of all stored block keys (generic query). | ||
226 | func (s *FileStore) List() ([]blocks.Query, error) { | ||
227 | return nil, ErrStoreNoList | ||
228 | } | ||
229 | |||
230 | // read block from storage for given key | ||
231 | func (s *FileStore) readBlock(key []byte) (block blocks.Block, err error) { | ||
232 | // get path and filename from key | ||
233 | path, fname := s.expandPath(key) | ||
234 | // read file content (block data) | ||
235 | var file *os.File | ||
236 | if file, err = os.Open(path + "/" + fname); err != nil { | ||
237 | return | ||
238 | } | ||
239 | defer file.Close() | ||
240 | // read block data | ||
241 | var data []byte | ||
242 | if data, err = ioutil.ReadAll(file); err == nil { | ||
243 | block = blocks.NewGenericBlock(data) | ||
244 | } | ||
245 | return | ||
246 | } | ||
247 | |||
248 | // expandPath returns the full path to the file for given key. | ||
249 | func (s *FileStore) expandPath(key []byte) (string, string) { | ||
250 | h := hex.EncodeToString(key) | ||
251 | return fmt.Sprintf("%s/%s/%s", s.path, h[:2], h[2:4]), h[4:] | ||
252 | } | ||
253 | |||
254 | // Prune list of file headers so we drop at least n entries. | ||
255 | // returns number of removed entries. | ||
256 | func (s *FileStore) prune(n int) (del int) { | ||
257 | // collect obsolete records | ||
258 | obsolete, err := s.meta.Obsolete(n) | ||
259 | if err != nil { | ||
260 | logger.Println(logger.ERROR, "[FileStore] failed to collect obsolete records: "+err.Error()) | ||
261 | return | ||
262 | } | ||
263 | for _, md := range obsolete { | ||
264 | if err := s.dropFile(md); err != nil { | ||
265 | return | ||
266 | } | ||
267 | del++ | ||
268 | } | ||
269 | return | ||
270 | } | ||
271 | |||
272 | // drop file removes a file from metadatabase and the physical storage. | ||
273 | func (s *FileStore) dropFile(md *FileMetadata) (err error) { | ||
274 | // adjust total size | ||
275 | s.totalSize -= md.size | ||
276 | // remove from database | ||
277 | if err = s.meta.Drop(md.key, md.btype); err != nil { | ||
278 | logger.Printf(logger.ERROR, "[store] can't remove metadata (%s,%d): %s", md.key, md.btype, err.Error()) | ||
279 | return | ||
280 | } | ||
281 | // remove from filesystem | ||
282 | path := fmt.Sprintf("%s/%s/%s/%s", s.path, md.key[:2], md.key[2:4], md.key[4:]) | ||
283 | if err = os.Remove(path); err != nil { | ||
284 | logger.Printf(logger.ERROR, "[store] can't remove file %s: %s", path, err.Error()) | ||
285 | } | ||
286 | return | ||
287 | } | ||
diff --git a/src/gnunet/service/store/store_fs_meta.go b/src/gnunet/service/store/store_fs_meta.go new file mode 100644 index 0000000..414921c --- /dev/null +++ b/src/gnunet/service/store/store_fs_meta.go | |||
@@ -0,0 +1,174 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package store | ||
20 | |||
21 | import ( | ||
22 | "database/sql" | ||
23 | _ "embed" | ||
24 | "gnunet/util" | ||
25 | "os" | ||
26 | ) | ||
27 | |||
28 | //============================================================ | ||
29 | // Metadata handling for file storage | ||
30 | //============================================================ | ||
31 | |||
32 | // FileMetadata holds information about a file (raw block data) | ||
33 | // and is stored in a SQL database for faster access. | ||
34 | type FileMetadata struct { | ||
35 | key []byte // storage key | ||
36 | size uint64 // size of file | ||
37 | btype uint16 // block type | ||
38 | stored util.AbsoluteTime // time added to store | ||
39 | expires util.AbsoluteTime // expiration time | ||
40 | lastUsed util.AbsoluteTime // time last used | ||
41 | usedCount uint64 // usage count | ||
42 | } | ||
43 | |||
44 | //------------------------------------------------------------ | ||
45 | // Metadata database: A SQLite3 database to hold metadata about | ||
46 | // blocks in file storage | ||
47 | //------------------------------------------------------------ | ||
48 | |||
49 | //go:embed store_fs_meta.sql | ||
50 | var initScript []byte | ||
51 | |||
52 | // FileMetaDB is a SQLite3 database for block metadata | ||
53 | type FileMetaDB struct { | ||
54 | conn *DBConn // database connection | ||
55 | } | ||
56 | |||
57 | // OpenMetaDB opens a metadata database in the given path. The name of the | ||
58 | // database is "access.db". | ||
59 | func OpenMetaDB(path string) (db *FileMetaDB, err error) { | ||
60 | // connect to database | ||
61 | dbFile := path + "/acccess.db" | ||
62 | if _, err = os.Stat(path + "/acccess.db"); err != nil { | ||
63 | var file *os.File | ||
64 | if file, err = os.Create(dbFile); err != nil { | ||
65 | return | ||
66 | } | ||
67 | file.Close() | ||
68 | } | ||
69 | db = new(FileMetaDB) | ||
70 | if db.conn, err = DBPool.Connect("sqlite3:" + dbFile); err != nil { | ||
71 | return | ||
72 | } | ||
73 | // check for initialized database | ||
74 | res := db.conn.QueryRow("select name from sqlite_master where type='table' and name='meta'") | ||
75 | var s string | ||
76 | if res.Scan(&s) != nil { | ||
77 | // initialize database | ||
78 | if _, err = db.conn.Exec(string(initScript)); err != nil { | ||
79 | return | ||
80 | } | ||
81 | } | ||
82 | return | ||
83 | } | ||
84 | |||
85 | // Store metadata in database: creates or updates a record for the metadata | ||
86 | // in the database; primary key is the query key | ||
87 | func (db *FileMetaDB) Store(md *FileMetadata) (err error) { | ||
88 | sql := "replace into meta(qkey,btype,size,stored,expires,lastUsed,usedCount) values(?,?,?,?,?,?,?)" | ||
89 | _, err = db.conn.Exec(sql, md.key, md.btype, md.size, md.stored.Epoch(), md.expires.Epoch(), md.lastUsed.Epoch(), md.usedCount) | ||
90 | return | ||
91 | } | ||
92 | |||
93 | // Get block metadata from database | ||
94 | func (db *FileMetaDB) Get(key []byte, btype uint16) (md *FileMetadata, err error) { | ||
95 | md = new(FileMetadata) | ||
96 | md.key = util.Clone(key) | ||
97 | md.btype = btype | ||
98 | stmt := "select size,stored,expires,lastUsed,usedCount from meta where qkey=? and btype=?" | ||
99 | row := db.conn.QueryRow(stmt, key, btype) | ||
100 | var st, exp, lu uint64 | ||
101 | if err = row.Scan(&md.size, &st, &exp, &lu, &md.usedCount); err == sql.ErrNoRows { | ||
102 | md = nil | ||
103 | err = nil | ||
104 | } else { | ||
105 | md.stored.Val = st * 1000000 | ||
106 | md.expires.Val = exp * 1000000 | ||
107 | md.lastUsed.Val = lu * 1000000 | ||
108 | } | ||
109 | return | ||
110 | } | ||
111 | |||
112 | // Drop metadata for block from database | ||
113 | func (db *FileMetaDB) Drop(key []byte, btype uint16) error { | ||
114 | _, err := db.conn.Exec("delete from meta where qkey=? and btype=?", key, btype) | ||
115 | return err | ||
116 | } | ||
117 | |||
118 | // Used a block from store: increment usage count and lastUsed time. | ||
119 | func (db *FileMetaDB) Used(key []byte, btype uint16) error { | ||
120 | _, err := db.conn.Exec("update meta set usedCount=usedCount+1,lastUsed=unixepoch() where qkey=? and btype=?", key, btype) | ||
121 | return err | ||
122 | } | ||
123 | |||
124 | // Obsolete collects records from the meta database that are considered | ||
125 | // "removable". Entries are rated by the value of "(lifetime * size) / usedCount" | ||
126 | func (db *FileMetaDB) Obsolete(n int) (removable []*FileMetadata, err error) { | ||
127 | // get obsolete records from database | ||
128 | rate := "(unixepoch()-unixepoch(stored))*size/usedCount" | ||
129 | stmt := "select qkey,btype from meta order by " + rate + " limit ?" | ||
130 | var rows *sql.Rows | ||
131 | if rows, err = db.conn.Query(stmt, n); err != nil { | ||
132 | return | ||
133 | } | ||
134 | var md *FileMetadata | ||
135 | for rows.Next() { | ||
136 | var st, exp, lu uint64 | ||
137 | if err = rows.Scan(&md.key, &md.btype, &md.size, &st, &exp, &lu, &md.usedCount); err != nil { | ||
138 | return | ||
139 | } | ||
140 | md.stored.Val = st * 1000000 | ||
141 | md.expires.Val = exp * 1000000 | ||
142 | md.lastUsed.Val = lu * 1000000 | ||
143 | removable = append(removable, md) | ||
144 | } | ||
145 | return | ||
146 | } | ||
147 | |||
148 | // Traverse metadata records and call function on each record | ||
149 | func (db *FileMetaDB) Traverse(f func(*FileMetadata)) error { | ||
150 | sql := "select qkey,btype,size,stored,expires,lastUsed,usedCount from meta" | ||
151 | rows, err := db.conn.Query(sql) | ||
152 | if err != nil { | ||
153 | return err | ||
154 | } | ||
155 | md := new(FileMetadata) | ||
156 | for rows.Next() { | ||
157 | var st, exp, lu uint64 | ||
158 | err = rows.Scan(&md.key, &md.btype, &md.size, &st, &exp, &lu, &md.usedCount) | ||
159 | if err != nil { | ||
160 | return err | ||
161 | } | ||
162 | md.stored.Val = st * 1000000 | ||
163 | md.expires.Val = exp * 1000000 | ||
164 | md.lastUsed.Val = lu * 1000000 | ||
165 | // call process function | ||
166 | f(md) | ||
167 | } | ||
168 | return nil | ||
169 | } | ||
170 | |||
171 | // Close metadata database | ||
172 | func (db *FileMetaDB) Close() error { | ||
173 | return db.conn.Close() | ||
174 | } | ||
diff --git a/src/gnunet/service/store/store_fs_meta.sql b/src/gnunet/service/store/store_fs_meta.sql new file mode 100644 index 0000000..a2692ab --- /dev/null +++ b/src/gnunet/service/store/store_fs_meta.sql | |||
@@ -0,0 +1,29 @@ | |||
1 | -- This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | -- Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | -- | ||
4 | -- gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | -- under the terms of the GNU Affero General Public License as published | ||
6 | -- by the Free Software Foundation, either version 3 of the License, | ||
7 | -- or (at your option) any later version. | ||
8 | -- | ||
9 | -- gnunet-go is distributed in the hope that it will be useful, but | ||
10 | -- WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | -- Affero General Public License for more details. | ||
13 | -- | ||
14 | -- You should have received a copy of the GNU Affero General Public License | ||
15 | -- along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | -- | ||
17 | -- SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | create table meta ( | ||
20 | qkey blob, -- key (SHA512 hash) | ||
21 | btype integer, -- block type | ||
22 | size integer, -- size of file | ||
23 | stored integer, -- time added to store | ||
24 | expires integer, -- expiration time | ||
25 | lastUsed integer, -- time last used | ||
26 | usedCount integer, -- usage count | ||
27 | |||
28 | unique(qkey,btype) -- unique key in database | ||
29 | ); | ||
diff --git a/src/gnunet/test/gnunet-dhtu/main.go b/src/gnunet/test/gnunet-dhtu/main.go deleted file mode 100644 index 2a49b9c..0000000 --- a/src/gnunet/test/gnunet-dhtu/main.go +++ /dev/null | |||
@@ -1,206 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package main | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "flag" | ||
24 | "fmt" | ||
25 | "gnunet/config" | ||
26 | "gnunet/core" | ||
27 | "gnunet/message" | ||
28 | "gnunet/service" | ||
29 | "gnunet/service/dht" | ||
30 | "gnunet/transport" | ||
31 | "gnunet/util" | ||
32 | "log" | ||
33 | "net/rpc" | ||
34 | "time" | ||
35 | |||
36 | "github.com/bfix/gospel/logger" | ||
37 | ) | ||
38 | |||
39 | //---------------------------------------------------------------------- | ||
40 | // Test Go node with DHTU GNUnet nodes | ||
41 | // | ||
42 | // N.B.: THIS TEST ONLY COVERS THE BASIC MESSAGE EXCHANGE LEVEL; NO | ||
43 | // MESSAGE PROCESSING EXCEPT FOR HELLO MESSAGES WILL TAKE PLACE. | ||
44 | //---------------------------------------------------------------------- | ||
45 | |||
46 | func main() { | ||
47 | // handle command-line arguments | ||
48 | var remoteAddr string | ||
49 | var cfgFile string | ||
50 | flag.StringVar(&cfgFile, "c", "gnunet-config.json", "configuration file") | ||
51 | flag.StringVar(&remoteAddr, "a", "", "address of remote node") | ||
52 | flag.Parse() | ||
53 | |||
54 | // read configuration file and set missing arguments. | ||
55 | if err := config.ParseConfig(cfgFile); err != nil { | ||
56 | logger.Printf(logger.ERROR, "[node] Invalid configuration file: %s\n", err.Error()) | ||
57 | return | ||
58 | } | ||
59 | |||
60 | // convert arguments | ||
61 | var rAddr *util.Address | ||
62 | var err error | ||
63 | if rAddr, err = util.ParseAddress(remoteAddr); err != nil { | ||
64 | logger.Println(logger.ERROR, err.Error()) | ||
65 | return | ||
66 | } | ||
67 | |||
68 | // setup execution context | ||
69 | ctx, cancel := context.WithCancel(context.Background()) | ||
70 | defer func() { | ||
71 | cancel() | ||
72 | time.Sleep(time.Second) | ||
73 | }() | ||
74 | |||
75 | // create and run node | ||
76 | node, err := NewTestNode(ctx) | ||
77 | if err != nil { | ||
78 | logger.Println(logger.ERROR, err.Error()) | ||
79 | return | ||
80 | } | ||
81 | defer node.Shutdown() | ||
82 | |||
83 | // show our HELLO URL | ||
84 | ep := config.Cfg.Local.Endpoints[0] | ||
85 | as := fmt.Sprintf("%s://%s:%d", ep.Network, ep.Address, ep.Port) | ||
86 | listen, err := util.ParseAddress(as) | ||
87 | if err != nil { | ||
88 | logger.Println(logger.ERROR, err.Error()) | ||
89 | return | ||
90 | } | ||
91 | aList := []*util.Address{listen} | ||
92 | logger.Println(logger.INFO, "[node] --> "+node.HelloURL(aList)) | ||
93 | |||
94 | // send HELLO to bootstrap address | ||
95 | if err = node.SendHello(ctx, rAddr); err != nil && err != transport.ErrEndpMaybeSent { | ||
96 | logger.Println(logger.ERROR, "[node] failed to send HELLO: "+err.Error()) | ||
97 | return | ||
98 | } | ||
99 | |||
100 | // run forever | ||
101 | var ch chan struct{} | ||
102 | <-ch | ||
103 | } | ||
104 | |||
105 | //---------------------------------------------------------------------- | ||
106 | // create and run a node with given spec | ||
107 | //---------------------------------------------------------------------- | ||
108 | |||
109 | type TestNode struct { | ||
110 | id int | ||
111 | peer *core.Peer | ||
112 | core *core.Core | ||
113 | addr *util.Address | ||
114 | } | ||
115 | |||
116 | func (n *TestNode) Shutdown() { | ||
117 | n.core.Shutdown() | ||
118 | } | ||
119 | func (n *TestNode) HelloURL(a []*util.Address) string { | ||
120 | hd, err := n.peer.HelloData(message.HelloAddressExpiration, a) | ||
121 | if err != nil { | ||
122 | return "" | ||
123 | } | ||
124 | return hd.URL() | ||
125 | } | ||
126 | |||
127 | func (n *TestNode) SendHello(ctx context.Context, addr *util.Address) error { | ||
128 | return n.core.SendHello(ctx, addr) | ||
129 | } | ||
130 | |||
131 | func NewTestNode(ctx context.Context) (node *TestNode, err error) { | ||
132 | |||
133 | // create test node | ||
134 | node = new(TestNode) | ||
135 | node.id = util.NextID() | ||
136 | |||
137 | // create core service | ||
138 | if node.core, err = core.NewCore(ctx, config.Cfg.Local); err != nil { | ||
139 | return | ||
140 | } | ||
141 | node.peer = node.core.Peer() | ||
142 | logger.Printf(logger.INFO, "[node] Node %s starting", node.peer.GetID()) | ||
143 | |||
144 | // start a new DHT service | ||
145 | dht, err := dht.NewService(ctx, node.core) | ||
146 | if err != nil { | ||
147 | log.Fatal(err) | ||
148 | } | ||
149 | |||
150 | // start JSON-RPC server on request | ||
151 | var rpc *rpc.Server | ||
152 | if rpc, err = service.StartRPC(ctx, config.Cfg.RPC.Endpoint); err != nil { | ||
153 | logger.Printf(logger.ERROR, "[node] RPC failed to start: %s", err.Error()) | ||
154 | return | ||
155 | } | ||
156 | dht.InitRPC(rpc) | ||
157 | |||
158 | // start listening on the network | ||
159 | list, err := node.core.Addresses() | ||
160 | if err != nil { | ||
161 | log.Fatal(err) | ||
162 | } | ||
163 | for _, addr := range list { | ||
164 | s := addr.Network() + "://" + addr.String() | ||
165 | if node.addr, err = util.ParseAddress(s); err != nil { | ||
166 | continue | ||
167 | } | ||
168 | logger.Printf(logger.INFO, "[node] Listening on %s", s) | ||
169 | } | ||
170 | |||
171 | // register as event listener | ||
172 | incoming := make(chan *core.Event) | ||
173 | node.core.Register(config.Cfg.Local.Name, core.NewListener(incoming, nil)) | ||
174 | |||
175 | // heart beat | ||
176 | tick := time.NewTicker(5 * time.Minute) | ||
177 | |||
178 | // run event handler | ||
179 | go func() { | ||
180 | for { | ||
181 | select { | ||
182 | // show incoming event | ||
183 | case ev := <-incoming: | ||
184 | switch ev.ID { | ||
185 | case core.EV_CONNECT: | ||
186 | logger.Printf(logger.INFO, "[node] <<< Peer %s connected", ev.Peer) | ||
187 | case core.EV_DISCONNECT: | ||
188 | logger.Printf(logger.INFO, "[node] <<< Peer %s diconnected", ev.Peer) | ||
189 | case core.EV_MESSAGE: | ||
190 | logger.Printf(logger.INFO, "[node] <<< Msg from %s of type %d", ev.Peer, ev.Msg.Header().MsgType) | ||
191 | logger.Printf(logger.INFO, "[node] <<< --> %s", ev.Msg.String()) | ||
192 | } | ||
193 | |||
194 | // handle termination signal | ||
195 | case <-ctx.Done(): | ||
196 | logger.Println(logger.INFO, "[node] Shutting down node") | ||
197 | return | ||
198 | |||
199 | // handle heart beat | ||
200 | case now := <-tick.C: | ||
201 | logger.Printf(logger.INFO, "[node] Heart beat at %s", now.String()) | ||
202 | } | ||
203 | } | ||
204 | }() | ||
205 | return | ||
206 | } | ||
diff --git a/src/gnunet/transport/endpoint.go b/src/gnunet/transport/endpoint.go index d98776a..a2f54d7 100644 --- a/src/gnunet/transport/endpoint.go +++ b/src/gnunet/transport/endpoint.go | |||
@@ -39,7 +39,7 @@ var ( | |||
39 | ErrEndpExists = errors.New("endpoint exists") | 39 | ErrEndpExists = errors.New("endpoint exists") |
40 | ErrEndpNoAddress = errors.New("no address for endpoint") | 40 | ErrEndpNoAddress = errors.New("no address for endpoint") |
41 | ErrEndpNoConnection = errors.New("no connection on endpoint") | 41 | ErrEndpNoConnection = errors.New("no connection on endpoint") |
42 | ErrEndpMaybeSent = errors.New("message may have been sent - cant know") | 42 | ErrEndpMaybeSent = errors.New("message may have been sent - can't know") |
43 | ErrEndpWriteShort = errors.New("write too short") | 43 | ErrEndpWriteShort = errors.New("write too short") |
44 | ) | 44 | ) |
45 | 45 | ||
@@ -48,10 +48,10 @@ var ( | |||
48 | // remote endpoints for TCP and UDP traffic. | 48 | // remote endpoints for TCP and UDP traffic. |
49 | type Endpoint interface { | 49 | type Endpoint interface { |
50 | // Run the endpoint and send received messages to channel | 50 | // Run the endpoint and send received messages to channel |
51 | Run(context.Context, chan *TransportMessage) error | 51 | Run(context.Context, chan *Message) error |
52 | 52 | ||
53 | // Send message on endpoint | 53 | // Send message on endpoint |
54 | Send(context.Context, net.Addr, *TransportMessage) error | 54 | Send(context.Context, net.Addr, *Message) error |
55 | 55 | ||
56 | // Address returns the listening address for the endpoint | 56 | // Address returns the listening address for the endpoint |
57 | Address() net.Addr | 57 | Address() net.Addr |
@@ -84,16 +84,17 @@ func NewEndpoint(addr net.Addr) (ep Endpoint, err error) { | |||
84 | 84 | ||
85 | // PacketEndpoint for packet-oriented network protocols | 85 | // PacketEndpoint for packet-oriented network protocols |
86 | type PaketEndpoint struct { | 86 | type PaketEndpoint struct { |
87 | sync.Mutex | ||
88 | |||
87 | id int // endpoint identifier | 89 | id int // endpoint identifier |
88 | netw string // network identifier ("udp", "udp4", "udp6", ...) | 90 | netw string // network identifier ("udp", "udp4", "udp6", ...) |
89 | addr net.Addr // endpoint address | 91 | addr net.Addr // endpoint address |
90 | conn net.PacketConn // packet connection | 92 | conn net.PacketConn // packet connection |
91 | buf []byte // buffer for read/write operations | 93 | buf []byte // buffer for read/write operations |
92 | mtx sync.Mutex // mutex for send operations | ||
93 | } | 94 | } |
94 | 95 | ||
95 | // Run packet endpoint: send incoming messages to the handler. | 96 | // Run packet endpoint: send incoming messages to the handler. |
96 | func (ep *PaketEndpoint) Run(ctx context.Context, hdlr chan *TransportMessage) (err error) { | 97 | func (ep *PaketEndpoint) Run(ctx context.Context, hdlr chan *Message) (err error) { |
97 | // create listener | 98 | // create listener |
98 | var lc net.ListenConfig | 99 | var lc net.ListenConfig |
99 | xproto := ep.addr.Network() | 100 | xproto := ep.addr.Network() |
@@ -144,7 +145,7 @@ func (ep *PaketEndpoint) Run(ctx context.Context, hdlr chan *TransportMessage) ( | |||
144 | } | 145 | } |
145 | 146 | ||
146 | // Read a transport message from endpoint based on extended protocol | 147 | // Read a transport message from endpoint based on extended protocol |
147 | func (ep *PaketEndpoint) read() (tm *TransportMessage, err error) { | 148 | func (ep *PaketEndpoint) read() (tm *Message, err error) { |
148 | // read next packet (assuming that it contains one complete message) | 149 | // read next packet (assuming that it contains one complete message) |
149 | var n int | 150 | var n int |
150 | if n, _, err = ep.conn.ReadFrom(ep.buf); err != nil { | 151 | if n, _, err = ep.conn.ReadFrom(ep.buf); err != nil { |
@@ -167,7 +168,7 @@ func (ep *PaketEndpoint) read() (tm *TransportMessage, err error) { | |||
167 | panic(ErrEndpProtocolUnknown) | 168 | panic(ErrEndpProtocolUnknown) |
168 | } | 169 | } |
169 | // return transport message | 170 | // return transport message |
170 | return &TransportMessage{ | 171 | return &Message{ |
171 | Peer: peer, | 172 | Peer: peer, |
172 | Msg: msg, | 173 | Msg: msg, |
173 | Resp: nil, | 174 | Resp: nil, |
@@ -176,10 +177,10 @@ func (ep *PaketEndpoint) read() (tm *TransportMessage, err error) { | |||
176 | } | 177 | } |
177 | 178 | ||
178 | // Send message to address from endpoint | 179 | // Send message to address from endpoint |
179 | func (ep *PaketEndpoint) Send(ctx context.Context, addr net.Addr, msg *TransportMessage) (err error) { | 180 | func (ep *PaketEndpoint) Send(ctx context.Context, addr net.Addr, msg *Message) (err error) { |
180 | // only one sender at a time | 181 | // only one sender at a time |
181 | ep.mtx.Lock() | 182 | ep.Lock() |
182 | defer ep.mtx.Unlock() | 183 | defer ep.Unlock() |
183 | 184 | ||
184 | // check for valid connection | 185 | // check for valid connection |
185 | if ep.conn == nil { | 186 | if ep.conn == nil { |
@@ -284,7 +285,7 @@ type StreamEndpoint struct { | |||
284 | } | 285 | } |
285 | 286 | ||
286 | // Run packet endpoint: send incoming messages to the handler. | 287 | // Run packet endpoint: send incoming messages to the handler. |
287 | func (ep *StreamEndpoint) Run(ctx context.Context, hdlr chan *TransportMessage) (err error) { | 288 | func (ep *StreamEndpoint) Run(ctx context.Context, hdlr chan *Message) (err error) { |
288 | // create listener | 289 | // create listener |
289 | var lc net.ListenConfig | 290 | var lc net.ListenConfig |
290 | xproto := ep.addr.Network() | 291 | xproto := ep.addr.Network() |
@@ -331,7 +332,7 @@ func (ep *StreamEndpoint) Run(ctx context.Context, hdlr chan *TransportMessage) | |||
331 | } | 332 | } |
332 | 333 | ||
333 | // Read a transport message from endpoint based on extended protocol | 334 | // Read a transport message from endpoint based on extended protocol |
334 | func (ep *StreamEndpoint) read(ctx context.Context, conn net.Conn) (tm *TransportMessage, err error) { | 335 | func (ep *StreamEndpoint) read(ctx context.Context, conn net.Conn) (tm *Message, err error) { |
335 | // parse transport message based on extended protocol | 336 | // parse transport message based on extended protocol |
336 | var ( | 337 | var ( |
337 | peer *util.PeerID | 338 | peer *util.PeerID |
@@ -341,7 +342,7 @@ func (ep *StreamEndpoint) read(ctx context.Context, conn net.Conn) (tm *Transpor | |||
341 | case "ip+udp": | 342 | case "ip+udp": |
342 | // parse peer id | 343 | // parse peer id |
343 | peer = util.NewPeerID(nil) | 344 | peer = util.NewPeerID(nil) |
344 | if _, err = conn.Read(peer.Key); err != nil { | 345 | if _, err = conn.Read(peer.Data); err != nil { |
345 | return | 346 | return |
346 | } | 347 | } |
347 | // read next message from connection | 348 | // read next message from connection |
@@ -352,14 +353,14 @@ func (ep *StreamEndpoint) read(ctx context.Context, conn net.Conn) (tm *Transpor | |||
352 | panic(ErrEndpProtocolUnknown) | 353 | panic(ErrEndpProtocolUnknown) |
353 | } | 354 | } |
354 | // return transport message | 355 | // return transport message |
355 | return &TransportMessage{ | 356 | return &Message{ |
356 | Peer: peer, | 357 | Peer: peer, |
357 | Msg: msg, | 358 | Msg: msg, |
358 | }, nil | 359 | }, nil |
359 | } | 360 | } |
360 | 361 | ||
361 | // Send message to address from endpoint | 362 | // Send message to address from endpoint |
362 | func (ep *StreamEndpoint) Send(ctx context.Context, addr net.Addr, msg *TransportMessage) error { | 363 | func (ep *StreamEndpoint) Send(ctx context.Context, addr net.Addr, msg *Message) error { |
363 | return nil | 364 | return nil |
364 | } | 365 | } |
365 | 366 | ||
diff --git a/src/gnunet/transport/reader_writer.go b/src/gnunet/transport/reader_writer.go index e99b3df..13028fe 100644 --- a/src/gnunet/transport/reader_writer.go +++ b/src/gnunet/transport/reader_writer.go | |||
@@ -54,12 +54,8 @@ func WriteMessage(ctx context.Context, wrt io.WriteCloser, msg message.Message) | |||
54 | } | 54 | } |
55 | // watch dog for write operation | 55 | // watch dog for write operation |
56 | go func() { | 56 | go func() { |
57 | for { | 57 | <-ctx.Done() |
58 | select { | 58 | wrt.Close() |
59 | case <-ctx.Done(): | ||
60 | wrt.Close() | ||
61 | } | ||
62 | } | ||
63 | }() | 59 | }() |
64 | // perform write operation | 60 | // perform write operation |
65 | var n int | 61 | var n int |
@@ -86,46 +82,41 @@ func ReadMessageDirect(rdr io.Reader, buf []byte) (msg message.Message, err erro | |||
86 | func ReadMessage(ctx context.Context, rdr io.ReadCloser, buf []byte) (msg message.Message, err error) { | 82 | func ReadMessage(ctx context.Context, rdr io.ReadCloser, buf []byte) (msg message.Message, err error) { |
87 | // watch dog for write operation | 83 | // watch dog for write operation |
88 | go func() { | 84 | go func() { |
89 | for { | 85 | <-ctx.Done() |
90 | select { | 86 | rdr.Close() |
91 | case <-ctx.Done(): | ||
92 | rdr.Close() | ||
93 | } | ||
94 | } | ||
95 | }() | 87 | }() |
96 | // get bytes from reader | 88 | // get bytes from reader |
97 | if buf == nil { | 89 | if buf == nil { |
98 | buf = make([]byte, 65536) | 90 | buf = make([]byte, 65536) |
99 | } | 91 | } |
100 | get := func(pos, count int) error { | 92 | get := func(pos, count int) (err error) { |
101 | n, err := rdr.Read(buf[pos : pos+count]) | 93 | var n int |
102 | if err == nil && n != count { | 94 | if n, err = rdr.Read(buf[pos : pos+count]); err == nil && n != count { |
103 | err = fmt.Errorf("not enough bytes on reader (%d of %d)", n, count) | 95 | err = fmt.Errorf("not enough bytes on reader (%d of %d)", n, count) |
104 | } | 96 | } |
105 | return err | 97 | return err |
106 | } | 98 | } |
107 | // read header first | 99 | // read header first |
108 | if err := get(0, 4); err != nil { | 100 | if err = get(0, 4); err != nil { |
109 | return nil, err | 101 | return |
110 | } | 102 | } |
111 | var mh *message.Header | 103 | var mh *message.Header |
112 | if mh, err = message.GetMsgHeader(buf[:4]); err != nil { | 104 | if mh, err = message.GetMsgHeader(buf[:4]); err != nil { |
113 | return nil, err | 105 | return |
114 | } | 106 | } |
115 | // get rest of message | 107 | // get rest of message |
116 | if err = get(4, int(mh.MsgSize)-4); err != nil { | 108 | if err = get(4, int(mh.MsgSize)-4); err != nil { |
117 | return nil, err | 109 | return |
118 | } | 110 | } |
119 | if msg, err = message.NewEmptyMessage(mh.MsgType); err != nil { | 111 | if msg, err = message.NewEmptyMessage(mh.MsgType); err != nil { |
120 | return nil, err | 112 | return |
121 | } | 113 | } |
122 | if msg == nil { | 114 | if msg == nil { |
123 | return nil, fmt.Errorf("message{%d} is nil", mh.MsgType) | 115 | err = fmt.Errorf("message{%d} is nil", mh.MsgType) |
124 | } | 116 | return |
125 | if err = data.Unmarshal(msg, buf[:mh.MsgSize]); err != nil { | ||
126 | return nil, err | ||
127 | } | 117 | } |
128 | return msg, nil | 118 | err = data.Unmarshal(msg, buf[:mh.MsgSize]) |
119 | return | ||
129 | } | 120 | } |
130 | 121 | ||
131 | //---------------------------------------------------------------------- | 122 | //---------------------------------------------------------------------- |
diff --git a/src/gnunet/transport/responder.go b/src/gnunet/transport/responder.go index f0d9d66..7032c78 100644 --- a/src/gnunet/transport/responder.go +++ b/src/gnunet/transport/responder.go | |||
@@ -32,6 +32,9 @@ import ( | |||
32 | type Responder interface { | 32 | type Responder interface { |
33 | // Handle outgoing message | 33 | // Handle outgoing message |
34 | Send(ctx context.Context, msg message.Message) error | 34 | Send(ctx context.Context, msg message.Message) error |
35 | |||
36 | // Receiver returns the receiving peer (string representation) | ||
37 | Receiver() string | ||
35 | } | 38 | } |
36 | 39 | ||
37 | //---------------------------------------------------------------------- | 40 | //---------------------------------------------------------------------- |
@@ -50,3 +53,8 @@ func (r *TransportResponder) Send(ctx context.Context, msg message.Message) erro | |||
50 | } | 53 | } |
51 | return r.SendFcn(ctx, r.Peer, msg) | 54 | return r.SendFcn(ctx, r.Peer, msg) |
52 | } | 55 | } |
56 | |||
57 | // Receiver returns the receiving peer id | ||
58 | func (r *TransportResponder) Receiver() string { | ||
59 | return r.Peer.String() | ||
60 | } | ||
diff --git a/src/gnunet/transport/transport.go b/src/gnunet/transport/transport.go index 2101849..bc4b632 100644 --- a/src/gnunet/transport/transport.go +++ b/src/gnunet/transport/transport.go | |||
@@ -32,17 +32,18 @@ import ( | |||
32 | // Trnsport layer error codes | 32 | // Trnsport layer error codes |
33 | var ( | 33 | var ( |
34 | ErrTransNoEndpoint = errors.New("no matching endpoint found") | 34 | ErrTransNoEndpoint = errors.New("no matching endpoint found") |
35 | ErrTransNoUPNP = errors.New("no UPnP available") | ||
35 | ) | 36 | ) |
36 | 37 | ||
37 | //====================================================================== | 38 | //====================================================================== |
38 | // Network-oriented transport implementation | 39 | // Network-oriented transport implementation |
39 | //====================================================================== | 40 | //====================================================================== |
40 | 41 | ||
41 | // TransportMessage is the unit processed by the transport mechanism. | 42 | // Message is the unit processed by the transport mechanism. |
42 | // Peer refers to the remote endpoint (sender/receiver) and | 43 | // Peer refers to the remote endpoint (sender/receiver) and |
43 | // Msg is the exchanged GNUnet message. The packet itself satisfies the | 44 | // Msg is the exchanged GNUnet message. The packet itself satisfies the |
44 | // message.Message interface. | 45 | // message.Message interface. |
45 | type TransportMessage struct { | 46 | type Message struct { |
46 | // Peer is a identifier for a remote peer | 47 | // Peer is a identifier for a remote peer |
47 | Peer *util.PeerID | 48 | Peer *util.PeerID |
48 | 49 | ||
@@ -62,10 +63,10 @@ type TransportMessage struct { | |||
62 | } | 63 | } |
63 | 64 | ||
64 | // Bytes returns the binary representation of a transport message | 65 | // Bytes returns the binary representation of a transport message |
65 | func (msg *TransportMessage) Bytes() ([]byte, error) { | 66 | func (msg *Message) Bytes() ([]byte, error) { |
66 | buf := new(bytes.Buffer) | 67 | buf := new(bytes.Buffer) |
67 | // serialize peer id | 68 | // serialize peer id |
68 | if _, err := buf.Write(msg.Peer.Key); err != nil { | 69 | if _, err := buf.Write(msg.Peer.Bytes()); err != nil { |
69 | return nil, err | 70 | return nil, err |
70 | } | 71 | } |
71 | // serialize message | 72 | // serialize message |
@@ -74,16 +75,16 @@ func (msg *TransportMessage) Bytes() ([]byte, error) { | |||
74 | } | 75 | } |
75 | 76 | ||
76 | // String returns the message in human-readable form | 77 | // String returns the message in human-readable form |
77 | func (msg *TransportMessage) String() string { | 78 | func (msg *Message) String() string { |
78 | return "TransportMessage{...}" | 79 | return "TransportMessage{...}" |
79 | } | 80 | } |
80 | 81 | ||
81 | // NewTransportMessage creates a message suitable for transfer | 82 | // NewTransportMessage creates a message suitable for transfer |
82 | func NewTransportMessage(peer *util.PeerID, msg message.Message) (tm *TransportMessage) { | 83 | func NewTransportMessage(peer *util.PeerID, msg message.Message) (tm *Message) { |
83 | if peer == nil { | 84 | if peer == nil { |
84 | peer = util.NewPeerID(nil) | 85 | peer = util.NewPeerID(nil) |
85 | } | 86 | } |
86 | tm = &TransportMessage{ | 87 | tm = &Message{ |
87 | Peer: peer, | 88 | Peer: peer, |
88 | Msg: msg, | 89 | Msg: msg, |
89 | Resp: nil, | 90 | Resp: nil, |
@@ -97,13 +98,13 @@ func NewTransportMessage(peer *util.PeerID, msg message.Message) (tm *TransportM | |||
97 | // Transport enables network-oriented (like IP, UDP, TCP or UDS) | 98 | // Transport enables network-oriented (like IP, UDP, TCP or UDS) |
98 | // message exchange on multiple endpoints. | 99 | // message exchange on multiple endpoints. |
99 | type Transport struct { | 100 | type Transport struct { |
100 | incoming chan *TransportMessage // messages as received from the network | 101 | incoming chan *Message // messages as received from the network |
101 | endpoints *util.Map[int, Endpoint] // list of available endpoints | 102 | endpoints *util.Map[int, Endpoint] // list of available endpoints |
102 | upnp *network.PortMapper // UPnP mapper (optional) | 103 | upnp *network.PortMapper // UPnP mapper (optional) |
103 | } | 104 | } |
104 | 105 | ||
105 | // NewTransport creates and runs a new transport layer implementation. | 106 | // NewTransport creates and runs a new transport layer implementation. |
106 | func NewTransport(ctx context.Context, tag string, ch chan *TransportMessage) (t *Transport) { | 107 | func NewTransport(ctx context.Context, tag string, ch chan *Message) (t *Transport) { |
107 | // create transport instance | 108 | // create transport instance |
108 | mngr, err := network.NewPortMapper(tag) | 109 | mngr, err := network.NewPortMapper(tag) |
109 | if err != nil { | 110 | if err != nil { |
@@ -124,7 +125,7 @@ func (t *Transport) Shutdown() { | |||
124 | } | 125 | } |
125 | 126 | ||
126 | // Send a message over suitable endpoint | 127 | // Send a message over suitable endpoint |
127 | func (t *Transport) Send(ctx context.Context, addr net.Addr, msg *TransportMessage) (err error) { | 128 | func (t *Transport) Send(ctx context.Context, addr net.Addr, msg *Message) (err error) { |
128 | // select best endpoint able to handle address | 129 | // select best endpoint able to handle address |
129 | var bestEp Endpoint | 130 | var bestEp Endpoint |
130 | err = t.endpoints.ProcessRange(func(_ int, ep Endpoint) error { | 131 | err = t.endpoints.ProcessRange(func(_ int, ep Endpoint) error { |
@@ -174,7 +175,7 @@ func (t *Transport) AddEndpoint(ctx context.Context, addr *util.Address) (ep End | |||
174 | } | 175 | } |
175 | // add endpoint to list and run it | 176 | // add endpoint to list and run it |
176 | t.endpoints.Put(ep.ID(), ep) | 177 | t.endpoints.Put(ep.ID(), ep) |
177 | ep.Run(ctx, t.incoming) | 178 | err = ep.Run(ctx, t.incoming) |
178 | return | 179 | return |
179 | } | 180 | } |
180 | 181 | ||
@@ -185,11 +186,20 @@ func (t *Transport) AddEndpoint(ctx context.Context, addr *util.Address) (ep End | |||
185 | // ForwardOpen returns a local address for listening that will receive traffic | 186 | // ForwardOpen returns a local address for listening that will receive traffic |
186 | // from a port forward handled by UPnP on the router. | 187 | // from a port forward handled by UPnP on the router. |
187 | func (t *Transport) ForwardOpen(protocol, param string, port int) (id, local, remote string, err error) { | 188 | func (t *Transport) ForwardOpen(protocol, param string, port int) (id, local, remote string, err error) { |
189 | // check for available UPnP | ||
190 | if t.upnp == nil { | ||
191 | err = ErrTransNoUPNP | ||
192 | return | ||
193 | } | ||
188 | // no parameters currently defined, so just do the assignment. | 194 | // no parameters currently defined, so just do the assignment. |
189 | return t.upnp.Assign(protocol, port) | 195 | return t.upnp.Assign(protocol, port) |
190 | } | 196 | } |
191 | 197 | ||
192 | // ForwardClose closes a specific port forwarding | 198 | // ForwardClose closes a specific port forwarding |
193 | func (t *Transport) ForwardClose(id string) error { | 199 | func (t *Transport) ForwardClose(id string) error { |
200 | // check for available UPnP | ||
201 | if t.upnp == nil { | ||
202 | return ErrTransNoUPNP | ||
203 | } | ||
194 | return t.upnp.Unassign(id) | 204 | return t.upnp.Unassign(id) |
195 | } | 205 | } |
diff --git a/src/gnunet/util/address.go b/src/gnunet/util/address.go index 4cd07da..35e0030 100644 --- a/src/gnunet/util/address.go +++ b/src/gnunet/util/address.go | |||
@@ -27,10 +27,10 @@ import ( | |||
27 | 27 | ||
28 | // Address specifies how a peer is reachable on the network. | 28 | // Address specifies how a peer is reachable on the network. |
29 | type Address struct { | 29 | type Address struct { |
30 | Netw string `` // network protocol | 30 | Netw string // network protocol |
31 | Options uint32 `order:"big"` // address options | 31 | Options uint32 // address options |
32 | Expires AbsoluteTime `` // expiration date for address | 32 | Expires AbsoluteTime // expiration date for address |
33 | Address []byte `size:"*"` // address data (protocol-dependent) | 33 | Address []byte // address data (protocol-dependent) |
34 | } | 34 | } |
35 | 35 | ||
36 | // NewAddress returns a new Address for the given transport and specs | 36 | // NewAddress returns a new Address for the given transport and specs |
@@ -43,6 +43,8 @@ func NewAddress(transport string, addr string) *Address { | |||
43 | } | 43 | } |
44 | } | 44 | } |
45 | 45 | ||
46 | // NewAddressWrap returns new address from net.Addr with no options | ||
47 | // or expiry date. | ||
46 | func NewAddressWrap(addr net.Addr) *Address { | 48 | func NewAddressWrap(addr net.Addr) *Address { |
47 | return &Address{ | 49 | return &Address{ |
48 | Netw: addr.Network(), | 50 | Netw: addr.Network(), |
@@ -53,7 +55,7 @@ func NewAddressWrap(addr net.Addr) *Address { | |||
53 | } | 55 | } |
54 | 56 | ||
55 | // ParseAddress translates a GNUnet address string like | 57 | // ParseAddress translates a GNUnet address string like |
56 | // "r5n+ip+udp://1.2.3.4:6789" or "gnunet+tcp://12.3.4.5/". | 58 | // "ip+udp://1.2.3.4:6789" or "gnunet+tcp://12.3.4.5/". |
57 | // It can also handle standard strings like "udp:127.0.0.1:6735". | 59 | // It can also handle standard strings like "udp:127.0.0.1:6735". |
58 | func ParseAddress(s string) (addr *Address, err error) { | 60 | func ParseAddress(s string) (addr *Address, err error) { |
59 | p := strings.SplitN(s, ":", 2) | 61 | p := strings.SplitN(s, ":", 2) |
@@ -72,11 +74,6 @@ func (a *Address) Equals(b *Address) bool { | |||
72 | bytes.Equal(a.Address, b.Address) | 74 | bytes.Equal(a.Address, b.Address) |
73 | } | 75 | } |
74 | 76 | ||
75 | // StringAll returns a human-readable representation of an address. | ||
76 | func (a *Address) StringAll() string { | ||
77 | return a.Netw + "://" + string(a.Address) | ||
78 | } | ||
79 | |||
80 | // implement net.Addr interface methods: | 77 | // implement net.Addr interface methods: |
81 | 78 | ||
82 | // String returns a human-readable representation of an address. | 79 | // String returns a human-readable representation of an address. |
@@ -91,7 +88,7 @@ func (a *Address) Network() string { | |||
91 | 88 | ||
92 | //---------------------------------------------------------------------- | 89 | //---------------------------------------------------------------------- |
93 | 90 | ||
94 | // URI returns a string representaion of an address. | 91 | // URI returns a string representation of an address. |
95 | func (a *Address) URI() string { | 92 | func (a *Address) URI() string { |
96 | return URI(a.Netw, a.Address) | 93 | return URI(a.Netw, a.Address) |
97 | } | 94 | } |
@@ -101,24 +98,6 @@ func URI(network string, addr []byte) string { | |||
101 | 98 | ||
102 | //---------------------------------------------------------------------- | 99 | //---------------------------------------------------------------------- |
103 | 100 | ||
104 | // IPAddress (can be IPv4 or IPv6 or a DNS name) | ||
105 | type IPAddress struct { | ||
106 | Host []byte `size:"*-2"` | ||
107 | Port uint16 `order:"big"` | ||
108 | } | ||
109 | |||
110 | // NewIPAddress creates a new instance for a given host and port. | ||
111 | func NewIPAddress(host []byte, port uint16) *IPAddress { | ||
112 | ip := &IPAddress{ | ||
113 | Host: make([]byte, len(host)), | ||
114 | Port: port, | ||
115 | } | ||
116 | copy(ip.Host, host) | ||
117 | return ip | ||
118 | } | ||
119 | |||
120 | //---------------------------------------------------------------------- | ||
121 | |||
122 | // PeerAddrList is a list of addresses per peer ID. | 101 | // PeerAddrList is a list of addresses per peer ID. |
123 | type PeerAddrList struct { | 102 | type PeerAddrList struct { |
124 | list *Map[string, []*Address] | 103 | list *Map[string, []*Address] |
@@ -133,12 +112,13 @@ func NewPeerAddrList() *PeerAddrList { | |||
133 | 112 | ||
134 | // Add address for peer. The returned mode is 0=not added, 1=new peer, | 113 | // Add address for peer. The returned mode is 0=not added, 1=new peer, |
135 | // 2=new address | 114 | // 2=new address |
136 | func (a *PeerAddrList) Add(id string, addr *Address) (mode int) { | 115 | func (a *PeerAddrList) Add(peer *PeerID, addr *Address) (mode int) { |
137 | // check for expired address. | 116 | // check for expired address. |
138 | mode = 0 | 117 | mode = 0 |
139 | if !addr.Expires.Expired() { | 118 | if !addr.Expires.Expired() { |
140 | // run add operation | 119 | // run add operation |
141 | a.list.Process(func() error { | 120 | _ = a.list.Process(func() error { |
121 | id := peer.String() | ||
142 | list, ok := a.list.Get(id) | 122 | list, ok := a.list.Get(id) |
143 | if !ok { | 123 | if !ok { |
144 | list = make([]*Address, 0) | 124 | list = make([]*Address, 0) |
@@ -160,7 +140,8 @@ func (a *PeerAddrList) Add(id string, addr *Address) (mode int) { | |||
160 | } | 140 | } |
161 | 141 | ||
162 | // Get address for peer | 142 | // Get address for peer |
163 | func (a *PeerAddrList) Get(id string, transport string) (res []*Address) { | 143 | func (a *PeerAddrList) Get(peer *PeerID, transport string) (res []*Address) { |
144 | id := peer.String() | ||
164 | list, ok := a.list.Get(id) | 145 | list, ok := a.list.Get(id) |
165 | if ok { | 146 | if ok { |
166 | for _, addr := range list { | 147 | for _, addr := range list { |
@@ -181,6 +162,13 @@ func (a *PeerAddrList) Get(id string, transport string) (res []*Address) { | |||
181 | } | 162 | } |
182 | 163 | ||
183 | // Delete a list entry by key. | 164 | // Delete a list entry by key. |
184 | func (a *PeerAddrList) Delete(id string) { | 165 | func (a *PeerAddrList) Delete(peer *PeerID) { |
185 | a.list.Delete(id) | 166 | a.list.Delete(peer.String()) |
167 | } | ||
168 | |||
169 | // Contains checks if a peer is contained in the list. Does not check | ||
170 | // for expired entries. | ||
171 | func (a *PeerAddrList) Contains(peer *PeerID) (ok bool) { | ||
172 | _, ok = a.list.Get(peer.String()) | ||
173 | return | ||
186 | } | 174 | } |
diff --git a/src/gnunet/util/address_test.go b/src/gnunet/util/address_test.go index d4936e8..1222124 100644 --- a/src/gnunet/util/address_test.go +++ b/src/gnunet/util/address_test.go | |||
@@ -37,17 +37,24 @@ func TestAddrList(t *testing.T) { | |||
37 | t.Fatal(err) | 37 | t.Fatal(err) |
38 | } | 38 | } |
39 | } | 39 | } |
40 | // test peer | ||
41 | peer := NewPeerID(nil) | ||
40 | // allocate AddrList | 42 | // allocate AddrList |
41 | addrL := NewPeerAddrList() | 43 | addrL := NewPeerAddrList() |
42 | for _, addr := range addrA { | 44 | for _, addr := range addrA { |
43 | rc := addrL.Add("2BHV4BN8736W5W3CJNXY2S9WABWTGH35QMFG4BPCWBH7DNBCFC60", addr) | 45 | rc := addrL.Add(peer, addr) |
44 | t.Logf("added %s (%d)", addr.URI(), rc) | 46 | t.Logf("added %s (%d)", addr.URI(), rc) |
45 | } | 47 | } |
46 | 48 | ||
47 | // check list | 49 | // check list |
48 | t.Log("checking list...") | 50 | t.Log("checking list...") |
49 | list := addrL.Get("2BHV4BN8736W5W3CJNXY2S9WABWTGH35QMFG4BPCWBH7DNBCFC60", "ip+udp") | 51 | list := addrL.Get(peer, "ip+udp") |
50 | t.Logf("got: %v", list) | 52 | for i, addr := range list { |
53 | t.Logf("got: %s", addr.URI()) | ||
54 | if addr != addrA[i] { | ||
55 | t.Errorf("address mismatch at index %d", i) | ||
56 | } | ||
57 | } | ||
51 | if len(list) != len(addrS) { | 58 | if len(list) != len(addrS) { |
52 | t.Fatal("list size not matching") | 59 | t.Fatal("list size not matching") |
53 | } | 60 | } |
diff --git a/src/gnunet/util/array.go b/src/gnunet/util/array.go index 99c74d9..954521c 100644 --- a/src/gnunet/util/array.go +++ b/src/gnunet/util/array.go | |||
@@ -33,6 +33,11 @@ var ( | |||
33 | 33 | ||
34 | // Clone creates a new array of same content as the argument. | 34 | // Clone creates a new array of same content as the argument. |
35 | func Clone[T []E, E any](d T) T { | 35 | func Clone[T []E, E any](d T) T { |
36 | // handle nil slices | ||
37 | if d == nil { | ||
38 | return nil | ||
39 | } | ||
40 | // create copy | ||
36 | r := make(T, len(d)) | 41 | r := make(T, len(d)) |
37 | copy(r, d) | 42 | copy(r, d) |
38 | return r | 43 | return r |
diff --git a/src/gnunet/util/base32.go b/src/gnunet/util/base32.go index f0b149a..e9ca494 100644 --- a/src/gnunet/util/base32.go +++ b/src/gnunet/util/base32.go | |||
@@ -46,9 +46,9 @@ const xlate = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" | |||
46 | 46 | ||
47 | var ( | 47 | var ( |
48 | // ErrInvalidEncoding signals an invalid encoding | 48 | // ErrInvalidEncoding signals an invalid encoding |
49 | ErrInvalidEncoding = errors.New("Invalid encoding") | 49 | ErrInvalidEncoding = errors.New("invalid encoding") |
50 | // ErrBufferTooSmall signalsa too small buffer for decoding | 50 | // ErrBufferTooSmall signalsa too small buffer for decoding |
51 | ErrBufferTooSmall = errors.New("Buffer to small") | 51 | ErrBufferTooSmall = errors.New("buffer to small") |
52 | ) | 52 | ) |
53 | 53 | ||
54 | // EncodeBinaryToString encodes a byte array into a string. | 54 | // EncodeBinaryToString encodes a byte array into a string. |
diff --git a/src/gnunet/util/base32_test.go b/src/gnunet/util/base32_test.go index 2ec7231..32ea2de 100644 --- a/src/gnunet/util/base32_test.go +++ b/src/gnunet/util/base32_test.go | |||
@@ -73,7 +73,7 @@ func TestBase32Preset(t *testing.T) { | |||
73 | if err != nil { | 73 | if err != nil { |
74 | t.Fatal(err) | 74 | t.Fatal(err) |
75 | } | 75 | } |
76 | if bytes.Compare(x.bin, e) != 0 { | 76 | if !bytes.Equal(x.bin, e) { |
77 | t.Fatalf("Decoding mismatch: '%s' != '%s' for '%s'\n", hex.EncodeToString(e), hex.EncodeToString(x.bin), x.str) | 77 | t.Fatalf("Decoding mismatch: '%s' != '%s' for '%s'\n", hex.EncodeToString(e), hex.EncodeToString(x.bin), x.str) |
78 | } | 78 | } |
79 | } | 79 | } |
diff --git a/src/gnunet/util/fs.go b/src/gnunet/util/fs.go index 3df641c..00ffa28 100644 --- a/src/gnunet/util/fs.go +++ b/src/gnunet/util/fs.go | |||
@@ -37,7 +37,7 @@ func EnforceDirExists(path string) error { | |||
37 | return err | 37 | return err |
38 | } | 38 | } |
39 | if !fi.IsDir() { | 39 | if !fi.IsDir() { |
40 | return fmt.Errorf("Not a directory (%s)", path) | 40 | return fmt.Errorf("not a directory (%s)", path) |
41 | } | 41 | } |
42 | return nil | 42 | return nil |
43 | } | 43 | } |
diff --git a/src/gnunet/util/map.go b/src/gnunet/util/map.go new file mode 100644 index 0000000..adfcc0e --- /dev/null +++ b/src/gnunet/util/map.go | |||
@@ -0,0 +1,151 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package util | ||
20 | |||
21 | import ( | ||
22 | "math/rand" | ||
23 | "sync" | ||
24 | ) | ||
25 | |||
26 | //---------------------------------------------------------------------- | ||
27 | // Thread-safe map implementation | ||
28 | //---------------------------------------------------------------------- | ||
29 | |||
30 | // Map keys to values | ||
31 | type Map[K comparable, V any] struct { | ||
32 | sync.RWMutex | ||
33 | |||
34 | list map[K]V | ||
35 | inProcess bool | ||
36 | } | ||
37 | |||
38 | // NewMap allocates a new mapping. | ||
39 | func NewMap[K comparable, V any]() *Map[K, V] { | ||
40 | return &Map[K, V]{ | ||
41 | list: make(map[K]V), | ||
42 | inProcess: false, | ||
43 | } | ||
44 | } | ||
45 | |||
46 | //---------------------------------------------------------------------- | ||
47 | |||
48 | // Process a function in the locked map context. Calls | ||
49 | // to other map functions in 'f' will skip their locks. | ||
50 | func (m *Map[K, V]) Process(f func() error, readonly bool) error { | ||
51 | // handle locking | ||
52 | m.lock(readonly) | ||
53 | m.inProcess = true | ||
54 | defer func() { | ||
55 | m.inProcess = false | ||
56 | m.unlock(readonly) | ||
57 | }() | ||
58 | // function call in unlocked environment | ||
59 | return f() | ||
60 | } | ||
61 | |||
62 | // Process a ranged function in the locked map context. Calls | ||
63 | // to other map functions in 'f' will skip their locks. | ||
64 | func (m *Map[K, V]) ProcessRange(f func(key K, value V) error, readonly bool) error { | ||
65 | // handle locking | ||
66 | m.lock(readonly) | ||
67 | m.inProcess = true | ||
68 | defer func() { | ||
69 | m.inProcess = false | ||
70 | m.unlock(readonly) | ||
71 | }() | ||
72 | // range over map and call function. | ||
73 | for key, value := range m.list { | ||
74 | if err := f(key, value); err != nil { | ||
75 | return err | ||
76 | } | ||
77 | } | ||
78 | return nil | ||
79 | } | ||
80 | |||
81 | //---------------------------------------------------------------------- | ||
82 | |||
83 | // Size returns the number of entries in the map. | ||
84 | func (m *Map[K, V]) Size() int { | ||
85 | return len(m.list) | ||
86 | } | ||
87 | |||
88 | // Put value into map under given key. | ||
89 | func (m *Map[K, V]) Put(key K, value V) { | ||
90 | m.lock(false) | ||
91 | defer m.unlock(false) | ||
92 | m.list[key] = value | ||
93 | } | ||
94 | |||
95 | // Get value with iven key from map. | ||
96 | func (m *Map[K, V]) Get(key K) (value V, ok bool) { | ||
97 | m.lock(true) | ||
98 | defer m.unlock(true) | ||
99 | value, ok = m.list[key] | ||
100 | return | ||
101 | } | ||
102 | |||
103 | // GetRandom returns a random map entry. | ||
104 | func (m *Map[K, V]) GetRandom() (key K, value V, ok bool) { | ||
105 | m.lock(true) | ||
106 | defer m.unlock(true) | ||
107 | |||
108 | ok = false | ||
109 | if size := m.Size(); size > 0 { | ||
110 | idx := rand.Intn(size) | ||
111 | for key, value = range m.list { | ||
112 | if idx == 0 { | ||
113 | ok = true | ||
114 | return | ||
115 | } | ||
116 | idx-- | ||
117 | } | ||
118 | } | ||
119 | return | ||
120 | } | ||
121 | |||
122 | // Delete key/value pair from map. | ||
123 | func (m *Map[K, V]) Delete(key K) { | ||
124 | m.lock(false) | ||
125 | defer m.unlock(false) | ||
126 | delete(m.list, key) | ||
127 | } | ||
128 | |||
129 | //---------------------------------------------------------------------- | ||
130 | |||
131 | // lock with given mode (if not in processing function) | ||
132 | func (m *Map[K, V]) lock(readonly bool) { | ||
133 | if !m.inProcess { | ||
134 | if readonly { | ||
135 | m.RLock() | ||
136 | } else { | ||
137 | m.Lock() | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | |||
142 | // lock with given mode (if not in processing function) | ||
143 | func (m *Map[K, V]) unlock(readonly bool) { | ||
144 | if !m.inProcess { | ||
145 | if readonly { | ||
146 | m.RUnlock() | ||
147 | } else { | ||
148 | m.Unlock() | ||
149 | } | ||
150 | } | ||
151 | } | ||
diff --git a/src/gnunet/util/misc.go b/src/gnunet/util/misc.go index 2ee8f1d..1768a96 100644 --- a/src/gnunet/util/misc.go +++ b/src/gnunet/util/misc.go | |||
@@ -20,11 +20,10 @@ package util | |||
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "strings" | 22 | "strings" |
23 | "sync" | ||
24 | ) | 23 | ) |
25 | 24 | ||
26 | //---------------------------------------------------------------------- | 25 | //---------------------------------------------------------------------- |
27 | // Count occurence of multiple instance at the same time. | 26 | // Count occurrence of multiple instance at the same time. |
28 | //---------------------------------------------------------------------- | 27 | //---------------------------------------------------------------------- |
29 | 28 | ||
30 | // Counter is a metric with single key | 29 | // Counter is a metric with single key |
@@ -52,107 +51,23 @@ func (cm Counter[T]) Num(i T) int { | |||
52 | } | 51 | } |
53 | 52 | ||
54 | //---------------------------------------------------------------------- | 53 | //---------------------------------------------------------------------- |
55 | // Thread-safe map implementation | 54 | // Parameter set with string keys and variable value types |
56 | //---------------------------------------------------------------------- | 55 | //---------------------------------------------------------------------- |
57 | 56 | ||
58 | // Map keys to values | 57 | // ParameterSet with string keys and variable value types |
59 | type Map[K comparable, V any] struct { | 58 | type ParameterSet map[string]any |
60 | list map[K]V | ||
61 | mtx sync.RWMutex | ||
62 | inProcess bool | ||
63 | } | ||
64 | |||
65 | // NewMap allocates a new mapping. | ||
66 | func NewMap[K comparable, V any]() *Map[K, V] { | ||
67 | return &Map[K, V]{ | ||
68 | list: make(map[K]V), | ||
69 | inProcess: false, | ||
70 | } | ||
71 | } | ||
72 | 59 | ||
73 | //---------------------------------------------------------------------- | 60 | // Get a parameter value with given type 'V' |
74 | 61 | func GetParam[V any](params ParameterSet, key string) (i V, ok bool) { | |
75 | // Process a function in the locked map context. Calls | 62 | var v any |
76 | // to other map functions in 'f' will skip their locks. | 63 | if v, ok = params[key]; ok { |
77 | func (m *Map[K, V]) Process(f func() error, readonly bool) error { | 64 | if i, ok = v.(V); ok { |
78 | // handle locking | 65 | return |
79 | m.lock(readonly) | ||
80 | m.inProcess = true | ||
81 | defer func() { | ||
82 | m.inProcess = false | ||
83 | m.unlock(readonly) | ||
84 | }() | ||
85 | // function call in unlocked environment | ||
86 | return f() | ||
87 | } | ||
88 | |||
89 | // Process a ranged function in the locked map context. Calls | ||
90 | // to other map functions in 'f' will skip their locks. | ||
91 | func (m *Map[K, V]) ProcessRange(f func(key K, value V) error, readonly bool) error { | ||
92 | // handle locking | ||
93 | m.lock(readonly) | ||
94 | m.inProcess = true | ||
95 | defer func() { | ||
96 | m.inProcess = false | ||
97 | m.unlock(readonly) | ||
98 | }() | ||
99 | // range over map and call function. | ||
100 | for key, value := range m.list { | ||
101 | if err := f(key, value); err != nil { | ||
102 | return err | ||
103 | } | 66 | } |
104 | } | 67 | } |
105 | return nil | ||
106 | } | ||
107 | |||
108 | //---------------------------------------------------------------------- | ||
109 | |||
110 | // Put value into map under given key. | ||
111 | func (m *Map[K, V]) Put(key K, value V) { | ||
112 | m.lock(false) | ||
113 | defer m.unlock(false) | ||
114 | m.list[key] = value | ||
115 | } | ||
116 | |||
117 | // Get value with iven key from map. | ||
118 | func (m *Map[K, V]) Get(key K) (value V, ok bool) { | ||
119 | m.lock(true) | ||
120 | defer m.unlock(true) | ||
121 | value, ok = m.list[key] | ||
122 | return | 68 | return |
123 | } | 69 | } |
124 | 70 | ||
125 | // Delete key/value pair from map. | ||
126 | func (m *Map[K, V]) Delete(key K) { | ||
127 | m.lock(false) | ||
128 | defer m.unlock(false) | ||
129 | delete(m.list, key) | ||
130 | } | ||
131 | |||
132 | //---------------------------------------------------------------------- | ||
133 | |||
134 | // lock with given mode (if not in processing function) | ||
135 | func (m *Map[K, V]) lock(readonly bool) { | ||
136 | if !m.inProcess { | ||
137 | if readonly { | ||
138 | m.mtx.RLock() | ||
139 | } else { | ||
140 | m.mtx.Lock() | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | // lock with given mode (if not in processing function) | ||
146 | func (m *Map[K, V]) unlock(readonly bool) { | ||
147 | if !m.inProcess { | ||
148 | if readonly { | ||
149 | m.mtx.RUnlock() | ||
150 | } else { | ||
151 | m.mtx.Unlock() | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | //---------------------------------------------------------------------- | 71 | //---------------------------------------------------------------------- |
157 | // additional helpers | 72 | // additional helpers |
158 | //---------------------------------------------------------------------- | 73 | //---------------------------------------------------------------------- |
diff --git a/src/gnunet/util/peer.go b/src/gnunet/util/peer.go new file mode 100644 index 0000000..f880040 --- /dev/null +++ b/src/gnunet/util/peer.go | |||
@@ -0,0 +1,94 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package util | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | ) | ||
24 | |||
25 | //---------------------------------------------------------------------- | ||
26 | // Peer public key (Ed25519 public key) | ||
27 | //---------------------------------------------------------------------- | ||
28 | |||
29 | // PeerPublicKey is the binary representation of an Ed25519 public key | ||
30 | type PeerPublicKey struct { | ||
31 | Data []byte `size:"32"` // Ed25519 public key data | ||
32 | } | ||
33 | |||
34 | // NewPeerPublicKey creates a key instance from binary data | ||
35 | func NewPeerPublicKey(data []byte) *PeerPublicKey { | ||
36 | pk := &PeerPublicKey{ | ||
37 | Data: make([]byte, 32), | ||
38 | } | ||
39 | if data != nil { | ||
40 | if len(data) < 32 { | ||
41 | CopyAlignedBlock(pk.Data, data) | ||
42 | } else { | ||
43 | copy(pk.Data, data[:32]) | ||
44 | } | ||
45 | } | ||
46 | return pk | ||
47 | } | ||
48 | |||
49 | //---------------------------------------------------------------------- | ||
50 | // Peer identifier: | ||
51 | //---------------------------------------------------------------------- | ||
52 | |||
53 | // PeerID is a wrpped PeerPublicKey | ||
54 | type PeerID PeerPublicKey | ||
55 | |||
56 | // NewPeerID creates a new peer id from data. | ||
57 | func NewPeerID(data []byte) (p *PeerID) { | ||
58 | return (*PeerID)(NewPeerPublicKey(data)) | ||
59 | } | ||
60 | |||
61 | // Equals returns true if two peer IDs match. | ||
62 | func (p *PeerID) Equals(q *PeerID) bool { | ||
63 | return bytes.Equal(p.Data, q.Data) | ||
64 | } | ||
65 | |||
66 | // String returns a human-readable representation of a peer id. | ||
67 | func (p *PeerID) String() string { | ||
68 | return EncodeBinaryToString(p.Data) | ||
69 | } | ||
70 | |||
71 | // Bytes returns the binary representation of a peer identifier. | ||
72 | func (p *PeerID) Bytes() []byte { | ||
73 | return Clone(p.Data) | ||
74 | } | ||
75 | |||
76 | //---------------------------------------------------------------------- | ||
77 | |||
78 | // PeerSignature is a EdDSA signature from the peer | ||
79 | type PeerSignature struct { | ||
80 | Data []byte `size:"64"` | ||
81 | } | ||
82 | |||
83 | // NewPeerSignature is a EdDSA signatre with the private peer key | ||
84 | func NewPeerSignature(data []byte) *PeerSignature { | ||
85 | var v []byte | ||
86 | if data == nil { | ||
87 | v = make([]byte, 64) | ||
88 | } else { | ||
89 | v = Clone(data) | ||
90 | } | ||
91 | return &PeerSignature{ | ||
92 | Data: v, | ||
93 | } | ||
94 | } | ||
diff --git a/src/gnunet/util/peer_id.go b/src/gnunet/util/peer_id.go deleted file mode 100644 index 384f46c..0000000 --- a/src/gnunet/util/peer_id.go +++ /dev/null | |||
@@ -1,59 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package util | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | |||
24 | "github.com/bfix/gospel/crypto/ed25519" | ||
25 | ) | ||
26 | |||
27 | // PeerID is the 32-byte binary representation od a Ed25519 key | ||
28 | type PeerID struct { | ||
29 | Key []byte `size:"32"` | ||
30 | } | ||
31 | |||
32 | // NewPeerID creates a new peer id from data. | ||
33 | func NewPeerID(data []byte) (p *PeerID) { | ||
34 | p = &PeerID{ | ||
35 | Key: make([]byte, 32), | ||
36 | } | ||
37 | if data != nil { | ||
38 | if len(data) < 32 { | ||
39 | CopyAlignedBlock(p.Key, data) | ||
40 | } else { | ||
41 | copy(p.Key, data[:32]) | ||
42 | } | ||
43 | } | ||
44 | return | ||
45 | } | ||
46 | |||
47 | // Equals returns true if two peer IDs match. | ||
48 | func (p *PeerID) Equals(q *PeerID) bool { | ||
49 | return bytes.Equal(p.Key, q.Key) | ||
50 | } | ||
51 | |||
52 | // String returns a human-readable representation of a peer id. | ||
53 | func (p *PeerID) String() string { | ||
54 | return EncodeBinaryToString(p.Key) | ||
55 | } | ||
56 | |||
57 | func (p *PeerID) PublicKey() *ed25519.PublicKey { | ||
58 | return ed25519.NewPublicKeyFromBytes(p.Key) | ||
59 | } | ||
diff --git a/src/gnunet/util/rnd.go b/src/gnunet/util/rnd.go index c01f331..5bf6bd0 100644 --- a/src/gnunet/util/rnd.go +++ b/src/gnunet/util/rnd.go | |||
@@ -22,17 +22,21 @@ import ( | |||
22 | "bytes" | 22 | "bytes" |
23 | "crypto/rand" | 23 | "crypto/rand" |
24 | "encoding/binary" | 24 | "encoding/binary" |
25 | |||
26 | "github.com/bfix/gospel/logger" | ||
25 | ) | 27 | ) |
26 | 28 | ||
27 | // RndArray fills a buffer with random content | 29 | // RndArray fills a buffer with random content |
28 | func RndArray(b []byte) { | 30 | func RndArray(b []byte) { |
29 | rand.Read(b) | 31 | if _, err := rand.Read(b); err != nil { |
32 | logger.Printf(logger.ERROR, "[RndArray] failed: %s", err.Error()) | ||
33 | } | ||
30 | } | 34 | } |
31 | 35 | ||
32 | // NewRndArray creates a new buffer of given size; filled with random content. | 36 | // NewRndArray creates a new buffer of given size; filled with random content. |
33 | func NewRndArray(size int) []byte { | 37 | func NewRndArray(size int) []byte { |
34 | b := make([]byte, size) | 38 | b := make([]byte, size) |
35 | rand.Read(b) | 39 | RndArray(b) |
36 | return b | 40 | return b |
37 | } | 41 | } |
38 | 42 | ||
@@ -42,7 +46,9 @@ func RndUInt64() uint64 { | |||
42 | RndArray(b) | 46 | RndArray(b) |
43 | var v uint64 | 47 | var v uint64 |
44 | c := bytes.NewBuffer(b) | 48 | c := bytes.NewBuffer(b) |
45 | binary.Read(c, binary.BigEndian, &v) | 49 | if err := binary.Read(c, binary.BigEndian, &v); err != nil { |
50 | logger.Printf(logger.ERROR, "[RndUInt64] failed: %s", err.Error()) | ||
51 | } | ||
46 | return v | 52 | return v |
47 | } | 53 | } |
48 | 54 | ||
diff --git a/src/gnunet/util/time.go b/src/gnunet/util/time.go index 9a9d365..6e98514 100644 --- a/src/gnunet/util/time.go +++ b/src/gnunet/util/time.go | |||
@@ -46,7 +46,7 @@ func NewAbsoluteTime(t time.Time) AbsoluteTime { | |||
46 | // NewAbsoluteTimeEpoch set the point in time to the given time value | 46 | // NewAbsoluteTimeEpoch set the point in time to the given time value |
47 | func NewAbsoluteTimeEpoch(secs uint64) AbsoluteTime { | 47 | func NewAbsoluteTimeEpoch(secs uint64) AbsoluteTime { |
48 | return AbsoluteTime{ | 48 | return AbsoluteTime{ |
49 | Val: uint64(secs * 1000000), | 49 | Val: secs * 1000000, |
50 | } | 50 | } |
51 | } | 51 | } |
52 | 52 | ||
@@ -137,8 +137,9 @@ func (t AbsoluteTime) Compare(t2 AbsoluteTime) int { | |||
137 | // Relative time | 137 | // Relative time |
138 | //---------------------------------------------------------------------- | 138 | //---------------------------------------------------------------------- |
139 | 139 | ||
140 | // RelativeTime is a timestamp defined relative to the current time. | 140 | // RelativeTime is a timestamp defined relative to an AbsoluteTime. |
141 | // It actually is more like a duration than a time... | 141 | // It is measured in microseconds and is actually more like a duration |
142 | // than a time... | ||
142 | type RelativeTime struct { | 143 | type RelativeTime struct { |
143 | Val uint64 `order:"big"` | 144 | Val uint64 `order:"big"` |
144 | } | 145 | } |
@@ -146,7 +147,7 @@ type RelativeTime struct { | |||
146 | // NewRelativeTime is initialized with a given duration. | 147 | // NewRelativeTime is initialized with a given duration. |
147 | func NewRelativeTime(d time.Duration) RelativeTime { | 148 | func NewRelativeTime(d time.Duration) RelativeTime { |
148 | return RelativeTime{ | 149 | return RelativeTime{ |
149 | Val: uint64(d.Milliseconds()), | 150 | Val: uint64(d.Microseconds()), |
150 | } | 151 | } |
151 | } | 152 | } |
152 | 153 | ||
@@ -155,12 +156,14 @@ func (t RelativeTime) String() string { | |||
155 | if t.Val == math.MaxUint64 { | 156 | if t.Val == math.MaxUint64 { |
156 | return "Forever" | 157 | return "Forever" |
157 | } | 158 | } |
158 | return time.Duration(t.Val * 1000).String() | 159 | return time.Duration(t.Val * 1000000).String() |
159 | } | 160 | } |
160 | 161 | ||
161 | // Add two durations | 162 | // Add two durations |
162 | func (t RelativeTime) Add(t2 RelativeTime) { | 163 | func (t RelativeTime) Add(t2 RelativeTime) RelativeTime { |
163 | t.Val += t2.Val | 164 | return RelativeTime{ |
165 | Val: t.Val + t2.Val, | ||
166 | } | ||
164 | } | 167 | } |
165 | 168 | ||
166 | // Compare two durations | 169 | // Compare two durations |