aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBernd Fix <brf@hoi-polloi.org>2022-07-18 11:16:34 +0200
committerBernd Fix <brf@hoi-polloi.org>2022-07-18 11:16:34 +0200
commitf425c2aeef06d1a6105678c8b058bdde65a26e78 (patch)
tree1868bb9c2b343aca55108e97671631178521a4f0 /src
parent4261e07def81e7c3eb183b9d5c4059a2e9c53759 (diff)
downloadgnunet-go-f425c2aeef06d1a6105678c8b058bdde65a26e78.tar.gz
gnunet-go-f425c2aeef06d1a6105678c8b058bdde65a26e78.zip
Improved handling of pending DHT results.
Diffstat (limited to 'src')
-rwxr-xr-xsrc/gnunet/build.sh2
-rw-r--r--src/gnunet/cmd/gnunet-service-dht-go/main.go33
-rw-r--r--src/gnunet/cmd/gnunet-service-gns-go/main.go9
-rw-r--r--src/gnunet/cmd/gnunet-service-revocation-go/main.go9
-rw-r--r--src/gnunet/cmd/peer_mockup/main.go40
-rw-r--r--src/gnunet/cmd/revoke-zonekey/main.go80
-rw-r--r--src/gnunet/cmd/vanityid/main.go10
-rw-r--r--src/gnunet/config/config.go56
-rw-r--r--src/gnunet/config/config_test.go2
-rw-r--r--src/gnunet/config/gnunet-config.json14
-rw-r--r--src/gnunet/core/core.go186
-rw-r--r--src/gnunet/core/core_test.go32
-rw-r--r--src/gnunet/core/event.go17
-rw-r--r--src/gnunet/core/hello_test.go1
-rw-r--r--src/gnunet/core/peer.go13
-rw-r--r--src/gnunet/core/peer_test.go8
-rw-r--r--src/gnunet/crypto/gns.go109
-rw-r--r--src/gnunet/crypto/gns_edkey.go46
-rw-r--r--src/gnunet/crypto/gns_pkey.go45
-rw-r--r--src/gnunet/crypto/gns_test.go18
-rw-r--r--src/gnunet/crypto/hash.go13
-rw-r--r--src/gnunet/crypto/key_exchange_test.go52
-rw-r--r--src/gnunet/crypto/keys_test.go2
-rw-r--r--src/gnunet/enums/blocktype_string.go2
-rw-r--r--src/gnunet/enums/dht.go14
-rw-r--r--src/gnunet/enums/dht_block_type.go3
-rw-r--r--src/gnunet/enums/gns.go3
-rw-r--r--src/gnunet/enums/gns_type.go1
-rw-r--r--src/gnunet/enums/gnunet-dht.tpl3
-rw-r--r--src/gnunet/enums/gnunet-gns.tpl1
-rw-r--r--src/gnunet/enums/gnunet-signature.tpl1
-rw-r--r--src/gnunet/enums/signature_purpose.go1
-rw-r--r--src/gnunet/go.mod3
-rw-r--r--src/gnunet/go.sum6
-rw-r--r--src/gnunet/message/factory.go12
-rw-r--r--src/gnunet/message/msg_core.go30
-rw-r--r--src/gnunet/message/msg_dht_p2p.go473
-rw-r--r--src/gnunet/message/msg_hello.go20
-rw-r--r--src/gnunet/message/msg_hello_dht.go167
-rw-r--r--src/gnunet/message/msg_namecache.go2
-rw-r--r--src/gnunet/message/msg_transport.go13
-rw-r--r--src/gnunet/message/types.go5
-rw-r--r--src/gnunet/service/client.go1
-rw-r--r--src/gnunet/service/connection.go42
-rw-r--r--src/gnunet/service/dht/blocks/filters.go296
-rw-r--r--src/gnunet/service/dht/blocks/filters_test.go110
-rw-r--r--src/gnunet/service/dht/blocks/generic.go64
-rw-r--r--src/gnunet/service/dht/blocks/generic_test.go67
-rw-r--r--src/gnunet/service/dht/blocks/gns.go31
-rw-r--r--src/gnunet/service/dht/blocks/handlers.go80
-rw-r--r--src/gnunet/service/dht/blocks/hello.go253
-rw-r--r--src/gnunet/service/dht/blocks/types.go26
-rw-r--r--src/gnunet/service/dht/bloomfilter.go123
-rw-r--r--src/gnunet/service/dht/messages.go380
-rw-r--r--src/gnunet/service/dht/module.go155
-rw-r--r--src/gnunet/service/dht/resulthandler.go351
-rw-r--r--src/gnunet/service/dht/routingtable.go305
-rw-r--r--src/gnunet/service/dht/routingtable_test.go42
-rw-r--r--src/gnunet/service/dht/rpc.go50
-rw-r--r--src/gnunet/service/dht/service.go56
-rw-r--r--src/gnunet/service/gns/block_handler.go6
-rw-r--r--src/gnunet/service/gns/dns.go4
-rw-r--r--src/gnunet/service/gns/module.go30
-rw-r--r--src/gnunet/service/gns/rpc.go4
-rw-r--r--src/gnunet/service/gns/service.go22
-rw-r--r--src/gnunet/service/module.go20
-rw-r--r--src/gnunet/service/namecache/module.go9
-rw-r--r--src/gnunet/service/revocation/module.go7
-rw-r--r--src/gnunet/service/revocation/pow.go27
-rw-r--r--src/gnunet/service/revocation/pow_test.go1
-rw-r--r--src/gnunet/service/revocation/rpc.go4
-rw-r--r--src/gnunet/service/revocation/service.go9
-rw-r--r--src/gnunet/service/rpc.go29
-rw-r--r--src/gnunet/service/service.go3
-rw-r--r--src/gnunet/service/store.go502
-rw-r--r--src/gnunet/service/store/database.go (renamed from src/gnunet/util/database.go)63
-rw-r--r--src/gnunet/service/store/dhtstore_test.go (renamed from src/gnunet/service/dht/dhtstore_test.go)15
-rw-r--r--src/gnunet/service/store/store.go278
-rw-r--r--src/gnunet/service/store/store_fs.go287
-rw-r--r--src/gnunet/service/store/store_fs_meta.go174
-rw-r--r--src/gnunet/service/store/store_fs_meta.sql29
-rw-r--r--src/gnunet/test/gnunet-dhtu/main.go206
-rw-r--r--src/gnunet/transport/endpoint.go31
-rw-r--r--src/gnunet/transport/reader_writer.go41
-rw-r--r--src/gnunet/transport/responder.go8
-rw-r--r--src/gnunet/transport/transport.go32
-rw-r--r--src/gnunet/util/address.go56
-rw-r--r--src/gnunet/util/address_test.go13
-rw-r--r--src/gnunet/util/array.go5
-rw-r--r--src/gnunet/util/base32.go4
-rw-r--r--src/gnunet/util/base32_test.go2
-rw-r--r--src/gnunet/util/fs.go2
-rw-r--r--src/gnunet/util/map.go151
-rw-r--r--src/gnunet/util/misc.go105
-rw-r--r--src/gnunet/util/peer.go94
-rw-r--r--src/gnunet/util/peer_id.go59
-rw-r--r--src/gnunet/util/rnd.go12
-rw-r--r--src/gnunet/util/time.go17
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
3go generate ./... 3[ "$1" = "withgen" ] && go generate ./...
4go install -v -gcflags "-N -l" ./... 4go 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
21import ( 21import (
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(&param, "p", "", "socket parameters (<key>=<value>,...)") 59 flag.StringVar(&param, "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
21import ( 21import (
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
21import ( 21import (
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.
119func process(ctx context.Context, ev *core.Event) { 121func 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
45const ( 45const (
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
106func (r *RevData) Write(filename string) (err error) { 105func (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
15func main() { 16func 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
21import ( 21import (
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.
63type BootstrapConfig struct { 64type 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.
103type ParameterConfig map[string]any
104
105// Get a parameter value with given type 'V'
106func 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)
121type DHTConfig struct { 103type 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
111type 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
131type NamecacheConfig struct { 120type 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
141type RevocationConfig struct { 130type 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.
154type Config struct { 143type 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
195var ( 184var (
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.
217func applySubstitutions(x interface{}, env map[string]string) { 206func 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
21import ( 21import (
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.
81func NewCore(ctx context.Context, node *config.NodeConfig) (c *Core, err error) { 81func 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
263func (c *Core) send(ctx context.Context, addr *util.Address, msg message.Message) error { 250func (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
271func (c *Core) Learn(ctx context.Context, peer *util.PeerID, addrs []*util.Address) (err error) { 258func (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
296func (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.
308func (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
347func (c *Core) Addresses() (list []*util.Address, err error) { 269func (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
291type 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
300func (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().
391func (c *Core) Drop(peer *util.PeerID) {} 336func (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.
397func (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
420func (c *Core) dispatch(ev *Event) { 357func (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
38func TestCoreSimple(t *testing.T) { 38func 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
106func TestCoreUPNP(t *testing.T) { 105func 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
255func NewTestNode(t *testing.T, ctx context.Context, cfg *config.NodeConfig) (node *TestNode, err error) { 255func 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 @@
19package core 19package core
20 20
21import ( 21import (
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
32const ( 34const (
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
86type Event struct { 90type 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.
99func (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
73func TestHelloURL(t *testing.T) { 73func 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
140func (p *Peer) GetID() *util.PeerID { 145func (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.
152func (p *Peer) Sign(msg []byte) (*ed25519.EdSignature, error) { 157func (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
45func TestPeerHello(t *testing.T) { 45func 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
146var ( 149var (
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
154var (
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
178var ( 183var (
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
207func NewZonePrivate(ztype uint32, d []byte) (*ZonePrivate, error) { 213func 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)
240func (zp *ZonePrivate) Derive(label, context string) (*ZonePrivate, *math.Int) { 248func (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
287func NewZoneKey(d []byte) (*ZoneKey, error) { 296func 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)
313func (zk *ZoneKey) Derive(label, context string) (*ZoneKey, *math.Int) { 323func (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
340func (zk *ZoneKey) Verify(data []byte, zs *ZoneSignature) (ok bool, err error) { 353func (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.
346func (zk *ZoneKey) ID() string { 361func (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
365func (zk *ZoneKey) withImpl() { 385func (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
385func NewZoneSignature(d []byte) (*ZoneSignature, error) { 406func 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.
79func (pk *EDKEYPublicImpl) Derive(h *math.Int) (ZoneKeyImpl, *math.Int) { 80func (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.
191func (pk *EDKEYPrivateImpl) Derive(h *math.Int) (ZonePrivateImpl, *math.Int) { 195func (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
206func (pk *EDKEYPrivateImpl) Sign(data []byte) (*ZoneSignature, error) { 210func (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.
79func (pk *PKEYPublicImpl) Derive(h *math.Int) (ZoneKeyImpl, *math.Int) { 80func (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.
187func (pk *PKEYPrivateImpl) Derive(h *math.Int) (ZonePrivateImpl, *math.Int) { 192func (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
202func (pk *PKEYPrivateImpl) Sign(data []byte) (*ZoneSignature, error) { 207func (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
299func TestHKDF_gnunet(t *testing.T) { 304func 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
21import ( 21import (
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
40func (hc *HashCode) Clone() *HashCode {
41 return NewHashCode(hc.Bits)
42}
43
44// String returns a hex-representation of the hash code
45func (hc *HashCode) String() string {
46 return hex.EncodeToString(hc.Bits)
47}
48
49// NewHashCode creates a new (initialized) hash value
39func NewHashCode(buf []byte) *HashCode { 50func 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
31var ( 31var (
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
64func calcSharedSecret() bool { 64func 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
75func TestDHE(t *testing.T) { 75func 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 (
58func TestPrvKey(t *testing.T) { 58func 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
41func (i BlockType) String() string { 41func (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
19package enums 20package enums
20 21
21// DHT flags and settings 22// DHT flags and settings
22const ( 23const (
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
3package enums 4package enums
4 5
5type BlockType int 6type BlockType uint16
6 7
7// DHT block types 8// DHT block types
8const ( 9const (
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
19package enums 20package enums
20 21
21const ( 22const (
@@ -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
3package enums 4package enums
4 5
5type GNSType int 6type 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
3package enums 4package enums
4 5
5type BlockType int 6type BlockType uint16
6 7
7// DHT block types 8// DHT block types
8const ( 9const (
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
3package enums 4package enums
4 5
5type GNSType int 6type 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
3package enums 4package enums
4 5
5type SigPurpose int 6type 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
3package enums 4package enums
4 5
5type SigPurpose int 6type 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
3go 1.18 3go 1.18
4 4
5require ( 5require (
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 @@
1github.com/bfix/gospel v1.2.14 h1:lIdagJvkebG+uYbVdfK6XbT1udnq/ezd/Gi54EaMtV0= 1github.com/bfix/gospel v1.2.15 h1:f0t8dvihSXWvhnXDI2q7FCtG7LHg5qImjEWdzIN/luY=
2github.com/bfix/gospel v1.2.14/go.mod h1:cdu63bA9ZdfeDoqZ+vnWOcbY9Puwdzmf5DMxMGMznRI= 2github.com/bfix/gospel v1.2.15/go.mod h1:cdu63bA9ZdfeDoqZ+vnWOcbY9Puwdzmf5DMxMGMznRI=
3github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 3github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
4github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 4github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
5github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 5github.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
11github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 11github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
12github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 12github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
13github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 13github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
14github.com/gorilla/rpc v1.2.0 h1:WvvdC2lNeT1SP32zrIce5l0ECBfbAlmrmSBsuc57wfk=
15github.com/gorilla/rpc v1.2.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
14github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= 16github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
15github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= 17github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
16github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= 18github.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.
45type EphemeralKeyMsg struct { 45type 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.
74func (m *EphemeralKeyMsg) String() string { 74func (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.
88func (m *EphemeralKeyMsg) Public() *ed25519.PublicKey { 88func (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.
108func NewEphemeralKey(peerID []byte, ltPrv *ed25519.PrivateKey) (*ed25519.PrivateKey, *EphemeralKeyMsg, error) { 108func 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
19package message
20
21import (
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
43type 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
51type 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//
68type PathElement struct {
69 pathElementData
70 Signature *util.PeerSignature // signature
71}
72
73// NewPathElement creates a new path element from data
74func 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
87type 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
93func (pew *PathElementWire) Size() uint16 {
94 return 96
95}
96
97// SignedData gets the data to be signed by peer ('Signable' interface)
98func (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.
113func (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
119func (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
132type 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
147func 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.
164func (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.
170func (m *DHTP2PGetMsg) Header() *Header {
171 return &Header{m.MsgSize, m.MsgType}
172}
173
174// Clone message
175func (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
199type 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
217func 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
237func (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
252func (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.
260func (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.
266func (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
276type 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
293func 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
309func (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.
324func (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.
330func (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
343type 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.
354func 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
369func (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.
393func (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.
412func (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.
425func (m *DHTP2PHelloMsg) Header() *Header {
426 return &Header{m.MsgSize, m.MsgType}
427}
428
429// Verify the message signature
430func (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
442func (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.
448func (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
113func (a *HelloAddress) Bytes() []byte { 115func (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
19package message
20
21import (
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
43type 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.
54func 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
69func (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.
93func (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.
112func (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.
125func (m *HelloDHTMsg) Header() *Header {
126 return &Header{m.MsgSize, m.MsgType}
127}
128
129// Verify the message signature
130func (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
142func (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.
154func (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
153type NamecacheCacheResponseMsg struct { 153type 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.
107func (m *TransportPingMsg) String() string { 108func (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
159type TransportPongMsg struct { 162type 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.
169func NewTransportPongMsg(challenge uint32, a *util.Address) *TransportPongMsg { 172func 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
232type SessionAckMsg struct { 235type 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
19package message 20package 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.
45type Connection struct { 46type 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 {
53func NewConnection(ctx context.Context, path string) (s *Connection, err error) { 55func 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)
141func (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
19package blocks
20
21import (
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
35type PeerFilter struct {
36 BF *BloomFilter
37}
38
39// NewPeerFilter creates an empty peer filter instance.
40func NewPeerFilter() *PeerFilter {
41 return &PeerFilter{
42 BF: NewBloomFilter(128),
43 }
44}
45
46// Add peer id to the filter
47func (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)
52func (pf *PeerFilter) Contains(p *util.PeerID) bool {
53 return pf.BF.Contains(p.Data)
54}
55
56// Cloone peer filter instance
57func (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
69const (
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
78const (
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.
96type 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.
120type PassResultFilter struct{}
121
122// Add a block to the result filter.
123func (rf *PassResultFilter) Add(Block) {
124}
125
126// Contains returns true if entry (binary representation) is filtered
127func (rf *PassResultFilter) Contains(Block) bool {
128 return false
129}
130
131// Bytes returns the binary representation of a result filter
132func (rf *PassResultFilter) Bytes() (buf []byte) {
133 return
134}
135
136// Merge two result filters
137func (rf *PassResultFilter) Merge(ResultFilter) bool {
138 return true
139}
140
141// Compare two result filters
142func (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.
157type 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).
166func 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!)
176func (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
204func (bf *BloomFilter) Mutator() []byte {
205 return bf.mInput
206}
207
208// Bytes returns the binary representation of a bloom filter
209func (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
219func (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
230func (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
241func (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.
253func (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.
263func (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.
276func (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
19package blocks
20
21import (
22 "bytes"
23 "crypto/rand"
24 "sort"
25 "testing"
26)
27
28type Entry []byte
29
30type EntryList []Entry
31
32func (list EntryList) Len() int { return len(list) }
33func (list EntryList) Swap(i, j int) { list[i], list[j] = list[j], list[i] }
34func (list EntryList) Less(i, j int) bool { return bytes.Compare(list[i], list[j]) < 0 }
35
36func (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
42func 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 @@
19package blocks 19package blocks
20 20
21import ( 21import (
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
110func (q *GenericQuery) Get(key string, value any) bool { 113func (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
120func (q *GenericQuery) Set(key string, value any) { 118func (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
141func (q *GenericQuery) String() string { 135func (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.
146func NewGenericQuery(buf []byte) *GenericQuery { 140func 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
184func (b *GenericBlock) Verify() error { 180func (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.
190func NewGenericBlock(buf []byte) *GenericBlock { 186func 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
19package blocks
20
21import (
22 "bytes"
23 "testing"
24)
25
26// Test parameter handling for queries
27func 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
31var ( 33var (
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().
164func (b *GNSBlock) Verify() (err error) { 176func (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
19package blocks
20
21import (
22 "gnunet/crypto"
23 "gnunet/enums"
24)
25
26// BlockHandler interface defines methods specific to block types.
27type 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.
71var BlockHandlers map[enums.BlockType]BlockHandler
72
73// initializer function
74func 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).
54type HelloBlock struct { 56type 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.
65func (h *HelloBlock) SetAddresses(a []*util.Address) { 67func (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. 201func (h *HelloBlock) Type() uint16 {
195func (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
206func (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
216func (h *HelloBlock) Expire() util.AbsoluteTime {
217 return h.Expires
218}
219
220// String returns the human-readable representation of a block
221func (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.
205func (h *HelloBlock) URL() string { 227func (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.
225func (h *HelloBlock) Equals(g *HelloBlock) bool { 247func (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
240func (h *HelloBlock) Verify() (bool, error) { 262func (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
252func (h *HelloBlock) Sign(prv *ed25519.PrivateKey) error { 274func (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.
264func (h *HelloBlock) signedData() []byte { 280func (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
312type HelloBlockHandler struct{}
313
314// Parse a block instance from binary data
315func (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.
321func (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.
328func (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.
340func (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.
349func (bh *HelloBlockHandler) SetupResultFilter(filterSize int, mutator uint32) ResultFilter {
350 return NewHelloResultFilter(filterSize, mutator)
351}
352
353// ParseResultFilter from binary data
354func (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.
370func (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
381type HelloResultFilter struct {
382 bf *BloomFilter
383}
384
385// NewHelloResultFilter initializes an empty resut filter
386func 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.
397func 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
413func (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
421func (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
430func (rf *HelloResultFilter) Bytes() []byte {
431 return rf.bf.Bytes()
432}
433
434// Compare two HELLO result filters
435func (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
444func (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
19package blocks
20
21// DHT Block types
22const (
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
19package dht
20
21import (
22 "bytes"
23 "crypto/sha512"
24 "encoding/binary"
25)
26
27//======================================================================
28// Generic BloomFilter
29//======================================================================
30
31// BloomFilter parameter
32var (
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.
40type 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.
47func 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.
58func (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.
68func (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.
81func (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.
104type PeerBloomFilter struct {
105 BloomFilter
106}
107
108// NewPeerBloomFilter creates a new filter for peer addresses.
109func NewPeerBloomFilter() *PeerBloomFilter {
110 return &PeerBloomFilter{
111 BloomFilter: *NewBloomFilter(nil),
112 }
113}
114
115// Add peer address to the filter.
116func (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.
121func (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
19package dht
20
21import (
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.
39func (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
370func (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
21import ( 21import (
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 (
42type Module struct { 48type 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.
54func NewModule(ctx context.Context, c *core.Core) (m *Module, err error) { 61func 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"]
80func (m *Module) Get(ctx context.Context, query blocks.Query) (block blocks.Block, err error) { 88func (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) 93func (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"]
99func (m *Module) Put(ctx context.Context, key blocks.Query, block blocks.Block) error { 101func (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
154func (m *Module) heartbeat(ctx context.Context) { 162func (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
171func (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.
183func (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
165func (m *Module) Export(fcn map[string]any) { 232func (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
172func (m *Module) Import(fcm map[string]any) { 240func (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
247func (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
19package dht
20
21import (
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
46type 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
69const (
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
79type 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.
92func 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
106func (t *GenericResultHandler) ID() int {
107 return t.id
108}
109
110// Key returns the key string
111func (t *GenericResultHandler) Key() string {
112 return t.key.String()
113}
114
115// Done returns true if the result handler is no longer active.
116func (t *GenericResultHandler) Done() bool {
117 return !t.active || t.started.Add(time.Hour).Expired()
118}
119
120// Compare two handlers
121func (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
132func (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
141type ForwardResultHandler struct {
142 GenericResultHandler
143
144 resp transport.Responder // responder for communicating back to originator
145}
146
147// NewForwardResultHandler derived from DHT-GET message
148func 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
161func (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
172func (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
187func (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:
215type ResultHandlerFcn func(context.Context, *message.DHTP2PResultMsg, chan<- any) bool
216
217// DirectResultHandler for local DHT-P2P-GET requests
218type 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
226func 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
240func (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
250func (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
261func (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
277type ResultHandlerList struct {
278 list *util.Map[string, []ResultHandler] // map of handlers
279}
280
281// NewResultHandlerList creates a new task list
282func NewResultHandlerList() *ResultHandlerList {
283 return &ResultHandlerList{
284 list: util.NewMap[string, []ResultHandler](),
285 }
286}
287
288// Add handler to list
289func (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
327func (t *ResultHandlerList) Get(key string) ([]ResultHandler, bool) {
328 return t.list.Get(key)
329}
330
331// Cleanup removes expired tasks from list
332func (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
21import ( 21import (
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
35var ( 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)
42const ( 37const (
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).
53type PeerAddress struct { 47type 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.
61func NewPeerAddress(peer *util.PeerID) *PeerAddress { 55func 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
65func 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.
72func (addr *PeerAddress) String() string { 75func (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.
77func (addr *PeerAddress) Equals(p *PeerAddress) bool { 80func (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).
83func (addr *PeerAddress) Distance(p *PeerAddress) (*math.Int, int) { 86func (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.
100type RoutingTable struct { 103type 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.
110func NewRoutingTable(ref *PeerAddress) *RoutingTable { 116func 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.
132func (rt *RoutingTable) Add(p *PeerAddress) bool { 140func (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.
154func (rt *RoutingTable) Remove(p *PeerAddress) bool { 166func (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
187func (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.
190func (rt *RoutingTable) SelectClosestPeer(p *PeerAddress, bf *PeerBloomFilter) (n *PeerAddress) { 226func (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)
210func (rt *RoutingTable) SelectRandomPeer(bf *PeerBloomFilter) *PeerAddress { 248func (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.
233func (rt *RoutingTable) SelectPeer(p *PeerAddress, hops int, bf *PeerBloomFilter) *PeerAddress { 271func (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
242func (rt *RoutingTable) IsClosestPeer(p, k *PeerAddress, bf *PeerBloomFilter) bool { 280// reference address is used.
243 n := rt.SelectClosestPeer(k, bf) 281func (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.
251func (rt *RoutingTable) ComputeOutDegree(repl, hop int) int { 304func (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 {
271func (rt *RoutingTable) heartbeat(ctx context.Context) { 324func (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
357func (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.
376func (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)
381func (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) {
295func (rt *RoutingTable) lock(readonly bool) { 388func (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) {
306func (rt *RoutingTable) unlock(readonly bool) { 399func (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
321type Bucket struct { 414type 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.
335func (b *Bucket) Add(p *PeerAddress) bool { 429func (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.
351func (b *Bucket) Remove(p *PeerAddress) bool { 446func (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.
368func (b *Bucket) SelectClosestPeer(p *PeerAddress, bf *PeerBloomFilter) (n *PeerAddress, dist *math.Int) { 463func (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 @@
19package dht 19package dht
20 20
21import ( 21import (
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
45var ( 48var (
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
150func 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 @@
19package dht 19package dht
20 20
21import ( 21import (
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
28type DHTCommand struct{} 30// RPCService is a type for DHT-related JSON-RPC requests
31type RPCService struct{}
32
33// local instance of service
34var dhtRPC = &RPCService{}
35
36//----------------------------------------------------------------------
37// Command "DHT.Status"
38//----------------------------------------------------------------------
29 39
30type DHTStats struct { 40// StatusRequest is a status request for specific information addressed
31 Started time.Time 41// by topic(s)
42type StatusRequest struct {
43 Topics []string `json:"topics"`
32} 44}
33 45
34func (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(), 48type StatusResponse struct {
49 Messages map[string]string `json:"messages"`
50}
51
52// Status requests information by topic(s).
53func (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
44func (m *Module) InitRPC(srv *rpc.Server) { 72func (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
51func NewService(ctx context.Context, c *core.Core) (service.Service, error) { 50func 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.
100func (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.
34type HdlrInst func(*message.ResourceRecord, []string) (BlockHandler, error) 34type 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.
197func (hl *BlockHandlerList) GetHandler(types ...enums.GNSType) BlockHandler { 197func (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.
207func (gns *Module) ResolveDNS( 207func (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
136func (m *Module) Import(fcn map[string]any) { 136func (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
19package gns 19package gns
20 20
21import "net/rpc" 21import "gnunet/service"
22 22
23//---------------------------------------------------------------------- 23//----------------------------------------------------------------------
24 24
25// InitRPC registers RPC commands for the module 25// InitRPC registers RPC commands for the module
26func (m *Module) InitRPC(srv *rpc.Server) { 26func (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
112func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back transport.Responder) bool { 111func (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
21import ( 21import (
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
79type Heartbeat func(context.Context) 76type Heartbeat func(context.Context)
80 77
78// CtxKey is a value-context key
79type 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.
82type ModuleImpl struct { 82type 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
91func NewModuleImpl() (m *ModuleImpl) { 88func 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 (
38type Module struct { 39type 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.
70func (m *Module) Get(ctx context.Context, query *blocks.GNSQuery) (block *blocks.GNSBlock, err error) { 71func (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
171func (m *Module) RPC() (string, func(http.ResponseWriter, *http.Request)) { 172func (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
72func (p *PoWData) SetPoW(pow uint64) error { 69func (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
82func (p *PoWData) GetPoW() uint64 { 75func (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
140func NewRevDataFromMsg(m *message.RevocationRevokeMsg) *RevData { 133func 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).
176func (rd *RevData) Verify(withSig bool) (zbits float64, rc int) { 166func (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
254func (rdc *RevDataCalc) Average() float64 { 243func (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() {
290func (rdc *RevDataCalc) Compute(ctx context.Context, bits int, last uint64, cb func(float64, uint64)) (float64, uint64) { 279func (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.
14func TestRevocationRFC(t *testing.T) { 14func 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
19package revocation 19package revocation
20 20
21import "net/rpc" 21import "gnunet/service"
22 22
23//---------------------------------------------------------------------- 23//----------------------------------------------------------------------
24 24
25// InitRPC registers RPC commands for the module 25// InitRPC registers RPC commands for the module
26func (m *Module) InitRPC(srv *rpc.Server) { 26func (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
89func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back transport.Responder) bool { 90func (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
21import ( 21import (
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)
36type 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.
37func StartRPC(ctx context.Context, endpoint string) (srvRPC *rpc.Server, err error) { 46func 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
19package service
20
21import (
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
44var (
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.
59type 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
75type DHTStore Store[blocks.Query, blocks.Block]
76
77// KVStore for key/value string pairs
78type KVStore Store[string, string]
79
80//------------------------------------------------------------
81// NewDHTStore creates a new storage handler with given spec
82// for use with DHT queries and blocks
83func 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.
102func 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.
131type 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.
143type 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.
155func 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
198func (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
214func (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
277func (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).
310func (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.
315func (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.
322func (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.
356func (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
372type RedisStore struct {
373 client *redis.Client // client connection
374 db int // index to database
375}
376
377// NewRedisStore creates a Redis service client instance.
378func 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
409func (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
414func (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
419func (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
444type SQLStore struct {
445 db *util.DbConn
446}
447
448// NewSQLStore creates a new SQL-based key/value store.
449func 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
473func (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
479func (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
486func (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
19package util 19package store
20 20
21import ( 21import (
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
33var ( 34var (
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.
44type DbConn struct { 45type 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.
51func (db *DbConn) Close() (err error) { 52func (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
60func (db *DbConn) QueryRow(query string, args ...any) *sql.Row { 61func (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
65func (db *DbConn) Query(query string, args ...any) (*sql.Rows, error) { 66func (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
70func (db *DbConn) Exec(query string, args ...any) (sql.Result, error) { 71func (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)
82var ( 83var (
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.
87type DbPoolEntry struct { 88type 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
94func init() { 95func 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
102type dbPool struct { 103type 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&...]").
139func (p *dbPool) Connect(spec string) (db *DbConn, err error) { 140func (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
19package dht 19package store
20 20
21import ( 21import (
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
32const ( 32const (
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.
39func TestDHTFilesStore(t *testing.T) { 39func 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
19package store
20
21import (
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
34var (
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.
51type 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
74type DHTStore Store[blocks.Query, blocks.Block]
75
76// KVStore for key/value string pairs
77type KVStore Store[string, string]
78
79//------------------------------------------------------------
80// NewDHTStore creates a new storage handler with given spec
81// for use with DHT queries and blocks
82func 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.
101func 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
128type RedisStore struct {
129 client *redis.Client // client connection
130 db int // index to database
131}
132
133// NewRedisStore creates a Redis service client instance.
134func 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
165func (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
170func (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
175func (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
180func (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
201func (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
210type SQLStore struct {
211 db *DBConn
212}
213
214// NewSQLStore creates a new SQL-based key/value store.
215func 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
239func (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
245func (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
252func (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
257func (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
276func (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
19package store
20
21import (
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.
39type 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.
56func 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.
95func (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
104func (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
159func (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
181func (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).
226func (s *FileStore) List() ([]blocks.Query, error) {
227 return nil, ErrStoreNoList
228}
229
230// read block from storage for given key
231func (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.
249func (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.
256func (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.
273func (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
19package store
20
21import (
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.
34type 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
50var initScript []byte
51
52// FileMetaDB is a SQLite3 database for block metadata
53type 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".
59func 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
87func (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
94func (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
113func (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.
119func (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"
126func (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
149func (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
172func (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
19create 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
19package main
20
21import (
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
46func 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
109type TestNode struct {
110 id int
111 peer *core.Peer
112 core *core.Core
113 addr *util.Address
114}
115
116func (n *TestNode) Shutdown() {
117 n.core.Shutdown()
118}
119func (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
127func (n *TestNode) SendHello(ctx context.Context, addr *util.Address) error {
128 return n.core.SendHello(ctx, addr)
129}
130
131func 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.
49type Endpoint interface { 49type 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
86type PaketEndpoint struct { 86type 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.
96func (ep *PaketEndpoint) Run(ctx context.Context, hdlr chan *TransportMessage) (err error) { 97func (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
147func (ep *PaketEndpoint) read() (tm *TransportMessage, err error) { 148func (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
179func (ep *PaketEndpoint) Send(ctx context.Context, addr net.Addr, msg *TransportMessage) (err error) { 180func (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.
287func (ep *StreamEndpoint) Run(ctx context.Context, hdlr chan *TransportMessage) (err error) { 288func (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
334func (ep *StreamEndpoint) read(ctx context.Context, conn net.Conn) (tm *TransportMessage, err error) { 335func (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
362func (ep *StreamEndpoint) Send(ctx context.Context, addr net.Addr, msg *TransportMessage) error { 363func (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
86func ReadMessage(ctx context.Context, rdr io.ReadCloser, buf []byte) (msg message.Message, err error) { 82func 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 (
32type Responder interface { 32type 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
58func (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
33var ( 33var (
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.
45type TransportMessage struct { 46type 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
65func (msg *TransportMessage) Bytes() ([]byte, error) { 66func (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
77func (msg *TransportMessage) String() string { 78func (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
82func NewTransportMessage(peer *util.PeerID, msg message.Message) (tm *TransportMessage) { 83func 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.
99type Transport struct { 100type 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.
106func NewTransport(ctx context.Context, tag string, ch chan *TransportMessage) (t *Transport) { 107func 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
127func (t *Transport) Send(ctx context.Context, addr net.Addr, msg *TransportMessage) (err error) { 128func (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.
187func (t *Transport) ForwardOpen(protocol, param string, port int) (id, local, remote string, err error) { 188func (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
193func (t *Transport) ForwardClose(id string) error { 199func (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.
29type Address struct { 29type 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.
46func NewAddressWrap(addr net.Addr) *Address { 48func 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".
58func ParseAddress(s string) (addr *Address, err error) { 60func 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.
76func (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.
95func (a *Address) URI() string { 92func (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)
105type 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.
111func 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.
123type PeerAddrList struct { 102type 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
136func (a *PeerAddrList) Add(id string, addr *Address) (mode int) { 115func (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
163func (a *PeerAddrList) Get(id string, transport string) (res []*Address) { 143func (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.
184func (a *PeerAddrList) Delete(id string) { 165func (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.
171func (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.
35func Clone[T []E, E any](d T) T { 35func 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
47var ( 47var (
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
19package util
20
21import (
22 "math/rand"
23 "sync"
24)
25
26//----------------------------------------------------------------------
27// Thread-safe map implementation
28//----------------------------------------------------------------------
29
30// Map keys to values
31type 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.
39func 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.
50func (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.
64func (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.
84func (m *Map[K, V]) Size() int {
85 return len(m.list)
86}
87
88// Put value into map under given key.
89func (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.
96func (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.
104func (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.
123func (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)
132func (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)
143func (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
21import ( 21import (
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
59type Map[K comparable, V any] struct { 58type ParameterSet map[string]any
60 list map[K]V
61 mtx sync.RWMutex
62 inProcess bool
63}
64
65// NewMap allocates a new mapping.
66func 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 61func 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 {
77func (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.
91func (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.
111func (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.
118func (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.
126func (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)
135func (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)
146func (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
19package util
20
21import (
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
30type PeerPublicKey struct {
31 Data []byte `size:"32"` // Ed25519 public key data
32}
33
34// NewPeerPublicKey creates a key instance from binary data
35func 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
54type PeerID PeerPublicKey
55
56// NewPeerID creates a new peer id from data.
57func NewPeerID(data []byte) (p *PeerID) {
58 return (*PeerID)(NewPeerPublicKey(data))
59}
60
61// Equals returns true if two peer IDs match.
62func (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.
67func (p *PeerID) String() string {
68 return EncodeBinaryToString(p.Data)
69}
70
71// Bytes returns the binary representation of a peer identifier.
72func (p *PeerID) Bytes() []byte {
73 return Clone(p.Data)
74}
75
76//----------------------------------------------------------------------
77
78// PeerSignature is a EdDSA signature from the peer
79type PeerSignature struct {
80 Data []byte `size:"64"`
81}
82
83// NewPeerSignature is a EdDSA signatre with the private peer key
84func 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
19package util
20
21import (
22 "bytes"
23
24 "github.com/bfix/gospel/crypto/ed25519"
25)
26
27// PeerID is the 32-byte binary representation od a Ed25519 key
28type PeerID struct {
29 Key []byte `size:"32"`
30}
31
32// NewPeerID creates a new peer id from data.
33func 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.
48func (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.
53func (p *PeerID) String() string {
54 return EncodeBinaryToString(p.Key)
55}
56
57func (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
28func RndArray(b []byte) { 30func 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.
33func NewRndArray(size int) []byte { 37func 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
47func NewAbsoluteTimeEpoch(secs uint64) AbsoluteTime { 47func 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...
142type RelativeTime struct { 143type 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.
147func NewRelativeTime(d time.Duration) RelativeTime { 148func 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
162func (t RelativeTime) Add(t2 RelativeTime) { 163func (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