diff options
author | Bernd Fix <brf@hoi-polloi.org> | 2022-06-01 20:59:52 +0200 |
---|---|---|
committer | Bernd Fix <brf@hoi-polloi.org> | 2022-06-01 20:59:52 +0200 |
commit | 913c80f317270b41f339048afa5d63278eecf8f4 (patch) | |
tree | 7ef1db049af2f4b009d97ab7240bb4744be9dfb6 /src | |
parent | 205cad60026bf0af1cd2712a8faa4bce08eafb1d (diff) | |
download | gnunet-go-913c80f317270b41f339048afa5d63278eecf8f4.tar.gz gnunet-go-913c80f317270b41f339048afa5d63278eecf8f4.zip |
Reworked gnunet/transport and gnunet/service.
Diffstat (limited to 'src')
74 files changed, 5686 insertions, 2395 deletions
diff --git a/src/cmd/peer_mockup/main.go b/src/cmd/peer_mockup/main.go deleted file mode 100644 index 59bc002..0000000 --- a/src/cmd/peer_mockup/main.go +++ /dev/null | |||
@@ -1,68 +0,0 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "encoding/hex" | ||
5 | "flag" | ||
6 | "fmt" | ||
7 | |||
8 | "github.com/bfix/gospel/logger" | ||
9 | "gnunet/core" | ||
10 | "gnunet/transport" | ||
11 | ) | ||
12 | |||
13 | var ( | ||
14 | p *core.Peer // local peer (with private key) | ||
15 | t *core.Peer // remote peer | ||
16 | ) | ||
17 | |||
18 | func main() { | ||
19 | // handle command line arguments | ||
20 | var ( | ||
21 | asServer bool | ||
22 | err error | ||
23 | ch transport.Channel | ||
24 | ) | ||
25 | flag.BoolVar(&asServer, "s", false, "accept incoming connections") | ||
26 | flag.Parse() | ||
27 | |||
28 | // setup peer instances from static data | ||
29 | if err = setupPeers(false); err != nil { | ||
30 | fmt.Println(err.Error()) | ||
31 | return | ||
32 | } | ||
33 | |||
34 | fmt.Println("======================================================================") | ||
35 | fmt.Println("GNUnet peer mock-up (EXPERIMENTAL) (c) 2018,2019 by Bernd Fix, >Y<") | ||
36 | fmt.Printf(" Identity '%s'\n", p.GetIDString()) | ||
37 | fmt.Printf(" [%s]\n", hex.EncodeToString(p.GetID())) | ||
38 | fmt.Println("======================================================================") | ||
39 | |||
40 | if asServer { | ||
41 | // run as server | ||
42 | fmt.Println("Waiting for connections...") | ||
43 | hdlr := make(chan transport.Channel) | ||
44 | go func() { | ||
45 | for { | ||
46 | select { | ||
47 | case ch = <-hdlr: | ||
48 | mc := transport.NewMsgChannel(ch) | ||
49 | if err = process(mc, t, p); err != nil { | ||
50 | logger.Println(logger.ERROR, err.Error()) | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | }() | ||
55 | _, err = transport.NewChannelServer("tcp+0.0.0.0:2086", hdlr) | ||
56 | } else { | ||
57 | // connect to peer | ||
58 | fmt.Println("Connecting to target peer") | ||
59 | if ch, err = transport.NewChannel("tcp+172.17.0.5:2086"); err != nil { | ||
60 | logger.Println(logger.ERROR, err.Error()) | ||
61 | } | ||
62 | mc := transport.NewMsgChannel(ch) | ||
63 | err = process(mc, p, t) | ||
64 | } | ||
65 | if err != nil { | ||
66 | fmt.Println(err) | ||
67 | } | ||
68 | } | ||
diff --git a/src/cmd/peer_mockup/peers.go b/src/cmd/peer_mockup/peers.go deleted file mode 100644 index 16d3c87..0000000 --- a/src/cmd/peer_mockup/peers.go +++ /dev/null | |||
@@ -1,46 +0,0 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "github.com/bfix/gospel/data" | ||
5 | "gnunet/core" | ||
6 | "gnunet/util" | ||
7 | ) | ||
8 | |||
9 | func setupPeers(rnd bool) (err error) { | ||
10 | |||
11 | //------------------------------------------------------------------ | ||
12 | // create local peer | ||
13 | //------------------------------------------------------------------ | ||
14 | secret := []byte{ | ||
15 | 0x78, 0xde, 0xcf, 0xc0, 0x26, 0x9e, 0x62, 0x3d, | ||
16 | 0x17, 0x24, 0xe6, 0x1b, 0x98, 0x25, 0xec, 0x2f, | ||
17 | 0x40, 0x6b, 0x1e, 0x39, 0xa5, 0x19, 0xac, 0x9b, | ||
18 | 0xb2, 0xdd, 0xf4, 0x6c, 0x12, 0x83, 0xdb, 0x86, | ||
19 | } | ||
20 | if rnd { | ||
21 | util.RndArray(secret) | ||
22 | } | ||
23 | p, err = core.NewPeer(secret, true) | ||
24 | if err != nil { | ||
25 | return | ||
26 | } | ||
27 | addr, _ := data.Marshal(util.NewIPAddress([]byte{172, 17, 0, 6}, 2086)) | ||
28 | p.AddAddress(util.NewAddress("tcp", addr)) | ||
29 | |||
30 | //------------------------------------------------------------------ | ||
31 | // create remote peer | ||
32 | //------------------------------------------------------------------ | ||
33 | id := []byte{ | ||
34 | 0x92, 0xdc, 0xbf, 0x39, 0x40, 0x2d, 0xc6, 0x3c, | ||
35 | 0x97, 0xa6, 0x81, 0xe0, 0xfc, 0xd8, 0x7c, 0x74, | ||
36 | 0x17, 0xd3, 0xa3, 0x8c, 0x52, 0xfd, 0xe0, 0x49, | ||
37 | 0xbc, 0xd0, 0x1c, 0x0a, 0x0b, 0x8c, 0x02, 0x51, | ||
38 | } | ||
39 | t, err = core.NewPeer(id, false) | ||
40 | if err != nil { | ||
41 | return | ||
42 | } | ||
43 | addr, _ = data.Marshal(util.NewIPAddress([]byte{172, 17, 0, 5}, 2086)) | ||
44 | t.AddAddress(util.NewAddress("tcp", addr)) | ||
45 | return | ||
46 | } | ||
diff --git a/src/cmd/peer_mockup/process.go b/src/cmd/peer_mockup/process.go deleted file mode 100644 index 510fc48..0000000 --- a/src/cmd/peer_mockup/process.go +++ /dev/null | |||
@@ -1,128 +0,0 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | |||
7 | "gnunet/core" | ||
8 | "gnunet/crypto" | ||
9 | "gnunet/message" | ||
10 | "gnunet/transport" | ||
11 | "gnunet/util" | ||
12 | |||
13 | "github.com/bfix/gospel/concurrent" | ||
14 | ) | ||
15 | |||
16 | var ( | ||
17 | sig = concurrent.NewSignaller() | ||
18 | ) | ||
19 | |||
20 | func process(ch *transport.MsgChannel, from, to *core.Peer) (err error) { | ||
21 | // create a new connection instance | ||
22 | c := transport.NewConnection(ch, from, to) | ||
23 | defer c.Close() | ||
24 | |||
25 | // read and push next message | ||
26 | in := make(chan message.Message) | ||
27 | go func() { | ||
28 | for { | ||
29 | msg, err := c.Receive(sig) | ||
30 | if err != nil { | ||
31 | fmt.Printf("Receive: %s\n", err.Error()) | ||
32 | return | ||
33 | } | ||
34 | in <- msg | ||
35 | } | ||
36 | }() | ||
37 | |||
38 | // are we initiating the connection? | ||
39 | init := (from == p) | ||
40 | if init { | ||
41 | peerid := util.NewPeerID(p.GetID()) | ||
42 | c.Send(message.NewTransportTcpWelcomeMsg(peerid), sig) | ||
43 | } | ||
44 | |||
45 | // remember peer addresses (only ONE!) | ||
46 | pAddr := p.GetAddressList()[0] | ||
47 | tAddr := t.GetAddressList()[0] | ||
48 | |||
49 | send := make(map[uint16]bool) | ||
50 | //received := make(map[uint16]bool) | ||
51 | pending := make(map[uint16]message.Message) | ||
52 | |||
53 | // process loop | ||
54 | for { | ||
55 | select { | ||
56 | case m := <-in: | ||
57 | switch msg := m.(type) { | ||
58 | |||
59 | case *message.TransportTcpWelcomeMsg: | ||
60 | peerid := util.NewPeerID(p.GetID()) | ||
61 | if init { | ||
62 | c.Send(message.NewHelloMsg(peerid), sig) | ||
63 | target := util.NewPeerID(t.GetID()) | ||
64 | c.Send(message.NewTransportPingMsg(target, tAddr), sig) | ||
65 | } else { | ||
66 | c.Send(message.NewTransportTcpWelcomeMsg(peerid), sig) | ||
67 | } | ||
68 | |||
69 | case *message.HelloMsg: | ||
70 | |||
71 | case *message.TransportPingMsg: | ||
72 | mOut := message.NewTransportPongMsg(msg.Challenge, pAddr) | ||
73 | if err := mOut.Sign(p.PrvKey()); err != nil { | ||
74 | return err | ||
75 | } | ||
76 | c.Send(mOut, sig) | ||
77 | |||
78 | case *message.TransportPongMsg: | ||
79 | rc, err := msg.Verify(t.PubKey()) | ||
80 | if err != nil { | ||
81 | return err | ||
82 | } | ||
83 | if !rc { | ||
84 | return errors.New("PONG verification failed") | ||
85 | } | ||
86 | send[message.TRANSPORT_PONG] = true | ||
87 | if mOut, ok := pending[message.TRANSPORT_SESSION_SYN]; ok { | ||
88 | c.Send(mOut, sig) | ||
89 | } | ||
90 | |||
91 | case *message.SessionSynMsg: | ||
92 | mOut := message.NewSessionSynAckMsg() | ||
93 | mOut.Timestamp = msg.Timestamp | ||
94 | if send[message.TRANSPORT_PONG] { | ||
95 | c.Send(mOut, sig) | ||
96 | } else { | ||
97 | pending[message.TRANSPORT_SESSION_SYN] = mOut | ||
98 | } | ||
99 | |||
100 | case *message.SessionQuotaMsg: | ||
101 | c.SetBandwidth(msg.Quota) | ||
102 | |||
103 | case *message.SessionAckMsg: | ||
104 | |||
105 | case *message.SessionKeepAliveMsg: | ||
106 | c.Send(message.NewSessionKeepAliveRespMsg(msg.Nonce), sig) | ||
107 | |||
108 | case *message.EphemeralKeyMsg: | ||
109 | rc, err := msg.Verify(t.PubKey()) | ||
110 | if err != nil { | ||
111 | return err | ||
112 | } | ||
113 | if !rc { | ||
114 | return errors.New("EPHKEY verification failed") | ||
115 | } | ||
116 | t.SetEphKeyMsg(msg) | ||
117 | c.Send(p.EphKeyMsg(), sig) | ||
118 | secret := crypto.SharedSecret(p.EphPrvKey(), t.EphKeyMsg().Public()) | ||
119 | c.SharedSecret(util.Clone(secret.Bits[:])) | ||
120 | |||
121 | default: | ||
122 | fmt.Printf("!!! %v\n", msg) | ||
123 | } | ||
124 | default: | ||
125 | } | ||
126 | } | ||
127 | return nil | ||
128 | } | ||
diff --git a/src/cmd/revoke-zonekey/main.go b/src/cmd/revoke-zonekey/main.go deleted file mode 100644 index 7deeda6..0000000 --- a/src/cmd/revoke-zonekey/main.go +++ /dev/null | |||
@@ -1,202 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package main | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "encoding/hex" | ||
24 | "flag" | ||
25 | "log" | ||
26 | "os" | ||
27 | "os/signal" | ||
28 | "sync" | ||
29 | "syscall" | ||
30 | |||
31 | "gnunet/service/revocation" | ||
32 | "gnunet/util" | ||
33 | |||
34 | "github.com/bfix/gospel/data" | ||
35 | ) | ||
36 | |||
37 | func main() { | ||
38 | log.Println("*** Compute revocation data for a zone key") | ||
39 | log.Println("*** Copyright (c) 2020, Bernd Fix >Y<") | ||
40 | log.Println("*** This is free software distributed under the Affero GPL v3.") | ||
41 | |||
42 | // handle command line arguments | ||
43 | var ( | ||
44 | verbose bool // be verbose with messages | ||
45 | bits int // number of leading zero-bit requested | ||
46 | zonekey string // zonekey to be revoked | ||
47 | filename string // name of file for persistance | ||
48 | ) | ||
49 | flag.IntVar(&bits, "b", 25, "Number of leading zero bits") | ||
50 | flag.BoolVar(&verbose, "v", false, "verbose output") | ||
51 | flag.StringVar(&zonekey, "z", "", "Zone key to be revoked") | ||
52 | flag.StringVar(&filename, "f", "", "Name of file to store revocation") | ||
53 | flag.Parse() | ||
54 | |||
55 | if len(filename) == 0 { | ||
56 | log.Fatal("Missing '-f' argument (filename fot revocation data)") | ||
57 | } | ||
58 | |||
59 | // define layout of persistant data | ||
60 | var revData struct { | ||
61 | Rd *revocation.RevDataCalc // Revocation data | ||
62 | T util.RelativeTime // time spend in calculations | ||
63 | Last uint64 // last value used for PoW test | ||
64 | Numbits uint8 // number of leading zero-bits | ||
65 | } | ||
66 | dataBuf := make([]byte, 450) | ||
67 | |||
68 | // read revocation object from file | ||
69 | file, err := os.Open(filename) | ||
70 | cont := true | ||
71 | if err != nil { | ||
72 | if len(zonekey) != 52 { | ||
73 | log.Fatal("Missing or invalid zonekey and no file specified -- aborting") | ||
74 | } | ||
75 | keyData, err := util.DecodeStringToBinary(zonekey, 32) | ||
76 | if err != nil { | ||
77 | log.Fatal("Invalid zonekey: " + err.Error()) | ||
78 | } | ||
79 | revData.Rd = revocation.NewRevDataCalc(keyData) | ||
80 | revData.Numbits = uint8(bits) | ||
81 | revData.T = util.NewRelativeTime(0) | ||
82 | cont = false | ||
83 | } else { | ||
84 | n, err := file.Read(dataBuf) | ||
85 | if err != nil { | ||
86 | log.Fatal("Error reading file: " + err.Error()) | ||
87 | } | ||
88 | if n != len(dataBuf) { | ||
89 | log.Fatal("File corrupted -- aborting") | ||
90 | } | ||
91 | if err = data.Unmarshal(&revData, dataBuf); err != nil { | ||
92 | log.Fatal("File corrupted: " + err.Error()) | ||
93 | } | ||
94 | bits = int(revData.Numbits) | ||
95 | if err = file.Close(); err != nil { | ||
96 | log.Fatal("Error closing file: " + err.Error()) | ||
97 | } | ||
98 | } | ||
99 | |||
100 | if cont { | ||
101 | log.Printf("Revocation calculation started at %s\n", revData.Rd.Timestamp.String()) | ||
102 | log.Printf("Time spent on calculation: %s\n", revData.T.String()) | ||
103 | log.Printf("Last tested PoW value: %d\n", revData.Last) | ||
104 | log.Println("Continuing...") | ||
105 | } else { | ||
106 | log.Println("Starting new revocation calculation...") | ||
107 | } | ||
108 | log.Println("Press ^C to abort...") | ||
109 | |||
110 | // pre-set difficulty | ||
111 | log.Printf("Difficulty: %d\n", bits) | ||
112 | if bits < 25 { | ||
113 | log.Println("WARNING: difficulty is less than 25!") | ||
114 | } | ||
115 | |||
116 | // Start or continue calculation | ||
117 | ctx, cancelFcn := context.WithCancel(context.Background()) | ||
118 | wg := new(sync.WaitGroup) | ||
119 | wg.Add(1) | ||
120 | go func() { | ||
121 | defer wg.Done() | ||
122 | cb := func(average float64, last uint64) { | ||
123 | log.Printf("Improved PoW: %f average zero bits, %d steps\n", average, last) | ||
124 | } | ||
125 | |||
126 | startTime := util.AbsoluteTimeNow() | ||
127 | average, last := revData.Rd.Compute(ctx, bits, revData.Last, cb) | ||
128 | if average < float64(bits) { | ||
129 | log.Printf("Incomplete revocation: Only %f zero bits on average!\n", average) | ||
130 | } else { | ||
131 | log.Println("Revocation data object:") | ||
132 | log.Println(" 0x" + hex.EncodeToString(revData.Rd.Blob())) | ||
133 | log.Println("Status:") | ||
134 | rc := revData.Rd.Verify(false) | ||
135 | switch { | ||
136 | case rc == -1: | ||
137 | log.Println(" Missing/invalid signature") | ||
138 | case rc == -2: | ||
139 | log.Println(" Expired revocation") | ||
140 | case rc == -3: | ||
141 | log.Println(" Wrong PoW sequence order") | ||
142 | case rc < 25: | ||
143 | log.Println(" Difficulty to small") | ||
144 | default: | ||
145 | log.Printf(" Difficulty: %d\n", rc) | ||
146 | } | ||
147 | } | ||
148 | if !cont || last != revData.Last { | ||
149 | revData.Last = last | ||
150 | revData.T = util.AbsoluteTimeNow().Diff(startTime) | ||
151 | |||
152 | log.Println("Writing revocation data to file...") | ||
153 | file, err := os.Create(filename) | ||
154 | if err != nil { | ||
155 | log.Fatal("Can't write to output file: " + err.Error()) | ||
156 | } | ||
157 | buf, err := data.Marshal(&revData) | ||
158 | if err != nil { | ||
159 | log.Fatal("Internal error: " + err.Error()) | ||
160 | } | ||
161 | if len(buf) != len(dataBuf) { | ||
162 | log.Fatalf("Internal error: Buffer mismatch %d != %d", len(buf), len(dataBuf)) | ||
163 | } | ||
164 | n, err := file.Write(buf) | ||
165 | if err != nil { | ||
166 | log.Fatal("Can't write to output file: " + err.Error()) | ||
167 | } | ||
168 | if n != len(dataBuf) { | ||
169 | log.Fatal("Can't write data to output file!") | ||
170 | } | ||
171 | if err = file.Close(); err != nil { | ||
172 | log.Fatal("Error closing file: " + err.Error()) | ||
173 | } | ||
174 | } | ||
175 | }() | ||
176 | |||
177 | go func() { | ||
178 | // handle OS signals | ||
179 | sigCh := make(chan os.Signal, 5) | ||
180 | signal.Notify(sigCh) | ||
181 | loop: | ||
182 | for { | ||
183 | select { | ||
184 | // handle OS signals | ||
185 | case sig := <-sigCh: | ||
186 | switch sig { | ||
187 | case syscall.SIGKILL, syscall.SIGINT, syscall.SIGTERM: | ||
188 | log.Printf("Terminating (on signal '%s')\n", sig) | ||
189 | cancelFcn() | ||
190 | break loop | ||
191 | case syscall.SIGHUP: | ||
192 | log.Println("SIGHUP") | ||
193 | case syscall.SIGURG: | ||
194 | // TODO: https://github.com/golang/go/issues/37942 | ||
195 | default: | ||
196 | log.Println("Unhandled signal: " + sig.String()) | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | }() | ||
201 | wg.Wait() | ||
202 | } | ||
diff --git a/src/gnunet/build.sh b/src/gnunet/build.sh new file mode 100755 index 0000000..5ec677f --- /dev/null +++ b/src/gnunet/build.sh | |||
@@ -0,0 +1,3 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | go install -v -gcflags "-N -l" ./... | ||
diff --git a/src/cmd/.gitignore b/src/gnunet/cmd/.gitignore index 1933786..1933786 100644 --- a/src/cmd/.gitignore +++ b/src/gnunet/cmd/.gitignore | |||
diff --git a/src/gnunet/cmd/gnunet-service-dht-test-go/main.go b/src/gnunet/cmd/gnunet-service-dht-test-go/main.go new file mode 100644 index 0000000..cd2bd1a --- /dev/null +++ b/src/gnunet/cmd/gnunet-service-dht-test-go/main.go | |||
@@ -0,0 +1,152 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package main | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "flag" | ||
24 | "os" | ||
25 | "os/signal" | ||
26 | "strings" | ||
27 | "syscall" | ||
28 | "time" | ||
29 | |||
30 | "gnunet/config" | ||
31 | "gnunet/core" | ||
32 | "gnunet/rpc" | ||
33 | "gnunet/service" | ||
34 | "gnunet/service/dht" | ||
35 | |||
36 | "github.com/bfix/gospel/logger" | ||
37 | ) | ||
38 | |||
39 | func main() { | ||
40 | defer func() { | ||
41 | logger.Println(logger.INFO, "[dht] Bye.") | ||
42 | // flush last messages | ||
43 | logger.Flush() | ||
44 | }() | ||
45 | logger.Println(logger.INFO, "[dht] Starting service...") | ||
46 | |||
47 | var ( | ||
48 | cfgFile string | ||
49 | socket string | ||
50 | param string | ||
51 | err error | ||
52 | logLevel int | ||
53 | rpcEndp string | ||
54 | ) | ||
55 | // handle command line arguments | ||
56 | flag.StringVar(&cfgFile, "c", "gnunet-config.json", "GNUnet configuration file") | ||
57 | flag.StringVar(&socket, "s", "", "GNS service socket") | ||
58 | flag.StringVar(¶m, "p", "", "socket parameters (<key>=<value>,...)") | ||
59 | flag.IntVar(&logLevel, "L", logger.INFO, "DHT log level (default: INFO)") | ||
60 | flag.StringVar(&rpcEndp, "R", "", "JSON-RPC endpoint (default: none)") | ||
61 | flag.Parse() | ||
62 | |||
63 | // read configuration file and set missing arguments. | ||
64 | if err = config.ParseConfig(cfgFile); err != nil { | ||
65 | logger.Printf(logger.ERROR, "[dht] Invalid configuration file: %s\n", err.Error()) | ||
66 | return | ||
67 | } | ||
68 | |||
69 | // apply configuration | ||
70 | logger.SetLogLevel(logLevel) | ||
71 | if len(socket) == 0 { | ||
72 | socket = config.Cfg.GNS.Service.Socket | ||
73 | } | ||
74 | params := make(map[string]string) | ||
75 | if len(param) == 0 { | ||
76 | for _, p := range strings.Split(param, ",") { | ||
77 | kv := strings.SplitN(p, "=", 2) | ||
78 | params[kv[0]] = kv[1] | ||
79 | } | ||
80 | } else { | ||
81 | params = config.Cfg.GNS.Service.Params | ||
82 | } | ||
83 | |||
84 | // instantiate core service | ||
85 | ctx, cancel := context.WithCancel(context.Background()) | ||
86 | var local *core.Peer | ||
87 | if local, err = core.NewLocalPeer(config.Cfg.Local); err != nil { | ||
88 | logger.Printf(logger.ERROR, "[dht] No local peer: %s\n", err.Error()) | ||
89 | return | ||
90 | } | ||
91 | var c *core.Core | ||
92 | if c, err = core.NewCore(ctx, local); err != nil { | ||
93 | logger.Printf(logger.ERROR, "[dht] core failed: %s\n", err.Error()) | ||
94 | return | ||
95 | } | ||
96 | |||
97 | // start a new DHT service | ||
98 | dht := dht.NewService(ctx, c) | ||
99 | srv := service.NewSocketHandler("dht", dht) | ||
100 | if err = srv.Start(ctx, socket, params); err != nil { | ||
101 | logger.Printf(logger.ERROR, "[dht] Failed to start DHT service: '%s'", err.Error()) | ||
102 | return | ||
103 | } | ||
104 | |||
105 | // start JSON-RPC server on request | ||
106 | if len(rpcEndp) > 0 { | ||
107 | parts := strings.Split(rpcEndp, ":") | ||
108 | if parts[0] != "tcp" { | ||
109 | logger.Println(logger.ERROR, "[dht] RPC must have a TCP/IP endpoint") | ||
110 | return | ||
111 | } | ||
112 | config.Cfg.RPC.Endpoint = parts[1] | ||
113 | if err = rpc.Start(ctx); err != nil { | ||
114 | logger.Printf(logger.ERROR, "[dht] RPC failed to start: %s", err.Error()) | ||
115 | return | ||
116 | } | ||
117 | rpc.Register(dht) | ||
118 | } | ||
119 | |||
120 | // handle OS signals | ||
121 | sigCh := make(chan os.Signal, 5) | ||
122 | signal.Notify(sigCh) | ||
123 | |||
124 | // heart beat | ||
125 | tick := time.NewTicker(5 * time.Minute) | ||
126 | |||
127 | loop: | ||
128 | for { | ||
129 | select { | ||
130 | // handle OS signals | ||
131 | case sig := <-sigCh: | ||
132 | switch sig { | ||
133 | case syscall.SIGKILL, syscall.SIGINT, syscall.SIGTERM: | ||
134 | logger.Printf(logger.INFO, "[dht] Terminating service (on signal '%s')\n", sig) | ||
135 | break loop | ||
136 | case syscall.SIGHUP: | ||
137 | logger.Println(logger.INFO, "[dht] SIGHUP") | ||
138 | case syscall.SIGURG: | ||
139 | // TODO: https://github.com/golang/go/issues/37942 | ||
140 | default: | ||
141 | logger.Println(logger.INFO, "[dht] Unhandled signal: "+sig.String()) | ||
142 | } | ||
143 | // handle heart beat | ||
144 | case now := <-tick.C: | ||
145 | logger.Println(logger.INFO, "[dht] Heart beat at "+now.String()) | ||
146 | } | ||
147 | } | ||
148 | |||
149 | // terminating service | ||
150 | cancel() | ||
151 | srv.Stop() | ||
152 | } | ||
diff --git a/src/cmd/gnunet-service-gns-go/main.go b/src/gnunet/cmd/gnunet-service-gns-go/main.go index 57eec7e..6eb027b 100644 --- a/src/cmd/gnunet-service-gns-go/main.go +++ b/src/gnunet/cmd/gnunet-service-gns-go/main.go | |||
@@ -45,14 +45,16 @@ func main() { | |||
45 | 45 | ||
46 | var ( | 46 | var ( |
47 | cfgFile string | 47 | cfgFile string |
48 | srvEndp string | 48 | socket string |
49 | param string | ||
49 | err error | 50 | err error |
50 | logLevel int | 51 | logLevel int |
51 | rpcEndp string | 52 | rpcEndp string |
52 | ) | 53 | ) |
53 | // handle command line arguments | 54 | // handle command line arguments |
54 | flag.StringVar(&cfgFile, "c", "gnunet-config.json", "GNUnet configuration file") | 55 | flag.StringVar(&cfgFile, "c", "gnunet-config.json", "GNUnet configuration file") |
55 | flag.StringVar(&srvEndp, "s", "", "GNS service end-point") | 56 | flag.StringVar(&socket, "s", "", "GNS service socket") |
57 | flag.StringVar(¶m, "p", "", "socket parameters (<key>=<value>,...)") | ||
56 | flag.IntVar(&logLevel, "L", logger.INFO, "GNS log level (default: INFO)") | 58 | flag.IntVar(&logLevel, "L", logger.INFO, "GNS log level (default: INFO)") |
57 | flag.StringVar(&rpcEndp, "R", "", "JSON-RPC endpoint (default: none)") | 59 | flag.StringVar(&rpcEndp, "R", "", "JSON-RPC endpoint (default: none)") |
58 | flag.Parse() | 60 | flag.Parse() |
@@ -63,26 +65,33 @@ func main() { | |||
63 | return | 65 | return |
64 | } | 66 | } |
65 | 67 | ||
66 | // apply configuration | 68 | // apply configuration (from file and command-line) |
67 | logger.SetLogLevel(logLevel) | 69 | logger.SetLogLevel(logLevel) |
68 | if len(srvEndp) == 0 { | 70 | if len(socket) == 0 { |
69 | srvEndp = config.Cfg.GNS.Endpoint | 71 | socket = config.Cfg.GNS.Service.Socket |
72 | } | ||
73 | params := make(map[string]string) | ||
74 | if len(param) == 0 { | ||
75 | for _, p := range strings.Split(param, ",") { | ||
76 | kv := strings.SplitN(p, "=", 2) | ||
77 | params[kv[0]] = kv[1] | ||
78 | } | ||
79 | } else { | ||
80 | params = config.Cfg.GNS.Service.Params | ||
70 | } | 81 | } |
71 | 82 | ||
72 | // start a new GNS service | 83 | // start a new GNS service |
84 | ctx, cancel := context.WithCancel(context.Background()) | ||
73 | gns := gns.NewService() | 85 | gns := gns.NewService() |
74 | srv := service.NewServiceImpl("gns", gns) | 86 | srv := service.NewSocketHandler("gns", gns) |
75 | if err = srv.Start(srvEndp); err != nil { | 87 | if err = srv.Start(ctx, socket, params); err != nil { |
76 | logger.Printf(logger.ERROR, "[gns] Error: '%s'", err.Error()) | 88 | logger.Printf(logger.ERROR, "[gns] Error: '%s'", err.Error()) |
77 | return | 89 | return |
78 | } | 90 | } |
79 | 91 | ||
80 | // start JSON-RPC server on request | 92 | // start JSON-RPC server on request |
81 | var cancel func() = func() {} | ||
82 | if len(rpcEndp) > 0 { | 93 | if len(rpcEndp) > 0 { |
83 | var ctx context.Context | 94 | parts := strings.Split(rpcEndp, ":") |
84 | ctx, cancel = context.WithCancel(context.Background()) | ||
85 | parts := strings.Split(rpcEndp, "+") | ||
86 | if parts[0] != "tcp" { | 95 | if parts[0] != "tcp" { |
87 | logger.Println(logger.ERROR, "[gns] RPC must have a TCP/IP endpoint") | 96 | logger.Println(logger.ERROR, "[gns] RPC must have a TCP/IP endpoint") |
88 | return | 97 | return |
diff --git a/src/cmd/gnunet-service-revocation-go/main.go b/src/gnunet/cmd/gnunet-service-revocation-go/main.go index ec5ce73..e21732c 100644 --- a/src/cmd/gnunet-service-revocation-go/main.go +++ b/src/gnunet/cmd/gnunet-service-revocation-go/main.go | |||
@@ -45,14 +45,16 @@ func main() { | |||
45 | 45 | ||
46 | var ( | 46 | var ( |
47 | cfgFile string | 47 | cfgFile string |
48 | srvEndp string | 48 | socket string |
49 | param string | ||
49 | err error | 50 | err error |
50 | logLevel int | 51 | logLevel int |
51 | rpcEndp string | 52 | rpcEndp string |
52 | ) | 53 | ) |
53 | // handle command line arguments | 54 | // handle command line arguments |
54 | flag.StringVar(&cfgFile, "c", "gnunet-config.json", "GNUnet configuration file") | 55 | flag.StringVar(&cfgFile, "c", "gnunet-config.json", "GNUnet configuration file") |
55 | flag.StringVar(&srvEndp, "s", "", "REVOCATION service end-point") | 56 | flag.StringVar(&socket, "s", "", "GNS service socket") |
57 | flag.StringVar(¶m, "p", "", "socket parameters (<key>=<value>,...)") | ||
56 | flag.IntVar(&logLevel, "L", logger.INFO, "REVOCATION log level (default: INFO)") | 58 | flag.IntVar(&logLevel, "L", logger.INFO, "REVOCATION log level (default: INFO)") |
57 | flag.StringVar(&rpcEndp, "R", "", "JSON-RPC endpoint (default: none)") | 59 | flag.StringVar(&rpcEndp, "R", "", "JSON-RPC endpoint (default: none)") |
58 | flag.Parse() | 60 | flag.Parse() |
@@ -65,24 +67,31 @@ func main() { | |||
65 | 67 | ||
66 | // apply configuration | 68 | // apply configuration |
67 | logger.SetLogLevel(logLevel) | 69 | logger.SetLogLevel(logLevel) |
68 | if len(srvEndp) == 0 { | 70 | if len(socket) == 0 { |
69 | srvEndp = config.Cfg.GNS.Endpoint | 71 | socket = config.Cfg.GNS.Service.Socket |
72 | } | ||
73 | params := make(map[string]string) | ||
74 | if len(param) == 0 { | ||
75 | for _, p := range strings.Split(param, ",") { | ||
76 | kv := strings.SplitN(p, "=", 2) | ||
77 | params[kv[0]] = kv[1] | ||
78 | } | ||
79 | } else { | ||
80 | params = config.Cfg.GNS.Service.Params | ||
70 | } | 81 | } |
71 | 82 | ||
72 | // start a new REVOCATION service | 83 | // start a new REVOCATION service |
84 | ctx, cancel := context.WithCancel(context.Background()) | ||
73 | rvc := revocation.NewService() | 85 | rvc := revocation.NewService() |
74 | srv := service.NewServiceImpl("revocation", rvc) | 86 | srv := service.NewSocketHandler("revocation", rvc) |
75 | if err = srv.Start(srvEndp); err != nil { | 87 | if err = srv.Start(ctx, socket, params); err != nil { |
76 | logger.Printf(logger.ERROR, "[revocation] Error: '%s'\n", err.Error()) | 88 | logger.Printf(logger.ERROR, "[revocation] Error: '%s'\n", err.Error()) |
77 | return | 89 | return |
78 | } | 90 | } |
79 | 91 | ||
80 | // start JSON-RPC server on request | 92 | // start JSON-RPC server on request |
81 | var cancel func() = func() {} | ||
82 | if len(rpcEndp) > 0 { | 93 | if len(rpcEndp) > 0 { |
83 | var ctx context.Context | 94 | parts := strings.Split(rpcEndp, ":") |
84 | ctx, cancel = context.WithCancel(context.Background()) | ||
85 | parts := strings.Split(rpcEndp, "+") | ||
86 | if parts[0] != "tcp" { | 95 | if parts[0] != "tcp" { |
87 | logger.Println(logger.ERROR, "[revocation] RPC must have a TCP/IP endpoint") | 96 | logger.Println(logger.ERROR, "[revocation] RPC must have a TCP/IP endpoint") |
88 | return | 97 | return |
diff --git a/src/gnunet/cmd/peer_mockup/main.go b/src/gnunet/cmd/peer_mockup/main.go new file mode 100644 index 0000000..4288fb1 --- /dev/null +++ b/src/gnunet/cmd/peer_mockup/main.go | |||
@@ -0,0 +1,178 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "context" | ||
5 | "flag" | ||
6 | "fmt" | ||
7 | "os" | ||
8 | "os/signal" | ||
9 | "syscall" | ||
10 | "time" | ||
11 | |||
12 | "gnunet/config" | ||
13 | "gnunet/core" | ||
14 | "gnunet/crypto" | ||
15 | "gnunet/message" | ||
16 | "gnunet/service" | ||
17 | |||
18 | "github.com/bfix/gospel/logger" | ||
19 | ) | ||
20 | |||
21 | var ( | ||
22 | // configuration for local node | ||
23 | localCfg = &config.NodeConfig{ | ||
24 | PrivateSeed: "YGoe6XFH3XdvFRl+agx9gIzPTvxA229WFdkazEMdcOs=", | ||
25 | Endpoints: []string{ | ||
26 | "udp:127.0.0.1:2086", | ||
27 | }, | ||
28 | } | ||
29 | // configuration for remote node | ||
30 | remoteCfg = "3GXXMNb5YpIUO7ejIR2Yy0Cf5texuLfDjHkXcqbPxkc=" | ||
31 | remoteAddr = "udp:172.17.0.5:2086" | ||
32 | |||
33 | // top-level variables used accross functions | ||
34 | local *core.Peer // local peer (with private key) | ||
35 | remote *core.Peer // remote peer | ||
36 | c *core.Core | ||
37 | secret *crypto.HashCode | ||
38 | ) | ||
39 | |||
40 | func main() { | ||
41 | ctx, cancel := context.WithCancel(context.Background()) | ||
42 | defer cancel() | ||
43 | |||
44 | // handle command line arguments | ||
45 | var ( | ||
46 | asServer bool | ||
47 | err error | ||
48 | ) | ||
49 | flag.BoolVar(&asServer, "s", false, "wait for incoming connections") | ||
50 | flag.Parse() | ||
51 | |||
52 | // setup peer and core instances | ||
53 | if local, err = core.NewLocalPeer(localCfg); err != nil { | ||
54 | fmt.Println("local failed: " + err.Error()) | ||
55 | return | ||
56 | } | ||
57 | if c, err = core.NewCore(ctx, local); err != nil { | ||
58 | fmt.Println("core failed: " + err.Error()) | ||
59 | return | ||
60 | } | ||
61 | if remote, err = core.NewPeer(remoteCfg); err != nil { | ||
62 | fmt.Println("remote failed: " + err.Error()) | ||
63 | return | ||
64 | } | ||
65 | |||
66 | fmt.Println("======================================================================") | ||
67 | fmt.Println("GNUnet peer mock-up (EXPERIMENTAL) (c) 2018-2022 by Bernd Fix, >Y<") | ||
68 | fmt.Printf(" Identity '%s'\n", local.GetIDString()) | ||
69 | fmt.Printf(" [%s]\n", local.GetID().String()) | ||
70 | fmt.Println("======================================================================") | ||
71 | |||
72 | // handle messages coming from network | ||
73 | module := service.NewModuleImpl() | ||
74 | listener := module.Run(ctx, process, nil) | ||
75 | c.Register("mockup", listener) | ||
76 | |||
77 | if !asServer { | ||
78 | // we start the message exchange | ||
79 | c.Send(ctx, remote.GetID(), message.NewTransportTCPWelcomeMsg(c.PeerID())) | ||
80 | } | ||
81 | |||
82 | // handle OS signals | ||
83 | sigCh := make(chan os.Signal, 5) | ||
84 | signal.Notify(sigCh) | ||
85 | |||
86 | // heart beat | ||
87 | tick := time.NewTicker(5 * time.Minute) | ||
88 | |||
89 | loop: | ||
90 | for { | ||
91 | select { | ||
92 | // handle OS signals | ||
93 | case sig := <-sigCh: | ||
94 | switch sig { | ||
95 | case syscall.SIGKILL, syscall.SIGINT, syscall.SIGTERM: | ||
96 | logger.Printf(logger.INFO, "Terminating service (on signal '%s')\n", sig) | ||
97 | break loop | ||
98 | case syscall.SIGHUP: | ||
99 | logger.Println(logger.INFO, "SIGHUP") | ||
100 | case syscall.SIGURG: | ||
101 | // TODO: https://github.com/golang/go/issues/37942 | ||
102 | default: | ||
103 | logger.Println(logger.INFO, "Unhandled signal: "+sig.String()) | ||
104 | } | ||
105 | // handle heart beat | ||
106 | case now := <-tick.C: | ||
107 | logger.Println(logger.INFO, "Heart beat at "+now.String()) | ||
108 | } | ||
109 | } | ||
110 | // terminate pending routines | ||
111 | cancel() | ||
112 | } | ||
113 | |||
114 | // process incoming messages and send responses; it is used for protocol exploration only. | ||
115 | // it tries to mimick the message flow between "real" GNUnet peers. | ||
116 | func process(ctx context.Context, ev *core.Event) { | ||
117 | |||
118 | logger.Printf(logger.DBG, "<<< %s", ev.Msg.String()) | ||
119 | |||
120 | switch msg := ev.Msg.(type) { | ||
121 | |||
122 | case *message.TransportTCPWelcomeMsg: | ||
123 | c.Send(ctx, ev.Peer, message.NewTransportPingMsg(ev.Peer, nil)) | ||
124 | |||
125 | case *message.HelloMsg: | ||
126 | |||
127 | case *message.TransportPingMsg: | ||
128 | mOut := message.NewTransportPongMsg(msg.Challenge, nil) | ||
129 | if err := mOut.Sign(local.PrvKey()); err != nil { | ||
130 | logger.Println(logger.ERROR, "PONG: signing failed") | ||
131 | return | ||
132 | } | ||
133 | c.Send(ctx, ev.Peer, mOut) | ||
134 | logger.Printf(logger.DBG, ">>> %s", mOut) | ||
135 | |||
136 | case *message.TransportPongMsg: | ||
137 | rc, err := msg.Verify(remote.PubKey()) | ||
138 | if err != nil { | ||
139 | logger.Println(logger.ERROR, "PONG verification: "+err.Error()) | ||
140 | } | ||
141 | if !rc { | ||
142 | logger.Println(logger.ERROR, "PONG verification failed") | ||
143 | } | ||
144 | |||
145 | case *message.SessionSynMsg: | ||
146 | mOut := message.NewSessionSynAckMsg() | ||
147 | mOut.Timestamp = msg.Timestamp | ||
148 | c.Send(ctx, ev.Peer, mOut) | ||
149 | logger.Printf(logger.DBG, ">>> %s", mOut) | ||
150 | |||
151 | case *message.SessionQuotaMsg: | ||
152 | |||
153 | case *message.SessionAckMsg: | ||
154 | |||
155 | case *message.SessionKeepAliveMsg: | ||
156 | mOut := message.NewSessionKeepAliveRespMsg(msg.Nonce) | ||
157 | c.Send(ctx, ev.Peer, mOut) | ||
158 | logger.Printf(logger.DBG, ">>> %s", mOut) | ||
159 | |||
160 | case *message.EphemeralKeyMsg: | ||
161 | rc, err := msg.Verify(remote.PubKey()) | ||
162 | if err != nil { | ||
163 | logger.Println(logger.ERROR, "EPHKEY verification: "+err.Error()) | ||
164 | return | ||
165 | } else if !rc { | ||
166 | logger.Println(logger.ERROR, "EPHKEY verification failed") | ||
167 | return | ||
168 | } | ||
169 | remote.SetEphKeyMsg(msg) | ||
170 | mOut := local.EphKeyMsg() | ||
171 | c.Send(ctx, ev.Peer, mOut) | ||
172 | logger.Printf(logger.DBG, ">>> %s", mOut) | ||
173 | secret = crypto.SharedSecret(local.EphPrvKey(), remote.EphKeyMsg().Public()) | ||
174 | |||
175 | default: | ||
176 | fmt.Printf("!!! %v\n", msg) | ||
177 | } | ||
178 | } | ||
diff --git a/src/gnunet/cmd/revoke-zonekey/main.go b/src/gnunet/cmd/revoke-zonekey/main.go new file mode 100644 index 0000000..298a7e4 --- /dev/null +++ b/src/gnunet/cmd/revoke-zonekey/main.go | |||
@@ -0,0 +1,336 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package main | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "encoding/base64" | ||
24 | "flag" | ||
25 | "fmt" | ||
26 | "log" | ||
27 | "os" | ||
28 | "os/signal" | ||
29 | "sync" | ||
30 | "syscall" | ||
31 | |||
32 | "gnunet/crypto" | ||
33 | "gnunet/service/revocation" | ||
34 | "gnunet/util" | ||
35 | |||
36 | "github.com/bfix/gospel/data" | ||
37 | ) | ||
38 | |||
39 | //---------------------------------------------------------------------- | ||
40 | // Data structure used to calculate a valid revocation for a given | ||
41 | // zone key. | ||
42 | //---------------------------------------------------------------------- | ||
43 | |||
44 | // State of RevData calculation | ||
45 | const ( | ||
46 | S_NEW = iota // start new PoW calculation | ||
47 | S_CONT // continue PoW calculation | ||
48 | S_DONE // PoW calculation done | ||
49 | S_SIGNED // revocation data signed | ||
50 | ) | ||
51 | |||
52 | // RevData is the storage layout for persistent data used by this program. | ||
53 | // Data is read from and written to a file | ||
54 | type RevData struct { | ||
55 | Rd *revocation.RevDataCalc `` // Revocation data | ||
56 | T util.RelativeTime `` // time spend in calculations | ||
57 | Last uint64 `order:"big"` // last value used for PoW test | ||
58 | Numbits uint8 `` // number of leading zero-bits (difficulty) | ||
59 | State uint8 `` // processing state | ||
60 | } | ||
61 | |||
62 | // ReadRevData restores revocation data from perstistent storage. If no | ||
63 | // stored data is found, a new revocation data structure is returned. | ||
64 | func ReadRevData(filename string, bits int, zk *crypto.ZoneKey) (rd *RevData, err error) { | ||
65 | // create new initialized revocation instance with no PoWs. | ||
66 | rd = &RevData{ | ||
67 | Rd: revocation.NewRevDataCalc(zk), | ||
68 | Numbits: uint8(bits), | ||
69 | T: util.NewRelativeTime(0), | ||
70 | State: S_NEW, | ||
71 | } | ||
72 | |||
73 | // read revocation object from file. If the file does not exist, a new | ||
74 | // calculation is started; otherwise the old calculation will continue. | ||
75 | var file *os.File | ||
76 | if file, err = os.Open(filename); err != nil { | ||
77 | return | ||
78 | } | ||
79 | // read existing file | ||
80 | dataBuf := make([]byte, rd.size()) | ||
81 | var n int | ||
82 | if n, err = file.Read(dataBuf); err != nil { | ||
83 | err = fmt.Errorf("Error reading file: " + err.Error()) | ||
84 | return | ||
85 | } | ||
86 | if n != len(dataBuf) { | ||
87 | err = fmt.Errorf("File size mismatch") | ||
88 | return | ||
89 | } | ||
90 | if err = data.Unmarshal(&rd, dataBuf); err != nil { | ||
91 | err = fmt.Errorf("File corrupted: " + err.Error()) | ||
92 | return | ||
93 | } | ||
94 | if !zk.Equal(&rd.Rd.RevData.ZoneKeySig.ZoneKey) { | ||
95 | err = fmt.Errorf("Zone key mismatch") | ||
96 | return | ||
97 | } | ||
98 | bits = int(rd.Numbits) | ||
99 | if err = file.Close(); err != nil { | ||
100 | err = fmt.Errorf("Error closing file: " + err.Error()) | ||
101 | } | ||
102 | return | ||
103 | } | ||
104 | |||
105 | // Write revocation data to file | ||
106 | func (r *RevData) Write(filename string) (err error) { | ||
107 | var file *os.File | ||
108 | if file, err = os.Create(filename); err != nil { | ||
109 | return fmt.Errorf("Can't write to output file: " + err.Error()) | ||
110 | } | ||
111 | var buf []byte | ||
112 | if buf, err = data.Marshal(r); err != nil { | ||
113 | return fmt.Errorf("Internal error: " + err.Error()) | ||
114 | } | ||
115 | if len(buf) != r.size() { | ||
116 | return fmt.Errorf("Internal error: Buffer mismatch %d != %d", len(buf), r.size()) | ||
117 | } | ||
118 | var n int | ||
119 | if n, err = file.Write(buf); err != nil { | ||
120 | return fmt.Errorf("Can't write to output file: " + err.Error()) | ||
121 | } | ||
122 | if n != len(buf) { | ||
123 | return fmt.Errorf("Can't write data to output file!") | ||
124 | } | ||
125 | if err = file.Close(); err != nil { | ||
126 | return fmt.Errorf("Error closing file: " + err.Error()) | ||
127 | } | ||
128 | return | ||
129 | } | ||
130 | |||
131 | // size of the RevData instance in bytes. | ||
132 | func (r *RevData) size() int { | ||
133 | return 18 + r.Rd.Size() | ||
134 | } | ||
135 | |||
136 | // revoke-zonekey generates a revocation message in a multi-step/multi-state | ||
137 | // process run stand-alone from other GNUnet services: | ||
138 | // | ||
139 | // (1) Generate the desired PoWs for the public zone key: | ||
140 | // This process can be started, stopped and resumed, so the long | ||
141 | // calculation time (usually days or even weeks) can be interruped if | ||
142 | // 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 | ||
144 | // to calculate the PoWs. | ||
145 | // | ||
146 | // | ||
147 | // (2) A fully generated PoW set can be signed with the private key to create | ||
148 | // the final revocation data to be send out. This requires to pass the "-k" | ||
149 | // and "-z" argument. | ||
150 | // | ||
151 | // The two steps can be run (sequentially) on separate machines; step one requires | ||
152 | // computing power nd memory and step two requires a trusted environment. | ||
153 | func main() { | ||
154 | log.Println("*** Compute revocation data for a zone key") | ||
155 | log.Println("*** Copyright (c) 2020-2022, Bernd Fix >Y<") | ||
156 | log.Println("*** This is free software distributed under the Affero GPL v3.") | ||
157 | |||
158 | //------------------------------------------------------------------ | ||
159 | // handle command line arguments | ||
160 | //------------------------------------------------------------------ | ||
161 | var ( | ||
162 | verbose bool // be verbose with messages | ||
163 | bits int // number of leading zero-bit requested | ||
164 | zonekey string // zonekey to be revoked | ||
165 | prvkey string // private zonekey (base64-encoded key data) | ||
166 | testing bool // test mode (no minimum difficulty) | ||
167 | filename string // name of file for persistance | ||
168 | ) | ||
169 | minDiff := revocation.MinDifficulty | ||
170 | flag.IntVar(&bits, "b", minDiff+1, "Number of leading zero bits") | ||
171 | flag.StringVar(&zonekey, "z", "", "Zone key to be revoked (zone ID)") | ||
172 | flag.StringVar(&prvkey, "k", "", "Private zone key (base54-encoded)") | ||
173 | flag.StringVar(&filename, "f", "", "Name of file to store revocation") | ||
174 | flag.BoolVar(&verbose, "v", false, "verbose output") | ||
175 | flag.BoolVar(&testing, "t", false, "test-mode only") | ||
176 | flag.Parse() | ||
177 | |||
178 | // check arguments (difficulty, zonekey and filename) | ||
179 | if bits < minDiff { | ||
180 | if testing { | ||
181 | log.Printf("WARNING: difficulty is less than %d!", minDiff) | ||
182 | } else { | ||
183 | log.Printf("INFO: difficulty set to %d (required minimum)", minDiff) | ||
184 | bits = minDiff | ||
185 | } | ||
186 | } | ||
187 | if len(filename) == 0 { | ||
188 | log.Fatal("Missing '-f' argument (filename for revocation data)") | ||
189 | } | ||
190 | |||
191 | //------------------------------------------------------------------ | ||
192 | // Handle zone keys. | ||
193 | //------------------------------------------------------------------ | ||
194 | var ( | ||
195 | keyData []byte // binary key data | ||
196 | zk *crypto.ZoneKey // GNUnet zone key | ||
197 | sk *crypto.ZonePrivate // GNUnet private zone key | ||
198 | err error | ||
199 | ) | ||
200 | // reconstruct public key | ||
201 | if keyData, err = util.DecodeStringToBinary(zonekey, 32); err != nil { | ||
202 | log.Fatal("Invalid zonekey encoding: " + err.Error()) | ||
203 | } | ||
204 | if zk, err = crypto.NewZoneKey(keyData); err != nil { | ||
205 | log.Fatal("Invalid zonekey format: " + err.Error()) | ||
206 | } | ||
207 | // reconstruct private key (optional) | ||
208 | if len(prvkey) > 0 { | ||
209 | if keyData, err = base64.StdEncoding.DecodeString(prvkey); err != nil { | ||
210 | log.Fatal("Invalid private zonekey encoding: " + err.Error()) | ||
211 | } | ||
212 | if sk, err = crypto.NewZonePrivate(zk.Type, keyData); err != nil { | ||
213 | log.Fatal("Invalid zonekey format: " + err.Error()) | ||
214 | } | ||
215 | // verify consistency | ||
216 | if !zk.Equal(sk.Public()) { | ||
217 | log.Fatal("Public and private zone keys don't match.") | ||
218 | } | ||
219 | } | ||
220 | |||
221 | //------------------------------------------------------------------ | ||
222 | // Read revocation data from file to continue calculation or to sign | ||
223 | // the revocation. If no file exists, a new (empty) instance is | ||
224 | // returned. | ||
225 | //------------------------------------------------------------------ | ||
226 | rd, err := ReadRevData(filename, bits, zk) | ||
227 | |||
228 | // handle revocation data state | ||
229 | switch rd.State { | ||
230 | case S_NEW: | ||
231 | log.Println("Starting new revocation calculation...") | ||
232 | rd.State = S_CONT | ||
233 | |||
234 | case S_CONT: | ||
235 | log.Printf("Revocation calculation started at %s\n", rd.Rd.Timestamp.String()) | ||
236 | log.Printf("Time spent on calculation: %s\n", rd.T.String()) | ||
237 | log.Printf("Last tested PoW value: %d\n", rd.Last) | ||
238 | log.Println("Continuing...") | ||
239 | |||
240 | case S_DONE: | ||
241 | // calculation complete: sign with private key | ||
242 | if sk == nil { | ||
243 | log.Fatal("Need to sign revocation: private key is missing.") | ||
244 | } | ||
245 | log.Println("Signing revocation with private key") | ||
246 | if err := rd.Rd.Sign(sk); err != nil { | ||
247 | log.Fatal("Failed to sign revocation: " + err.Error()) | ||
248 | } | ||
249 | // write final revocation | ||
250 | rd.State = S_SIGNED | ||
251 | if err = rd.Write(filename); err != nil { | ||
252 | log.Fatal("Failed to write revocation: " + err.Error()) | ||
253 | } | ||
254 | log.Println("Revocation complete and ready for (later) use.") | ||
255 | return | ||
256 | } | ||
257 | // Continue (or start) calculation | ||
258 | log.Println("Press ^C to abort...") | ||
259 | log.Printf("Difficulty: %d\n", bits) | ||
260 | |||
261 | ctx, cancelFcn := context.WithCancel(context.Background()) | ||
262 | wg := new(sync.WaitGroup) | ||
263 | wg.Add(1) | ||
264 | go func() { | ||
265 | defer wg.Done() | ||
266 | // show progress messages | ||
267 | cb := func(average float64, last uint64) { | ||
268 | log.Printf("Improved PoW: %.2f average zero bits, %d steps\n", average, last) | ||
269 | } | ||
270 | |||
271 | // calculate revocation data until the required difficulty is met | ||
272 | // or the process is terminated by the user (by pressing ^C). | ||
273 | startTime := util.AbsoluteTimeNow() | ||
274 | average, last := rd.Rd.Compute(ctx, bits, rd.Last, cb) | ||
275 | |||
276 | // check achieved diffiulty (average) | ||
277 | if average < float64(bits) { | ||
278 | // The calculation was interrupted; we still need to compute | ||
279 | // more and better PoWs... | ||
280 | log.Printf("Incomplete revocation: Only %f zero bits on average!\n", average) | ||
281 | rd.State = S_CONT | ||
282 | } else { | ||
283 | // we have reached the required PoW difficulty | ||
284 | rd.State = S_DONE | ||
285 | // check if we have a valid revocation. | ||
286 | log.Println("Revocation calculation complete:") | ||
287 | diff, rc := rd.Rd.Verify(false) | ||
288 | switch { | ||
289 | case rc == -1: | ||
290 | log.Println(" Missing/invalid signature") | ||
291 | case rc == -2: | ||
292 | log.Println(" Expired revocation") | ||
293 | case rc == -3: | ||
294 | log.Println(" Wrong PoW sequence order") | ||
295 | case diff < float64(revocation.MinAvgDifficulty): | ||
296 | log.Println(" Difficulty to small") | ||
297 | default: | ||
298 | log.Printf(" Difficulty is %.2f\n", diff) | ||
299 | } | ||
300 | } | ||
301 | // update elapsed time | ||
302 | rd.T.Add(util.AbsoluteTimeNow().Diff(startTime)) | ||
303 | rd.Last = last | ||
304 | |||
305 | log.Println("Writing revocation data to file...") | ||
306 | if err = rd.Write(filename); err != nil { | ||
307 | log.Fatal("Can't write to file: " + err.Error()) | ||
308 | } | ||
309 | }() | ||
310 | |||
311 | go func() { | ||
312 | // handle OS signals | ||
313 | sigCh := make(chan os.Signal, 5) | ||
314 | signal.Notify(sigCh) | ||
315 | loop: | ||
316 | for { | ||
317 | select { | ||
318 | // handle OS signals | ||
319 | case sig := <-sigCh: | ||
320 | switch sig { | ||
321 | case syscall.SIGKILL, syscall.SIGINT, syscall.SIGTERM: | ||
322 | log.Printf("Terminating (on signal '%s')\n", sig) | ||
323 | cancelFcn() | ||
324 | break loop | ||
325 | case syscall.SIGHUP: | ||
326 | log.Println("SIGHUP") | ||
327 | case syscall.SIGURG: | ||
328 | // TODO: https://github.com/golang/go/issues/37942 | ||
329 | default: | ||
330 | log.Println("Unhandled signal: " + sig.String()) | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | }() | ||
335 | wg.Wait() | ||
336 | } | ||
diff --git a/src/cmd/vanityid/main.go b/src/gnunet/cmd/vanityid/main.go index 938df61..938df61 100644 --- a/src/cmd/vanityid/main.go +++ b/src/gnunet/cmd/vanityid/main.go | |||
diff --git a/src/gnunet/config/config.go b/src/gnunet/config/config.go index 914a017..41a65f0 100644 --- a/src/gnunet/config/config.go +++ b/src/gnunet/config/config.go | |||
@@ -28,56 +28,96 @@ import ( | |||
28 | "github.com/bfix/gospel/logger" | 28 | "github.com/bfix/gospel/logger" |
29 | ) | 29 | ) |
30 | 30 | ||
31 | /////////////////////////////////////////////////////////////////////// | 31 | //---------------------------------------------------------------------- |
32 | // Configuration for local node | ||
33 | //---------------------------------------------------------------------- | ||
34 | |||
35 | // NodeConfig holds parameters for the local node instance | ||
36 | type NodeConfig struct { | ||
37 | PrivateSeed string `json:"privateSeed"` // Node private key seed (base64) | ||
38 | Endpoints []string `json:"endpoints"` // list of endpoints available | ||
39 | } | ||
40 | |||
41 | //---------------------------------------------------------------------- | ||
42 | // Bootstrap configuration | ||
43 | //---------------------------------------------------------------------- | ||
44 | |||
45 | // BootstrapConfig holds parameters for the initial connection to the network. | ||
46 | type BootstrapConfig struct { | ||
47 | Nodes []string `json:"nodes"` // bootstrap nodes | ||
48 | } | ||
49 | |||
50 | //---------------------------------------------------------------------- | ||
32 | // RPC configuration | 51 | // RPC configuration |
52 | //---------------------------------------------------------------------- | ||
33 | 53 | ||
34 | // RPCConfig contains parameters for the JSON-RPC service | 54 | // RPCConfig contains parameters for the JSON-RPC service |
35 | type RPCConfig struct { | 55 | type RPCConfig struct { |
36 | Endpoint string `json:"endpoint"` // end-point of JSON-RPC service | 56 | Endpoint string `json:"endpoint"` // endpoint for JSON-RPC service |
57 | } | ||
58 | |||
59 | //---------------------------------------------------------------------- | ||
60 | // Generic service endpoint configuration (socket) | ||
61 | //---------------------------------------------------------------------- | ||
62 | |||
63 | type ServiceConfig struct { | ||
64 | Socket string `json:"socket"` // socket file name | ||
65 | Params map[string]string `json:"params"` // socket parameters | ||
37 | } | 66 | } |
38 | 67 | ||
39 | /////////////////////////////////////////////////////////////////////// | 68 | //---------------------------------------------------------------------- |
40 | // GNS configuration | 69 | // GNS configuration |
70 | //---------------------------------------------------------------------- | ||
41 | 71 | ||
42 | // GNSConfig contains parameters for the GNU Name System service | 72 | // GNSConfig contains parameters for the GNU Name System service |
43 | type GNSConfig struct { | 73 | type GNSConfig struct { |
44 | Endpoint string `json:"endpoint"` // end-point of GNS service | 74 | Service *ServiceConfig `json:"service"` // socket for GNS service |
45 | DHTReplLevel int `json:"dhtReplLevel"` // DHT replication level | 75 | DHTReplLevel int `json:"dhtReplLevel"` // DHT replication level |
46 | MaxDepth int `json:"maxDepth"` // maximum recursion depth in resolution | 76 | MaxDepth int `json:"maxDepth"` // maximum recursion depth in resolution |
47 | } | 77 | } |
48 | 78 | ||
49 | /////////////////////////////////////////////////////////////////////// | 79 | //---------------------------------------------------------------------- |
50 | // DHT configuration | 80 | // DHT configuration |
81 | //---------------------------------------------------------------------- | ||
51 | 82 | ||
52 | // DHTConfig contains parameters for the distributed hash table (DHT) | 83 | // DHTConfig contains parameters for the distributed hash table (DHT) |
53 | type DHTConfig struct { | 84 | type DHTConfig struct { |
54 | Endpoint string `json:"endpoint"` // end-point of DHT service | 85 | Service *ServiceConfig `json:"service"` // socket for DHT service |
86 | Storage string `json:"storage"` // filesystem storage location | ||
87 | Cache string `json:"cache"` // key/value cache | ||
55 | } | 88 | } |
56 | 89 | ||
57 | /////////////////////////////////////////////////////////////////////// | 90 | //---------------------------------------------------------------------- |
58 | // Namecache configuration | 91 | // Namecache configuration |
92 | //---------------------------------------------------------------------- | ||
59 | 93 | ||
60 | // NamecacheConfig contains parameters for the local name cache | 94 | // NamecacheConfig contains parameters for the local name cache |
61 | type NamecacheConfig struct { | 95 | type NamecacheConfig struct { |
62 | Endpoint string `json:"endpoint"` // end-point of Namecache service | 96 | Service *ServiceConfig `json:"service"` // socket for Namecache service |
97 | Storage string `json:"storage"` // key/value cache | ||
63 | } | 98 | } |
64 | 99 | ||
65 | /////////////////////////////////////////////////////////////////////// | 100 | //---------------------------------------------------------------------- |
66 | // Revocation configuration | 101 | // Revocation configuration |
102 | //---------------------------------------------------------------------- | ||
67 | 103 | ||
68 | // RevocationConfig contains parameters for the key revocation service | 104 | // RevocationConfig contains parameters for the key revocation service |
69 | type RevocationConfig struct { | 105 | type RevocationConfig struct { |
70 | Endpoint string `json:"endpoint"` // end-point of Revocation service | 106 | Service *ServiceConfig `json:"service"` // socket for Revocation service |
71 | Storage string `json:"storage"` // persistance mechanism for revocation data | 107 | Storage string `json:"storage"` // persistance mechanism for revocation data |
72 | } | 108 | } |
73 | 109 | ||
74 | /////////////////////////////////////////////////////////////////////// | 110 | //---------------------------------------------------------------------- |
111 | // Combined configuration | ||
112 | //---------------------------------------------------------------------- | ||
75 | 113 | ||
76 | // Environment settings | 114 | // Environment settings |
77 | type Environment map[string]string | 115 | type Environment map[string]string |
78 | 116 | ||
79 | // Config is the aggregated configuration for GNUnet. | 117 | // Config is the aggregated configuration for GNUnet. |
80 | type Config struct { | 118 | type Config struct { |
119 | Local *NodeConfig `json:"local"` | ||
120 | Bootstrap *BootstrapConfig `json:"bootstrap"` | ||
81 | Env Environment `json:"environ"` | 121 | Env Environment `json:"environ"` |
82 | RPC *RPCConfig `json:"rpc"` | 122 | RPC *RPCConfig `json:"rpc"` |
83 | DHT *DHTConfig `json:"dht"` | 123 | DHT *DHTConfig `json:"dht"` |
diff --git a/src/gnunet/config/gnunet-config.json b/src/gnunet/config/gnunet-config.json index daf65f9..941cf21 100644 --- a/src/gnunet/config/gnunet-config.json +++ b/src/gnunet/config/gnunet-config.json | |||
@@ -1,24 +1,58 @@ | |||
1 | { | 1 | { |
2 | "local": { | ||
3 | "privateSeed": "YGoe6XFH3XdvFRl+agx9gIzPTvxA229WFdkazEMdcOs=", | ||
4 | "endpoints": [ | ||
5 | "r5n+ip+udp:127.0.0.1:6666" | ||
6 | ] | ||
7 | }, | ||
8 | "bootstrap": { | ||
9 | "nodes": [ | ||
10 | "gnunet://hello/7KTBJ90340HF1Q2GB0A57E2XJER4FDHX8HP5GHEB9125VPWPD27G/BNMDFN6HJCPWSPNBSEC06MC1K8QN1Z2DHRQSRXDTFR7FTBD4JHNBJ2RJAAEZ31FWG1Q3PMN3PXGZQ3Q7NTNEKQZFA7TE2Y46FM8E20R/1653499308?r5n%2Bip%2Budp%3A127.0.0.1%3A7654" | ||
11 | ] | ||
12 | }, | ||
2 | "environ": { | 13 | "environ": { |
3 | "TMP": "/tmp", | 14 | "TMP": "/tmp", |
4 | "RT_SYS": "${TMP}/gnunet-system-runtime" | 15 | "RT_SYS": "${TMP}/gnunet-system-runtime" |
5 | }, | 16 | }, |
6 | "dht": { | 17 | "dht": { |
7 | "endpoint": "unix+${RT_SYS}/gnunet-service-dht.sock" | 18 | "service": { |
19 | "socket": "${RT_SYS}/gnunet-service-dht.sock", | ||
20 | "params": { | ||
21 | "perm": "0770" | ||
22 | } | ||
23 | }, | ||
24 | "storage": "dht_file_store+/var/lib/gnunet/dht/store", | ||
25 | "cache": "dht_file_cache+/var/lib/gnunet/dht/cache+1000" | ||
8 | }, | 26 | }, |
9 | "gns": { | 27 | "gns": { |
10 | "endpoint": "unix+${RT_SYS}/gnunet-service-gns-go.sock+perm=0770", | 28 | "service": { |
29 | "socket": "${RT_SYS}/gnunet-service-gns-go.sock", | ||
30 | "params": { | ||
31 | "perm": "0770" | ||
32 | } | ||
33 | }, | ||
11 | "dhtReplLevel": 10, | 34 | "dhtReplLevel": 10, |
12 | "maxDepth": 250 | 35 | "maxDepth": 250 |
13 | }, | 36 | }, |
14 | "namecache": { | 37 | "namecache": { |
15 | "endpoint": "unix+${RT_SYS}/gnunet-service-namecache.sock" | 38 | "service": { |
39 | "socket": "${RT_SYS}/gnunet-service-namecache.sock", | ||
40 | "params": { | ||
41 | "perm": "0770" | ||
42 | } | ||
43 | }, | ||
44 | "storage": "dht_file_cache:/var/lib/gnunet/namecache:1000" | ||
16 | }, | 45 | }, |
17 | "revocation": { | 46 | "revocation": { |
18 | "endpoint": "unix+${RT_SYS}/gnunet-service-revocation-go.sock+perm=0770", | 47 | "service": { |
19 | "storage": "redis+localhost:6397++15" | 48 | "socket": "${RT_SYS}/gnunet-service-revocation-go.sock", |
49 | "params": { | ||
50 | "perm": "0770" | ||
51 | } | ||
52 | }, | ||
53 | "storage": "redis:localhost:6397::15" | ||
20 | }, | 54 | }, |
21 | "rpc": { | 55 | "rpc": { |
22 | "endpoint": "tcp+127.0.0.1:80" | 56 | "endpoint": "tcp:127.0.0.1:80" |
23 | } | 57 | } |
24 | } | 58 | } \ No newline at end of file |
diff --git a/src/gnunet/core/core.go b/src/gnunet/core/core.go new file mode 100644 index 0000000..c3bf355 --- /dev/null +++ b/src/gnunet/core/core.go | |||
@@ -0,0 +1,234 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package core | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "gnunet/message" | ||
24 | "gnunet/service/dht/blocks" | ||
25 | "gnunet/transport" | ||
26 | "gnunet/util" | ||
27 | "net" | ||
28 | "time" | ||
29 | |||
30 | "github.com/bfix/gospel/data" | ||
31 | ) | ||
32 | |||
33 | // Core service | ||
34 | type Core struct { | ||
35 | // local peer instance | ||
36 | local *Peer | ||
37 | |||
38 | // incoming messages from transport | ||
39 | incoming chan *transport.TransportMessage | ||
40 | |||
41 | // reference to transport implementation | ||
42 | trans *transport.Transport | ||
43 | |||
44 | // registered listeners | ||
45 | listeners map[string]*Listener | ||
46 | |||
47 | // list of known peers with addresses | ||
48 | peers *util.PeerAddrList | ||
49 | } | ||
50 | |||
51 | //---------------------------------------------------------------------- | ||
52 | |||
53 | // NewCore creates and runs a new core instance. | ||
54 | func NewCore(ctx context.Context, local *Peer) (c *Core, err error) { | ||
55 | // create new core instance | ||
56 | incoming := make(chan *transport.TransportMessage) | ||
57 | c = &Core{ | ||
58 | local: local, | ||
59 | incoming: incoming, | ||
60 | listeners: make(map[string]*Listener), | ||
61 | trans: transport.NewTransport(ctx, incoming), | ||
62 | peers: util.NewPeerAddrList(), | ||
63 | } | ||
64 | // add all local peer endpoints to transport. | ||
65 | for _, addr := range local.addrList { | ||
66 | if _, err = c.trans.AddEndpoint(ctx, addr); err != nil { | ||
67 | return | ||
68 | } | ||
69 | } | ||
70 | // run message pump | ||
71 | go func() { | ||
72 | // wait for incoming messages | ||
73 | for { | ||
74 | select { | ||
75 | // get (next) message from transport | ||
76 | case tm := <-c.incoming: | ||
77 | var ev *Event | ||
78 | |||
79 | // inspect message for peer state events | ||
80 | m, err := tm.Message() | ||
81 | if err == nil { | ||
82 | switch msg := m.(type) { | ||
83 | case *message.HelloMsg: | ||
84 | // keep peer addresses | ||
85 | for _, addr := range msg.Addresses { | ||
86 | a := &util.Address{ | ||
87 | Netw: addr.Transport, | ||
88 | Address: addr.Address, | ||
89 | Expires: addr.ExpireOn, | ||
90 | } | ||
91 | c.Learn(ctx, msg.PeerID, a) | ||
92 | } | ||
93 | // generate EV_CONNECT event | ||
94 | ev = new(Event) | ||
95 | ev.ID = EV_CONNECT | ||
96 | ev.Peer = tm.Peer | ||
97 | ev.Msg = msg | ||
98 | c.dispatch(ev) | ||
99 | } | ||
100 | } | ||
101 | // generate EV_MESSAGE event | ||
102 | ev = new(Event) | ||
103 | ev.ID = EV_MESSAGE | ||
104 | ev.Peer = tm.Peer | ||
105 | ev.Msg, _ = tm.Message() | ||
106 | c.dispatch(ev) | ||
107 | |||
108 | // wait for termination | ||
109 | case <-ctx.Done(): | ||
110 | return | ||
111 | } | ||
112 | } | ||
113 | }() | ||
114 | return | ||
115 | } | ||
116 | |||
117 | //---------------------------------------------------------------------- | ||
118 | |||
119 | // Send is a function that allows the local peer to send a protocol | ||
120 | // message to a remote peer. | ||
121 | func (c *Core) Send(ctx context.Context, peer *util.PeerID, msg message.Message) error { | ||
122 | // TODO: select best endpoint protocol for transport; now fixed to UDP | ||
123 | netw := "udp" | ||
124 | addr := c.peers.Get(peer.String(), netw) | ||
125 | payload, err := data.Marshal(msg) | ||
126 | if err != nil { | ||
127 | return err | ||
128 | } | ||
129 | tm := transport.NewTransportMessage(c.PeerID(), payload) | ||
130 | return c.trans.Send(ctx, addr, tm) | ||
131 | } | ||
132 | |||
133 | // Learn a (new) address for peer | ||
134 | func (c *Core) Learn(ctx context.Context, peer *util.PeerID, addr *util.Address) (err error) { | ||
135 | if c.peers.Add(peer.String(), addr) == 1 { | ||
136 | // new peer id: send HELLO message to newly added peer | ||
137 | node := c.local | ||
138 | var hello *blocks.HelloBlock | ||
139 | hello, err = node.HelloData(time.Hour) | ||
140 | if err != nil { | ||
141 | return | ||
142 | } | ||
143 | msg := message.NewHelloMsg(node.GetID()) | ||
144 | for _, a := range hello.Addresses() { | ||
145 | ha := message.NewHelloAddress(a) | ||
146 | msg.AddAddress(ha) | ||
147 | } | ||
148 | err = c.Send(ctx, peer, msg) | ||
149 | } | ||
150 | return | ||
151 | } | ||
152 | |||
153 | // PeerID returns the peer id of the local node. | ||
154 | func (c *Core) PeerID() *util.PeerID { | ||
155 | return c.local.GetID() | ||
156 | } | ||
157 | |||
158 | // TryConnect is a function which allows the local peer to attempt the | ||
159 | // establishment of a connection to another peer using an address. | ||
160 | // When the connection attempt is successful, information on the new | ||
161 | // peer is offered through the PEER_CONNECTED signal. | ||
162 | func (c *Core) TryConnect(peer *util.PeerID, addr net.Addr) error { | ||
163 | // select endpoint for address | ||
164 | if ep := c.findEndpoint(peer, addr); ep == nil { | ||
165 | return transport.ErrTransNoEndpoint | ||
166 | } | ||
167 | return nil | ||
168 | } | ||
169 | |||
170 | func (c *Core) findEndpoint(peer *util.PeerID, addr net.Addr) transport.Endpoint { | ||
171 | return nil | ||
172 | } | ||
173 | |||
174 | // Hold is a function which tells the underlay to keep a hold on to a | ||
175 | // connection to a peer P. Underlays are usually limited in the number | ||
176 | // of active connections. With this function the DHT can indicate to the | ||
177 | // underlay which connections should preferably be preserved. | ||
178 | func (c *Core) Hold(peer *util.PeerID) {} | ||
179 | |||
180 | // Drop is a function which tells the underlay to drop the connection to a | ||
181 | // peer P. This function is only there for symmetry and used during the | ||
182 | // peer's shutdown to release all of the remaining HOLDs. As R5N always | ||
183 | // prefers the longest-lived connections, it would never drop an active | ||
184 | // connection that it has called HOLD() on before. Nevertheless, underlay | ||
185 | // implementations should not rely on this always being true. A call to | ||
186 | // DROP() also does not imply that the underlay must close the connection: | ||
187 | // it merely removes the preference to preserve the connection that was | ||
188 | // established by HOLD(). | ||
189 | func (c *Core) Drop(peer *util.PeerID) {} | ||
190 | |||
191 | // L2NSE is ESTIMATE_NETWORK_SIZE(), a procedure that provides estimates | ||
192 | // on the base-2 logarithm of the network size L2NSE, that is the base-2 | ||
193 | // logarithm number of peers in the network, for use by the routing | ||
194 | // algorithm. | ||
195 | func (c *Core) L2NSE() float64 { | ||
196 | return 0. | ||
197 | } | ||
198 | |||
199 | //---------------------------------------------------------------------- | ||
200 | // Event listener and event dispatch. | ||
201 | //---------------------------------------------------------------------- | ||
202 | |||
203 | // Register a named event listener. | ||
204 | func (c *Core) Register(name string, l *Listener) { | ||
205 | c.listeners[name] = l | ||
206 | } | ||
207 | |||
208 | // Unregister named event listener. | ||
209 | func (c *Core) Unregister(name string) *Listener { | ||
210 | if l, ok := c.listeners[name]; ok { | ||
211 | delete(c.listeners, name) | ||
212 | return l | ||
213 | } | ||
214 | return nil | ||
215 | } | ||
216 | |||
217 | // internal: dispatch event to listeners | ||
218 | func (c *Core) dispatch(ev *Event) { | ||
219 | // dispatch event to listeners | ||
220 | for _, l := range c.listeners { | ||
221 | if l.filter.CheckEvent(ev.ID) { | ||
222 | mt := ev.Msg.Header().MsgType | ||
223 | if ev.ID == EV_MESSAGE { | ||
224 | if mt != 0 && !l.filter.CheckMsgType(mt) { | ||
225 | // skip event | ||
226 | return | ||
227 | } | ||
228 | } | ||
229 | go func() { | ||
230 | l.ch <- ev | ||
231 | }() | ||
232 | } | ||
233 | } | ||
234 | } | ||
diff --git a/src/gnunet/core/core_test.go b/src/gnunet/core/core_test.go new file mode 100644 index 0000000..102abf1 --- /dev/null +++ b/src/gnunet/core/core_test.go | |||
@@ -0,0 +1,150 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package core | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "gnunet/config" | ||
24 | "gnunet/util" | ||
25 | "testing" | ||
26 | "time" | ||
27 | ) | ||
28 | |||
29 | var ( | ||
30 | peer1Cfg = &config.NodeConfig{ | ||
31 | PrivateSeed: "iYK1wSi5XtCP774eNFk1LYXqKlOPEpwKBw+2/bMkE24=", | ||
32 | Endpoints: []string{"udp://127.0.0.1:20861"}, | ||
33 | } | ||
34 | |||
35 | peer2Cfg = &config.NodeConfig{ | ||
36 | PrivateSeed: "Bv9umksEO51jjWWrOGEH+4r8wl9Vi+LItpdBpTOi2PE=", | ||
37 | Endpoints: []string{"udp://127.0.0.1:20862"}, | ||
38 | } | ||
39 | ) | ||
40 | |||
41 | //---------------------------------------------------------------------- | ||
42 | // create and run a node with given spec | ||
43 | //---------------------------------------------------------------------- | ||
44 | |||
45 | type TestNode struct { | ||
46 | id int | ||
47 | t *testing.T | ||
48 | peer *Peer | ||
49 | core *Core | ||
50 | addr *util.Address | ||
51 | } | ||
52 | |||
53 | func (n *TestNode) Learn(ctx context.Context, peer *util.PeerID, addr *util.Address) { | ||
54 | n.t.Logf("[%d] Learning %s for %s", n.id, addr.StringAll(), peer.String()) | ||
55 | n.core.Learn(ctx, peer, addr) | ||
56 | } | ||
57 | |||
58 | func NewTestNode(t *testing.T, ctx context.Context, cfg *config.NodeConfig) (node *TestNode, err error) { | ||
59 | |||
60 | // create test node | ||
61 | node = new(TestNode) | ||
62 | node.t = t | ||
63 | node.id = util.NextID() | ||
64 | |||
65 | // create peer object | ||
66 | if node.peer, err = NewLocalPeer(cfg); err != nil { | ||
67 | return | ||
68 | } | ||
69 | t.Logf("[%d] Node %s starting", node.id, node.peer.GetID()) | ||
70 | |||
71 | // create core service | ||
72 | if node.core, err = NewCore(ctx, node.peer); err != nil { | ||
73 | return | ||
74 | } | ||
75 | for _, addr := range node.core.trans.Endpoints() { | ||
76 | s := addr.Network() + ":" + addr.String() | ||
77 | if node.addr, err = util.ParseAddress(s); err != nil { | ||
78 | continue | ||
79 | } | ||
80 | t.Logf("[%d] Listening on %s", node.id, s) | ||
81 | } | ||
82 | |||
83 | // register as event listener | ||
84 | incoming := make(chan *Event) | ||
85 | node.core.Register("test", NewListener(incoming, nil)) | ||
86 | |||
87 | // heart beat | ||
88 | tick := time.NewTicker(5 * time.Minute) | ||
89 | |||
90 | // run event handler | ||
91 | go func() { | ||
92 | for { | ||
93 | select { | ||
94 | // show incoming event | ||
95 | case ev := <-incoming: | ||
96 | switch ev.ID { | ||
97 | case EV_CONNECT: | ||
98 | t.Logf("[%d] <<< Peer %s connected", node.id, ev.Peer) | ||
99 | case EV_DISCONNECT: | ||
100 | t.Logf("[%d] <<< Peer %s diconnected", node.id, ev.Peer) | ||
101 | case EV_MESSAGE: | ||
102 | t.Logf("[%d] <<< Msg from %s of type %d", node.id, ev.Peer, ev.Msg.Header().MsgType) | ||
103 | } | ||
104 | |||
105 | // handle termination signal | ||
106 | case <-ctx.Done(): | ||
107 | t.Logf("[%d] Shutting down node", node.id) | ||
108 | return | ||
109 | |||
110 | // handle heart beat | ||
111 | case now := <-tick.C: | ||
112 | t.Logf("[%d] Heart beat at %s", node.id, now.String()) | ||
113 | } | ||
114 | } | ||
115 | }() | ||
116 | return | ||
117 | } | ||
118 | |||
119 | //---------------------------------------------------------------------- | ||
120 | // Two node GNUnet (smallest and simplest network) | ||
121 | //---------------------------------------------------------------------- | ||
122 | |||
123 | // TestCoreSimple test a two node network | ||
124 | func TestCoreSimple(t *testing.T) { | ||
125 | |||
126 | // setup execution context | ||
127 | ctx, cancel := context.WithCancel(context.Background()) | ||
128 | defer func() { | ||
129 | cancel() | ||
130 | time.Sleep(time.Second) | ||
131 | }() | ||
132 | |||
133 | // create and run nodes | ||
134 | node1, err := NewTestNode(t, ctx, peer1Cfg) | ||
135 | if err != nil { | ||
136 | t.Fatal(err) | ||
137 | } | ||
138 | node2, err := NewTestNode(t, ctx, peer2Cfg) | ||
139 | if err != nil { | ||
140 | t.Fatal(err) | ||
141 | } | ||
142 | |||
143 | // learn peer addresses (triggers HELLO) | ||
144 | for _, addr := range node2.core.trans.Endpoints() { | ||
145 | node1.Learn(ctx, node2.peer.GetID(), util.NewAddressWrap(addr)) | ||
146 | } | ||
147 | |||
148 | // wait for 5 seconds | ||
149 | time.Sleep(5 * time.Second) | ||
150 | } | ||
diff --git a/src/gnunet/core/event.go b/src/gnunet/core/event.go new file mode 100644 index 0000000..4eab112 --- /dev/null +++ b/src/gnunet/core/event.go | |||
@@ -0,0 +1,109 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package core | ||
20 | |||
21 | import ( | ||
22 | "gnunet/message" | ||
23 | "gnunet/util" | ||
24 | ) | ||
25 | |||
26 | //---------------------------------------------------------------------- | ||
27 | // Core events and listeners | ||
28 | //---------------------------------------------------------------------- | ||
29 | |||
30 | // Event types | ||
31 | const ( | ||
32 | EV_CONNECT = iota // peer connected | ||
33 | EV_DISCONNECT // peer disconnected | ||
34 | EV_MESSAGE // incoming message | ||
35 | ) | ||
36 | |||
37 | // EventFilter is a filter for events a listener is interested in. | ||
38 | // The filter works on event types; if EV_MESSAGE is set, messages | ||
39 | // can be filtered by message type also. | ||
40 | type EventFilter struct { | ||
41 | evTypes map[int]bool | ||
42 | msgTypes map[uint16]bool | ||
43 | } | ||
44 | |||
45 | // NewEventFilter creates a new empty filter instance. | ||
46 | func NewEventFilter() *EventFilter { | ||
47 | return &EventFilter{ | ||
48 | evTypes: make(map[int]bool), | ||
49 | msgTypes: make(map[uint16]bool), | ||
50 | } | ||
51 | } | ||
52 | |||
53 | // AddEvent add an event id to filter | ||
54 | func (f *EventFilter) AddEvent(ev int) { | ||
55 | f.evTypes[ev] = true | ||
56 | } | ||
57 | |||
58 | // AddMsgType adds a message type to filter | ||
59 | func (f *EventFilter) AddMsgType(mt uint16) { | ||
60 | f.evTypes[EV_MESSAGE] = true | ||
61 | f.msgTypes[mt] = true | ||
62 | } | ||
63 | |||
64 | // CheckEvent returns true if an event id is matched | ||
65 | // by the filter or the filter is empty. | ||
66 | func (f *EventFilter) CheckEvent(ev int) bool { | ||
67 | if len(f.evTypes) == 0 { | ||
68 | return true | ||
69 | } | ||
70 | _, ok := f.evTypes[ev] | ||
71 | return ok | ||
72 | } | ||
73 | |||
74 | // CheckMsgType returns true if a message type is matched | ||
75 | // by the filter or the filter is empty. | ||
76 | func (f *EventFilter) CheckMsgType(mt uint16) bool { | ||
77 | if len(f.msgTypes) == 0 { | ||
78 | return true | ||
79 | } | ||
80 | _, ok := f.msgTypes[mt] | ||
81 | return ok | ||
82 | } | ||
83 | |||
84 | // Event sent to listeners | ||
85 | type Event struct { | ||
86 | ID int // event type | ||
87 | Peer *util.PeerID // remote peer | ||
88 | Msg message.Message // GNUnet message (can be nil) | ||
89 | } | ||
90 | |||
91 | //---------------------------------------------------------------------- | ||
92 | |||
93 | // Listener for network events | ||
94 | type Listener struct { | ||
95 | ch chan *Event // listener channel | ||
96 | filter *EventFilter // event filter settimgs | ||
97 | } | ||
98 | |||
99 | // NewListener for given filter and receiving channel | ||
100 | func NewListener(ch chan *Event, f *EventFilter) *Listener { | ||
101 | if f == nil { | ||
102 | // set empty default filter | ||
103 | f = NewEventFilter() | ||
104 | } | ||
105 | return &Listener{ | ||
106 | ch: ch, | ||
107 | filter: f, | ||
108 | } | ||
109 | } | ||
diff --git a/src/gnunet/core/peer.go b/src/gnunet/core/peer.go index f81bab8..2b6fe74 100644 --- a/src/gnunet/core/peer.go +++ b/src/gnunet/core/peer.go | |||
@@ -1,5 +1,5 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | 1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. |
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | 2 | // Copyright (C) 2019-2022 Bernd Fix >Y< |
3 | // | 3 | // |
4 | // gnunet-go is free software: you can redistribute it and/or modify it | 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 | 5 | // under the terms of the GNU Affero General Public License as published |
@@ -19,14 +19,30 @@ | |||
19 | package core | 19 | package core |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "encoding/base64" | ||
22 | "fmt" | 23 | "fmt" |
24 | "time" | ||
23 | 25 | ||
26 | "gnunet/config" | ||
24 | "gnunet/message" | 27 | "gnunet/message" |
28 | "gnunet/service/dht/blocks" | ||
25 | "gnunet/util" | 29 | "gnunet/util" |
26 | 30 | ||
27 | "github.com/bfix/gospel/crypto/ed25519" | 31 | "github.com/bfix/gospel/crypto/ed25519" |
28 | ) | 32 | ) |
29 | 33 | ||
34 | //---------------------------------------------------------------------- | ||
35 | // GNUnet P2P network node (local or remote): | ||
36 | // | ||
37 | // * A LOCAL node has a long-term EdDSA key pair used for signing. The | ||
38 | // public key is the node identifier (PeerID). | ||
39 | // Local nodes hold additional attributes like ephemeral keys for message | ||
40 | // exchange or a list of network addresses the node can be reached on. | ||
41 | // | ||
42 | // * A REMOTE node only has a public EdDSA key used by the local node | ||
43 | // to verify signatures from the remote node. | ||
44 | //---------------------------------------------------------------------- | ||
45 | |||
30 | // Peer represents a node in the GNUnet P2P network. | 46 | // Peer represents a node in the GNUnet P2P network. |
31 | type Peer struct { | 47 | type Peer struct { |
32 | prv *ed25519.PrivateKey // node private key (long-term signing key) | 48 | prv *ed25519.PrivateKey // node private key (long-term signing key) |
@@ -37,27 +53,87 @@ type Peer struct { | |||
37 | ephMsg *message.EphemeralKeyMsg // ephemeral signing key message | 53 | ephMsg *message.EphemeralKeyMsg // ephemeral signing key message |
38 | } | 54 | } |
39 | 55 | ||
40 | // NewPeer instantiates a new peer object from given data. If a local peer | 56 | //---------------------------------------------------------------------- |
41 | // is created, the data is the seed for generating the private key of the node; | 57 | // Create new peer objects |
42 | // for a remote peer the data is the binary representation of its public key. | 58 | //---------------------------------------------------------------------- |
43 | func NewPeer(data []byte, local bool) (p *Peer, err error) { | 59 | |
60 | // NewLocalPeer creates a new local node from configuration data. | ||
61 | func NewLocalPeer(cfg *config.NodeConfig) (p *Peer, err error) { | ||
44 | p = new(Peer) | 62 | p = new(Peer) |
45 | if local { | 63 | |
46 | p.prv = ed25519.NewPrivateKeyFromSeed(data) | 64 | // get the key material for local node |
47 | p.pub = p.prv.Public() | 65 | var data []byte |
48 | p.ephPrv, p.ephMsg, err = message.NewEphemeralKey(p.pub.Bytes(), p.prv) | 66 | if data, err = base64.StdEncoding.DecodeString(cfg.PrivateSeed); err != nil { |
49 | if err != nil { | 67 | return |
68 | } | ||
69 | p.prv = ed25519.NewPrivateKeyFromSeed(data) | ||
70 | p.pub = p.prv.Public() | ||
71 | p.idString = util.EncodeBinaryToString(p.pub.Bytes()) | ||
72 | p.ephPrv, p.ephMsg, err = message.NewEphemeralKey(p.pub.Bytes(), p.prv) | ||
73 | if err != nil { | ||
74 | return | ||
75 | } | ||
76 | // set the endpoint addresses for local node | ||
77 | p.addrList = make([]*util.Address, len(cfg.Endpoints)) | ||
78 | var addr *util.Address | ||
79 | for i, a := range cfg.Endpoints { | ||
80 | if addr, err = util.ParseAddress(a); err != nil { | ||
50 | return | 81 | return |
51 | } | 82 | } |
52 | } else { | 83 | addr.Expires = util.NewAbsoluteTime(time.Now().Add(12 * time.Hour)) |
53 | p.prv = nil | 84 | p.addrList[i] = addr |
54 | p.pub = ed25519.NewPublicKeyFromBytes(data) | ||
55 | } | 85 | } |
86 | return | ||
87 | } | ||
88 | |||
89 | // NewPeer instantiates a new (remote) peer object from given peer ID string. | ||
90 | func NewPeer(peerID string) (p *Peer, err error) { | ||
91 | p = new(Peer) | ||
92 | |||
93 | // get the key material for local node | ||
94 | var data []byte | ||
95 | if data, err = util.DecodeStringToBinary(peerID, 32); err != nil { | ||
96 | return | ||
97 | } | ||
98 | p.prv = nil | ||
99 | p.pub = ed25519.NewPublicKeyFromBytes(data) | ||
56 | p.idString = util.EncodeBinaryToString(p.pub.Bytes()) | 100 | p.idString = util.EncodeBinaryToString(p.pub.Bytes()) |
57 | p.addrList = make([]*util.Address, 0) | 101 | p.addrList = make([]*util.Address, 0) |
58 | return | 102 | return |
59 | } | 103 | } |
60 | 104 | ||
105 | //---------------------------------------------------------------------- | ||
106 | //---------------------------------------------------------------------- | ||
107 | |||
108 | // Address returns a peer address for the given transport protocol | ||
109 | func (p *Peer) Address(transport string) *util.Address { | ||
110 | for _, addr := range p.addrList { | ||
111 | // skip expired entries | ||
112 | if addr.Expires.Expired() { | ||
113 | continue | ||
114 | } | ||
115 | // filter by transport protocol | ||
116 | if len(transport) > 0 && transport != addr.Netw { | ||
117 | continue | ||
118 | } | ||
119 | return addr | ||
120 | } | ||
121 | return nil | ||
122 | } | ||
123 | |||
124 | // HelloData returns the current HELLO data for the peer | ||
125 | func (p *Peer) HelloData(ttl time.Duration) (h *blocks.HelloBlock, err error) { | ||
126 | // assemble HELLO data | ||
127 | h = new(blocks.HelloBlock) | ||
128 | h.PeerID = p.GetID() | ||
129 | h.Expire = util.NewAbsoluteTime(time.Now().Add(ttl)) | ||
130 | h.SetAddresses(p.addrList) | ||
131 | |||
132 | // sign data | ||
133 | err = h.Sign(p.prv) | ||
134 | return | ||
135 | } | ||
136 | |||
61 | // EphKeyMsg returns a new initialized message to negotiate session keys. | 137 | // EphKeyMsg returns a new initialized message to negotiate session keys. |
62 | func (p *Peer) EphKeyMsg() *message.EphemeralKeyMsg { | 138 | func (p *Peer) EphKeyMsg() *message.EphemeralKeyMsg { |
63 | return p.ephMsg | 139 | return p.ephMsg |
@@ -84,8 +160,10 @@ func (p *Peer) PubKey() *ed25519.PublicKey { | |||
84 | } | 160 | } |
85 | 161 | ||
86 | // GetID returns the node ID (public key) in binary format | 162 | // GetID returns the node ID (public key) in binary format |
87 | func (p *Peer) GetID() []byte { | 163 | func (p *Peer) GetID() *util.PeerID { |
88 | return p.pub.Bytes() | 164 | return &util.PeerID{ |
165 | Key: util.Clone(p.pub.Bytes()), | ||
166 | } | ||
89 | } | 167 | } |
90 | 168 | ||
91 | // GetIDString returns the string representation of the public key of the node. | 169 | // GetIDString returns the string representation of the public key of the node. |
diff --git a/src/gnunet/core/peer_test.go b/src/gnunet/core/peer_test.go new file mode 100644 index 0000000..28b328f --- /dev/null +++ b/src/gnunet/core/peer_test.go | |||
@@ -0,0 +1,72 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package core | ||
20 | |||
21 | import ( | ||
22 | "gnunet/config" | ||
23 | "gnunet/service/dht/blocks" | ||
24 | "testing" | ||
25 | "time" | ||
26 | ) | ||
27 | |||
28 | // test data | ||
29 | var ( | ||
30 | cfg = &config.NodeConfig{ | ||
31 | PrivateSeed: "YGoe6XFH3XdvFRl+agx9gIzPTvxA229WFdkazEMdcOs=", | ||
32 | Endpoints: []string{ | ||
33 | "r5n+ip+udp://127.0.0.1:6666", | ||
34 | }, | ||
35 | } | ||
36 | TTL = 6 * time.Hour | ||
37 | ) | ||
38 | |||
39 | func TestPeerHello(t *testing.T) { | ||
40 | |||
41 | // generate new local node | ||
42 | node, err := NewLocalPeer(cfg) | ||
43 | if err != nil { | ||
44 | t.Fatal(err) | ||
45 | } | ||
46 | |||
47 | // get HELLO data for the node | ||
48 | h, err := node.HelloData(TTL) | ||
49 | |||
50 | // convert to URL and back | ||
51 | u := h.URL() | ||
52 | t.Log(u) | ||
53 | h2, err := blocks.ParseHelloURL(u) | ||
54 | if err != nil { | ||
55 | t.Fatal(err) | ||
56 | } | ||
57 | u2 := h2.URL() | ||
58 | t.Log(u2) | ||
59 | |||
60 | // check if HELLO data is the same | ||
61 | if !h.Equals(h2) { | ||
62 | t.Fatal("HELLO data mismatch") | ||
63 | } | ||
64 | // verify signature | ||
65 | ok, err := h.Verify() | ||
66 | if err != nil { | ||
67 | t.Fatal(err) | ||
68 | } | ||
69 | if !ok { | ||
70 | t.Fatal("failed to verify signature") | ||
71 | } | ||
72 | } | ||
diff --git a/src/gnunet/crypto/gns.go b/src/gnunet/crypto/gns.go index 6aa7972..f4c5627 100644 --- a/src/gnunet/crypto/gns.go +++ b/src/gnunet/crypto/gns.go | |||
@@ -58,7 +58,8 @@ import ( | |||
58 | // example the RSA crypto scheme is outlined: | 58 | // example the RSA crypto scheme is outlined: |
59 | // | 59 | // |
60 | // (1) Register/define a new GNS_TYPE_RSAKEY | 60 | // (1) Register/define a new GNS_TYPE_RSAKEY |
61 | // (2) Add ZONE_RSAKEY to the "Zone types" declarations below. | 61 | // (2) Add ZONE_RSAKEY and GNS_TYPE_RSAKEY to the "Zone types" |
62 | // declarations in this file. | ||
62 | // (3) Code the implementation in a file named `gns_rsakey.go`: | 63 | // (3) Code the implementation in a file named `gns_rsakey.go`: |
63 | // You have to implement three interfaces (ZonePrivateImpl, | 64 | // You have to implement three interfaces (ZonePrivateImpl, |
64 | // ZoneKeyImpl and ZoneSigImpl) in three separate custom types. | 65 | // ZoneKeyImpl and ZoneSigImpl) in three separate custom types. |
@@ -145,6 +146,12 @@ type ZoneSigImpl interface { | |||
145 | var ( | 146 | var ( |
146 | ZONE_PKEY = uint32(enums.GNS_TYPE_PKEY) | 147 | ZONE_PKEY = uint32(enums.GNS_TYPE_PKEY) |
147 | ZONE_EDKEY = uint32(enums.GNS_TYPE_EDKEY) | 148 | ZONE_EDKEY = uint32(enums.GNS_TYPE_EDKEY) |
149 | |||
150 | // register available zone types for BlockHandler | ||
151 | ZoneTypes = []int{ | ||
152 | enums.GNS_TYPE_PKEY, | ||
153 | enums.GNS_TYPE_EDKEY, | ||
154 | } | ||
148 | ) | 155 | ) |
149 | 156 | ||
150 | //---------------------------------------------------------------------- | 157 | //---------------------------------------------------------------------- |
diff --git a/src/gnunet/crypto/hash.go b/src/gnunet/crypto/hash.go index bc715d4..049a8ef 100644 --- a/src/gnunet/crypto/hash.go +++ b/src/gnunet/crypto/hash.go | |||
@@ -19,6 +19,7 @@ | |||
19 | package crypto | 19 | package crypto |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "bytes" | ||
22 | "crypto/sha512" | 23 | "crypto/sha512" |
23 | 24 | ||
24 | "gnunet/util" | 25 | "gnunet/util" |
@@ -29,11 +30,20 @@ type HashCode struct { | |||
29 | Bits []byte `size:"64"` | 30 | Bits []byte `size:"64"` |
30 | } | 31 | } |
31 | 32 | ||
32 | // NewHashCode creates a new, uninitalized hash value | 33 | // Equals tests if two hash results are equal. |
33 | func NewHashCode() *HashCode { | 34 | func (hc *HashCode) Equals(n *HashCode) bool { |
34 | return &HashCode{ | 35 | return bytes.Equal(hc.Bits, n.Bits) |
36 | } | ||
37 | |||
38 | // NewHashCode creates a new (initalized) hash value | ||
39 | func NewHashCode(buf []byte) *HashCode { | ||
40 | hc := &HashCode{ | ||
35 | Bits: make([]byte, 64), | 41 | Bits: make([]byte, 64), |
36 | } | 42 | } |
43 | if buf != nil { | ||
44 | util.CopyAlignedBlock(hc.Bits, buf) | ||
45 | } | ||
46 | return hc | ||
37 | } | 47 | } |
38 | 48 | ||
39 | // Hash returns the SHA-512 hash value of a given blob | 49 | // Hash returns the SHA-512 hash value of a given blob |
diff --git a/src/gnunet/go.mod b/src/gnunet/go.mod index 8527379..7383eaf 100644 --- a/src/gnunet/go.mod +++ b/src/gnunet/go.mod | |||
@@ -1,21 +1,25 @@ | |||
1 | module gnunet | 1 | module gnunet |
2 | 2 | ||
3 | go 1.17 | 3 | go 1.18 |
4 | 4 | ||
5 | require ( | 5 | require ( |
6 | github.com/bfix/gospel v1.2.10 | 6 | github.com/bfix/gospel v1.2.11 |
7 | github.com/go-redis/redis/v8 v8.5.0 | 7 | github.com/go-redis/redis/v8 v8.11.5 |
8 | github.com/go-sql-driver/mysql v1.5.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/mattn/go-sqlite3 v1.14.6 | 10 | github.com/mattn/go-sqlite3 v1.14.13 |
11 | github.com/miekg/dns v1.1.26 | 11 | github.com/miekg/dns v1.1.49 |
12 | golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed | 12 | golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 |
13 | ) | 13 | ) |
14 | 14 | ||
15 | require ( | 15 | require ( |
16 | github.com/cespare/xxhash/v2 v2.1.1 // indirect | 16 | github.com/cespare/xxhash/v2 v2.1.2 // indirect |
17 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect | 17 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect |
18 | go.opentelemetry.io/otel v0.16.0 // indirect | 18 | golang.org/x/mod v0.4.2 // indirect |
19 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect | 19 | golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect |
20 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect | 20 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect |
21 | golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect | ||
22 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect | ||
21 | ) | 23 | ) |
24 | |||
25 | replace github.com/bfix/gospel v1.2.11 => /vault/prj/libs/Go/Gospel | ||
diff --git a/src/gnunet/go.sum b/src/gnunet/go.sum index 4fa501c..ea3c328 100644 --- a/src/gnunet/go.sum +++ b/src/gnunet/go.sum | |||
@@ -1,123 +1,67 @@ | |||
1 | github.com/bfix/gospel v1.2.10 h1:a8l/sET2y+FVKIO5M1l5hdTlqLxstvkhp+b6FpAkxOU= | 1 | github.com/bfix/gospel v1.2.11 h1:z/c6MFNq/lz4mO8+PK60a3NvH+lbTKAlLCShuFFZUvg= |
2 | github.com/bfix/gospel v1.2.10/go.mod h1:cdu63bA9ZdfeDoqZ+vnWOcbY9Puwdzmf5DMxMGMznRI= | 2 | github.com/bfix/gospel v1.2.11/go.mod h1:cdu63bA9ZdfeDoqZ+vnWOcbY9Puwdzmf5DMxMGMznRI= |
3 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= | 3 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= |
4 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | 4 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= |
5 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||
6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
7 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= | 5 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= |
8 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= | 6 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= |
9 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | ||
10 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= | 7 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= |
11 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= | 8 | github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= |
12 | github.com/go-redis/redis/v8 v8.5.0 h1:L3r1Q3I5WOUdXZGCP6g44EruKh0u3n6co5Hl5xWkdGA= | 9 | github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= |
13 | github.com/go-redis/redis/v8 v8.5.0/go.mod h1:YmEcgBDttjnkbMzDAhDtQxY9yVA7jMN6PCR5HeMvqFE= | 10 | github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= |
14 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= | 11 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= |
15 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | ||
16 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||
17 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||
18 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | ||
19 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | ||
20 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | ||
21 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | ||
22 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||
23 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||
24 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||
25 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
26 | github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= | ||
27 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
28 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= | 12 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= |
29 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= | 13 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= |
30 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | ||
31 | github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= | 14 | github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= |
32 | github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= | 15 | github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= |
33 | github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= | 16 | github.com/mattn/go-sqlite3 v1.14.13 h1:1tj15ngiFfcZzii7yd82foL+ks+ouQcj8j/TPq3fk1I= |
34 | github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= | 17 | github.com/mattn/go-sqlite3 v1.14.13/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= |
35 | github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU= | 18 | github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= |
36 | github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= | 19 | github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= |
37 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= | 20 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= |
38 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= | 21 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= |
39 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= | 22 | github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= |
40 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= | 23 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= |
41 | github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4= | ||
42 | github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= | ||
43 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= | ||
44 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= | ||
45 | github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ= | ||
46 | github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= | ||
47 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
48 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
49 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
50 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= | ||
51 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
52 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||
53 | go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw= | ||
54 | go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA= | ||
55 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | 24 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
56 | golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= | ||
57 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | 25 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |
58 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||
59 | golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= | 26 | golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= |
60 | golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed h1:YoWVYYAfvQ4ddHv3OKmIvX7NCAhFGTj62VP2l2kfBbA= | 27 | golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0= |
61 | golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | 28 | golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= |
62 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | 29 | golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= |
63 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | 30 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= |
64 | golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | 31 | golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= |
65 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | 32 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
66 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | 33 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |
67 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | 34 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= |
68 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | 35 | golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= |
69 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | 36 | golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= |
70 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | 37 | golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= |
71 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= | ||
72 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||
73 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
74 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | 38 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
75 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= | 39 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= |
76 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | 40 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |
77 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
78 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | 41 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
79 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 42 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
80 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
81 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
82 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
83 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
84 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 43 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
85 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
86 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
87 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
88 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 44 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
89 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 45 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
90 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | 46 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
91 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= | 47 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
92 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | 48 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
49 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= | ||
50 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
93 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= | 51 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= |
94 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | 52 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= |
95 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | 53 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
96 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||
97 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | 54 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
98 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | 55 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
99 | golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= | ||
100 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | 56 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |
57 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= | ||
101 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | 58 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |
102 | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||
103 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | 59 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |
104 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | 60 | golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0= |
61 | golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | ||
105 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | 62 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
106 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | 63 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
107 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | 64 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= |
108 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | 65 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
109 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | ||
110 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||
111 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||
112 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | ||
113 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | ||
114 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||
115 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
116 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | ||
117 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= | 66 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= |
118 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | 67 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= |
119 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||
120 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= | ||
121 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||
122 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||
123 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
diff --git a/src/gnunet/message/factory.go b/src/gnunet/message/factory.go index 8cbf9ce..6681eae 100644 --- a/src/gnunet/message/factory.go +++ b/src/gnunet/message/factory.go | |||
@@ -56,12 +56,16 @@ func NewEmptyMessage(msgType uint16) (Message, error) { | |||
56 | //------------------------------------------------------------------ | 56 | //------------------------------------------------------------------ |
57 | // DHT | 57 | // DHT |
58 | //------------------------------------------------------------------ | 58 | //------------------------------------------------------------------ |
59 | case DHT_CLIENT_PUT: | ||
60 | return NewDHTClientPutMsg(nil, 0, nil), nil | ||
59 | case DHT_CLIENT_GET: | 61 | case DHT_CLIENT_GET: |
60 | return NewDHTClientGetMsg(nil), nil | 62 | return NewDHTClientGetMsg(nil), nil |
61 | case DHT_CLIENT_GET_STOP: | 63 | case DHT_CLIENT_GET_STOP: |
62 | return NewDHTClientGetStopMsg(nil), nil | 64 | return NewDHTClientGetStopMsg(nil), nil |
63 | case DHT_CLIENT_RESULT: | 65 | case DHT_CLIENT_RESULT: |
64 | return NewDHTClientResultMsg(nil), nil | 66 | return NewDHTClientResultMsg(nil), nil |
67 | case DHT_CLIENT_GET_RESULTS_KNOWN: | ||
68 | return NewDHTClientGetResultsKnownMsg(nil), nil | ||
65 | 69 | ||
66 | //------------------------------------------------------------------ | 70 | //------------------------------------------------------------------ |
67 | // GNS | 71 | // GNS |
diff --git a/src/gnunet/message/message.go b/src/gnunet/message/message.go index 1b826d7..60e36c9 100644 --- a/src/gnunet/message/message.go +++ b/src/gnunet/message/message.go | |||
@@ -29,11 +29,19 @@ var ( | |||
29 | ErrMsgHeaderTooSmall = errors.New("Message header too small") | 29 | ErrMsgHeaderTooSmall = errors.New("Message header too small") |
30 | ) | 30 | ) |
31 | 31 | ||
32 | //---------------------------------------------------------------------- | ||
33 | |||
32 | // Message is an interface for all GNUnet-specific messages. | 34 | // Message is an interface for all GNUnet-specific messages. |
33 | type Message interface { | 35 | type Message interface { |
36 | // Header of message | ||
34 | Header() *Header | 37 | Header() *Header |
38 | |||
39 | // String returns a human-readable message | ||
40 | String() string | ||
35 | } | 41 | } |
36 | 42 | ||
43 | //---------------------------------------------------------------------- | ||
44 | |||
37 | // Header encapsulates the common part of all GNUnet messages (at the | 45 | // Header encapsulates the common part of all GNUnet messages (at the |
38 | // beginning of the data). | 46 | // beginning of the data). |
39 | type Header struct { | 47 | type Header struct { |
diff --git a/src/gnunet/message/msg_dht.go b/src/gnunet/message/msg_dht.go index 9b73557..bb8fc1a 100644 --- a/src/gnunet/message/msg_dht.go +++ b/src/gnunet/message/msg_dht.go | |||
@@ -28,6 +28,54 @@ import ( | |||
28 | ) | 28 | ) |
29 | 29 | ||
30 | //---------------------------------------------------------------------- | 30 | //---------------------------------------------------------------------- |
31 | // DHT_CLIENT_PUT | ||
32 | //---------------------------------------------------------------------- | ||
33 | |||
34 | // DHTClientPutMsg is the message for putting values into the DHT | ||
35 | type DHTClientPutMsg struct { | ||
36 | MsgSize uint16 `order:"big"` // total size of message | ||
37 | MsgType uint16 `order:"big"` // DHT_CLIENT_PUT (142) | ||
38 | Type uint32 `order:"big"` // The type of the data (BLOCK_TYPE_???) | ||
39 | Options uint32 `order:"big"` // Message options (DHT_RO_???) | ||
40 | ReplLevel uint32 `order:"big"` // Replication level for this message | ||
41 | Expire util.AbsoluteTime // Expiration time | ||
42 | Key *crypto.HashCode // The key to be used | ||
43 | Data []byte `size:"*"` // Block data | ||
44 | } | ||
45 | |||
46 | // NewDHTClientPutMsg creates a new default DHTClientPutMsg object. | ||
47 | func NewDHTClientPutMsg(key *crypto.HashCode, btype int, data []byte) *DHTClientPutMsg { | ||
48 | if key == nil { | ||
49 | key = new(crypto.HashCode) | ||
50 | } | ||
51 | var size uint16 = 88 | ||
52 | if data != nil { | ||
53 | size += uint16(len(data)) | ||
54 | } | ||
55 | return &DHTClientPutMsg{ | ||
56 | MsgSize: size, | ||
57 | MsgType: DHT_CLIENT_PUT, | ||
58 | Type: uint32(btype), | ||
59 | Options: uint32(enums.DHT_RO_NONE), | ||
60 | ReplLevel: 1, | ||
61 | Expire: util.AbsoluteTimeNever(), | ||
62 | Key: key, | ||
63 | Data: data, | ||
64 | } | ||
65 | } | ||
66 | |||
67 | // String returns a human-readable representation of the message. | ||
68 | func (m *DHTClientPutMsg) String() string { | ||
69 | return fmt.Sprintf("DHTClientPutMsg{Type=%d,Expire=%s,Options=%d,Repl=%d,Key=%s}", | ||
70 | m.Type, m.Expire, m.Options, m.ReplLevel, hex.EncodeToString(m.Key.Bits)) | ||
71 | } | ||
72 | |||
73 | // Header returns the message header in a separate instance. | ||
74 | func (m *DHTClientPutMsg) Header() *Header { | ||
75 | return &Header{m.MsgSize, m.MsgType} | ||
76 | } | ||
77 | |||
78 | //---------------------------------------------------------------------- | ||
31 | // DHT_CLIENT_GET | 79 | // DHT_CLIENT_GET |
32 | //---------------------------------------------------------------------- | 80 | //---------------------------------------------------------------------- |
33 | 81 | ||
@@ -102,7 +150,7 @@ type DHTClientResultMsg struct { | |||
102 | // NewDHTClientResultMsg creates a new default DHTClientResultMsg object. | 150 | // NewDHTClientResultMsg creates a new default DHTClientResultMsg object. |
103 | func NewDHTClientResultMsg(key *crypto.HashCode) *DHTClientResultMsg { | 151 | func NewDHTClientResultMsg(key *crypto.HashCode) *DHTClientResultMsg { |
104 | if key == nil { | 152 | if key == nil { |
105 | key = crypto.NewHashCode() | 153 | key = crypto.NewHashCode(nil) |
106 | } | 154 | } |
107 | return &DHTClientResultMsg{ | 155 | return &DHTClientResultMsg{ |
108 | MsgSize: 64, // empty message size (no data) | 156 | MsgSize: 64, // empty message size (no data) |
@@ -163,3 +211,48 @@ func (m *DHTClientGetStopMsg) String() string { | |||
163 | func (m *DHTClientGetStopMsg) Header() *Header { | 211 | func (m *DHTClientGetStopMsg) Header() *Header { |
164 | return &Header{m.MsgSize, m.MsgType} | 212 | return &Header{m.MsgSize, m.MsgType} |
165 | } | 213 | } |
214 | |||
215 | //---------------------------------------------------------------------- | ||
216 | // DHT_CLIENT_GET_RESULTS_KNOWN | ||
217 | //---------------------------------------------------------------------- | ||
218 | |||
219 | // DHTClientGetResultsKnownMsg is the message for putting values into the DHT | ||
220 | type DHTClientGetResultsKnownMsg struct { | ||
221 | MsgSize uint16 `order:"big"` // total size of message | ||
222 | MsgType uint16 `order:"big"` // DHT_CLIENT_GET_RESULTS_KNOWN (156) | ||
223 | Reserved uint32 `order:"big"` // Reserved for further use | ||
224 | Key *crypto.HashCode // The key to search for | ||
225 | ID uint64 `order:"big"` // Unique ID identifying this request | ||
226 | Known []*crypto.HashCode `size:"*"` // list of known results | ||
227 | } | ||
228 | |||
229 | // NewDHTClientPutMsg creates a new default DHTClientPutMsg object. | ||
230 | func NewDHTClientGetResultsKnownMsg(key *crypto.HashCode) *DHTClientGetResultsKnownMsg { | ||
231 | if key == nil { | ||
232 | key = new(crypto.HashCode) | ||
233 | } | ||
234 | return &DHTClientGetResultsKnownMsg{ | ||
235 | MsgSize: 80, | ||
236 | MsgType: DHT_CLIENT_GET_RESULTS_KNOWN, | ||
237 | Key: key, | ||
238 | ID: 0, | ||
239 | Known: make([]*crypto.HashCode, 0), | ||
240 | } | ||
241 | } | ||
242 | |||
243 | // AddKnown adds a known result to the list | ||
244 | func (m *DHTClientGetResultsKnownMsg) AddKnown(hc *crypto.HashCode) { | ||
245 | m.Known = append(m.Known, hc) | ||
246 | m.MsgSize += 64 | ||
247 | } | ||
248 | |||
249 | // String returns a human-readable representation of the message. | ||
250 | func (m *DHTClientGetResultsKnownMsg) String() string { | ||
251 | return fmt.Sprintf("DHTClientGetResultsKnownMsg{Id:%d,Key=%s,Num=%d}", | ||
252 | m.ID, hex.EncodeToString(m.Key.Bits), len(m.Known)) | ||
253 | } | ||
254 | |||
255 | // Header returns the message header in a separate instance. | ||
256 | func (m *DHTClientGetResultsKnownMsg) Header() *Header { | ||
257 | return &Header{m.MsgSize, m.MsgType} | ||
258 | } | ||
diff --git a/src/gnunet/message/msg_gns.go b/src/gnunet/message/msg_gns.go index 35630d6..9b85e40 100644 --- a/src/gnunet/message/msg_gns.go +++ b/src/gnunet/message/msg_gns.go | |||
@@ -25,15 +25,9 @@ import ( | |||
25 | "gnunet/enums" | 25 | "gnunet/enums" |
26 | "gnunet/util" | 26 | "gnunet/util" |
27 | 27 | ||
28 | "github.com/bfix/gospel/data" | ||
29 | "github.com/bfix/gospel/logger" | 28 | "github.com/bfix/gospel/logger" |
30 | ) | 29 | ) |
31 | 30 | ||
32 | // Error messages | ||
33 | var ( | ||
34 | ErrBlockNotDecrypted = fmt.Errorf("GNS block not decrypted") | ||
35 | ) | ||
36 | |||
37 | //---------------------------------------------------------------------- | 31 | //---------------------------------------------------------------------- |
38 | // GNS_LOOKUP | 32 | // GNS_LOOKUP |
39 | //---------------------------------------------------------------------- | 33 | //---------------------------------------------------------------------- |
@@ -147,94 +141,6 @@ func (rs *RecordSet) Expires() util.AbsoluteTime { | |||
147 | return expires | 141 | return expires |
148 | } | 142 | } |
149 | 143 | ||
150 | // SignedBlockData represents the signed and encrypted list of resource | ||
151 | // records stored in a GNSRecordSet | ||
152 | type SignedBlockData struct { | ||
153 | Purpose *crypto.SignaturePurpose `` // Size and purpose of signature (8 bytes) | ||
154 | Expire util.AbsoluteTime `` // Expiration time of the block. | ||
155 | EncData []byte `size:"*"` // encrypted GNSRecordSet | ||
156 | |||
157 | // transient data (not serialized) | ||
158 | data []byte // decrypted GNSRecord set | ||
159 | } | ||
160 | |||
161 | // Block is the result of GNS lookups for a given label in a zone. | ||
162 | // An encrypted and signed container for GNS resource records that represents | ||
163 | // the "atomic" data structure associated with a GNS label in a given zone. | ||
164 | type Block struct { | ||
165 | DerivedKeySig *crypto.ZoneSignature // Derived key used for signing | ||
166 | Block *SignedBlockData | ||
167 | |||
168 | // transient data (not serialized) | ||
169 | checked bool // block integrity checked | ||
170 | verified bool // block signature verified (internal) | ||
171 | decrypted bool // block data decrypted (internal) | ||
172 | } | ||
173 | |||
174 | // String returns the human-readable representation of a GNSBlock | ||
175 | func (b *Block) String() string { | ||
176 | return fmt.Sprintf("GNSBlock{Verified=%v,Decrypted=%v,data=[%d]}", | ||
177 | b.verified, b.decrypted, len(b.Block.EncData)) | ||
178 | } | ||
179 | |||
180 | // Records returns the list of resource records in a block. | ||
181 | func (b *Block) Records() ([]*ResourceRecord, error) { | ||
182 | // check if block is decrypted | ||
183 | if !b.decrypted { | ||
184 | return nil, ErrBlockNotDecrypted | ||
185 | } | ||
186 | // parse block data into record set | ||
187 | rs := NewRecordSet() | ||
188 | if err := data.Unmarshal(rs, b.Block.data); err != nil { | ||
189 | return nil, err | ||
190 | } | ||
191 | return rs.Records, nil | ||
192 | } | ||
193 | |||
194 | // Verify the integrity of the block data from a signature. | ||
195 | func (b *Block) Verify(zkey *crypto.ZoneKey, label string) (err error) { | ||
196 | // Integrity check performed | ||
197 | b.checked = true | ||
198 | |||
199 | // verify derived key | ||
200 | dkey := b.DerivedKeySig.ZoneKey | ||
201 | dkey2, _ := zkey.Derive(label, "gns") | ||
202 | if !dkey.Equal(dkey2) { | ||
203 | return fmt.Errorf("invalid signature key for GNS Block") | ||
204 | } | ||
205 | // verify signature | ||
206 | var buf []byte | ||
207 | if buf, err = data.Marshal(b.Block); err != nil { | ||
208 | return | ||
209 | } | ||
210 | b.verified, err = b.DerivedKeySig.Verify(buf) | ||
211 | return | ||
212 | } | ||
213 | |||
214 | // Decrypt block data with a key derived from zone key and label. | ||
215 | func (b *Block) Decrypt(zkey *crypto.ZoneKey, label string) (err error) { | ||
216 | // decrypt payload | ||
217 | b.Block.data, err = zkey.Decrypt(b.Block.EncData, label, b.Block.Expire) | ||
218 | b.decrypted = true | ||
219 | return | ||
220 | } | ||
221 | |||
222 | // NewBlock instantiates an empty GNS block | ||
223 | func NewBlock() *Block { | ||
224 | return &Block{ | ||
225 | DerivedKeySig: nil, | ||
226 | Block: &SignedBlockData{ | ||
227 | Purpose: new(crypto.SignaturePurpose), | ||
228 | Expire: *new(util.AbsoluteTime), | ||
229 | EncData: nil, | ||
230 | data: nil, | ||
231 | }, | ||
232 | checked: false, | ||
233 | verified: false, | ||
234 | decrypted: false, | ||
235 | } | ||
236 | } | ||
237 | |||
238 | // ResourceRecord is the GNUnet-specific representation of resource | 144 | // ResourceRecord is the GNUnet-specific representation of resource |
239 | // records (not to be confused with DNS resource records). | 145 | // records (not to be confused with DNS resource records). |
240 | type ResourceRecord struct { | 146 | type ResourceRecord struct { |
diff --git a/src/gnunet/message/msg_hello.go b/src/gnunet/message/msg_hello.go new file mode 100644 index 0000000..18fe9d5 --- /dev/null +++ b/src/gnunet/message/msg_hello.go | |||
@@ -0,0 +1,103 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package message | ||
20 | |||
21 | import ( | ||
22 | "fmt" | ||
23 | "gnunet/util" | ||
24 | ) | ||
25 | |||
26 | //---------------------------------------------------------------------- | ||
27 | // HELLO | ||
28 | // | ||
29 | // A HELLO message is used to exchange information about transports with | ||
30 | // other peers. This struct is always followed by the actual network | ||
31 | // addresses which have the format: | ||
32 | // | ||
33 | // 1) transport-name (0-terminated) | ||
34 | // 2) address-length (uint16_t, network byte order) | ||
35 | // 3) address expiration | ||
36 | // 4) address (address-length bytes) | ||
37 | //---------------------------------------------------------------------- | ||
38 | |||
39 | // HelloAddress represents a (generic) peer address with expiration date | ||
40 | type HelloAddress struct { | ||
41 | Transport string // Name of transport | ||
42 | AddrSize uint16 `order:"big"` // Size of address entry | ||
43 | ExpireOn util.AbsoluteTime // Expiry date | ||
44 | Address []byte `size:"AddrSize"` // Address specification | ||
45 | } | ||
46 | |||
47 | // NewHelloAddress create a new HELLO address from the given address | ||
48 | func NewHelloAddress(a *util.Address) *HelloAddress { | ||
49 | addr := &HelloAddress{ | ||
50 | Transport: a.Netw, | ||
51 | AddrSize: uint16(len(a.Address)), | ||
52 | ExpireOn: a.Expires, | ||
53 | Address: make([]byte, len(a.Address)), | ||
54 | } | ||
55 | copy(addr.Address, a.Address) | ||
56 | return addr | ||
57 | } | ||
58 | |||
59 | // String returns a human-readable representation of the message. | ||
60 | func (a *HelloAddress) String() string { | ||
61 | return fmt.Sprintf("Address{%s,expire=%s}", | ||
62 | util.AddressString(a.Transport, a.Address), a.ExpireOn) | ||
63 | } | ||
64 | |||
65 | // HelloMsg is a message send by peers to announce their presence | ||
66 | type HelloMsg struct { | ||
67 | MsgSize uint16 `order:"big"` // total size of message | ||
68 | MsgType uint16 `order:"big"` // HELLO (17) | ||
69 | FriendOnly uint32 `order:"big"` // =1: do not gossip this HELLO | ||
70 | PeerID *util.PeerID // EdDSA public key (long-term) | ||
71 | Addresses []*HelloAddress `size:"*"` // List of end-point addressess | ||
72 | } | ||
73 | |||
74 | // NewHelloMsg creates a new HELLO msg for a given peer. | ||
75 | func NewHelloMsg(peerid *util.PeerID) *HelloMsg { | ||
76 | if peerid == nil { | ||
77 | peerid = util.NewPeerID(nil) | ||
78 | } | ||
79 | return &HelloMsg{ | ||
80 | MsgSize: 40, | ||
81 | MsgType: HELLO, | ||
82 | FriendOnly: 0, | ||
83 | PeerID: peerid, | ||
84 | Addresses: make([]*HelloAddress, 0), | ||
85 | } | ||
86 | } | ||
87 | |||
88 | // String returns a human-readable representation of the message. | ||
89 | func (m *HelloMsg) String() string { | ||
90 | return fmt.Sprintf("HelloMsg{peer=%s,friendsonly=%d,addr=%v}", | ||
91 | m.PeerID, m.FriendOnly, m.Addresses) | ||
92 | } | ||
93 | |||
94 | // AddAddress adds a new address to the HELLO message. | ||
95 | func (m *HelloMsg) AddAddress(a *HelloAddress) { | ||
96 | m.Addresses = append(m.Addresses, a) | ||
97 | m.MsgSize += uint16(len(a.Transport)) + a.AddrSize + 11 | ||
98 | } | ||
99 | |||
100 | // Header returns the message header in a separate instance. | ||
101 | func (m *HelloMsg) Header() *Header { | ||
102 | return &Header{m.MsgSize, m.MsgType} | ||
103 | } | ||
diff --git a/src/gnunet/message/msg_namecache.go b/src/gnunet/message/msg_namecache.go index c3a0ac7..5acb807 100644 --- a/src/gnunet/message/msg_namecache.go +++ b/src/gnunet/message/msg_namecache.go | |||
@@ -21,8 +21,8 @@ package message | |||
21 | import ( | 21 | import ( |
22 | "encoding/hex" | 22 | "encoding/hex" |
23 | "fmt" | 23 | "fmt" |
24 | |||
25 | "gnunet/crypto" | 24 | "gnunet/crypto" |
25 | "gnunet/service/dht/blocks" | ||
26 | "gnunet/util" | 26 | "gnunet/util" |
27 | ) | 27 | ) |
28 | 28 | ||
@@ -41,7 +41,7 @@ type NamecacheLookupMsg struct { | |||
41 | // NewNamecacheLookupMsg creates a new default message. | 41 | // NewNamecacheLookupMsg creates a new default message. |
42 | func NewNamecacheLookupMsg(query *crypto.HashCode) *NamecacheLookupMsg { | 42 | func NewNamecacheLookupMsg(query *crypto.HashCode) *NamecacheLookupMsg { |
43 | if query == nil { | 43 | if query == nil { |
44 | query = crypto.NewHashCode() | 44 | query = crypto.NewHashCode(nil) |
45 | } | 45 | } |
46 | return &NamecacheLookupMsg{ | 46 | return &NamecacheLookupMsg{ |
47 | MsgSize: 72, | 47 | MsgSize: 72, |
@@ -114,7 +114,7 @@ type NamecacheCacheMsg struct { | |||
114 | } | 114 | } |
115 | 115 | ||
116 | // NewNamecacheCacheMsg creates a new default message. | 116 | // NewNamecacheCacheMsg creates a new default message. |
117 | func NewNamecacheCacheMsg(block *Block) *NamecacheCacheMsg { | 117 | func NewNamecacheCacheMsg(block *blocks.GNSBlock) *NamecacheCacheMsg { |
118 | msg := &NamecacheCacheMsg{ | 118 | msg := &NamecacheCacheMsg{ |
119 | MsgSize: 108, | 119 | MsgSize: 108, |
120 | MsgType: NAMECACHE_BLOCK_CACHE, | 120 | MsgType: NAMECACHE_BLOCK_CACHE, |
@@ -125,10 +125,10 @@ func NewNamecacheCacheMsg(block *Block) *NamecacheCacheMsg { | |||
125 | } | 125 | } |
126 | if block != nil { | 126 | if block != nil { |
127 | msg.DerivedKeySig = block.DerivedKeySig | 127 | msg.DerivedKeySig = block.DerivedKeySig |
128 | msg.Expire = block.Block.Expire | 128 | msg.Expire = block.Body.Expire |
129 | size := len(block.Block.EncData) | 129 | size := len(block.Body.Data) |
130 | msg.EncData = make([]byte, size) | 130 | msg.EncData = make([]byte, size) |
131 | copy(msg.EncData, block.Block.EncData) | 131 | copy(msg.EncData, block.Body.Data) |
132 | msg.MsgSize += uint16(size) | 132 | msg.MsgSize += uint16(size) |
133 | } | 133 | } |
134 | return msg | 134 | return msg |
diff --git a/src/gnunet/message/msg_transport.go b/src/gnunet/message/msg_transport.go index 6e64c4d..d0e927b 100644 --- a/src/gnunet/message/msg_transport.go +++ b/src/gnunet/message/msg_transport.go | |||
@@ -1,5 +1,5 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | 1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. |
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | 2 | // Copyright (C) 2019-2022 Bernd Fix >Y< |
3 | // | 3 | // |
4 | // gnunet-go is free software: you can redistribute it and/or modify it | 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 | 5 | // under the terms of the GNU Affero General Public License as published |
@@ -225,86 +225,6 @@ func (m *TransportPongMsg) Verify(pub *ed25519.PublicKey) (bool, error) { | |||
225 | } | 225 | } |
226 | 226 | ||
227 | //---------------------------------------------------------------------- | 227 | //---------------------------------------------------------------------- |
228 | // HELLO | ||
229 | // | ||
230 | // A HELLO message is used to exchange information about | ||
231 | // transports with other peers. This struct is always | ||
232 | // followed by the actual network addresses which have | ||
233 | // the format: | ||
234 | // | ||
235 | // 1) transport-name (0-terminated) | ||
236 | // 2) address-length (uint16_t, network byte order) | ||
237 | // 3) address expiration | ||
238 | // 4) address (address-length bytes) | ||
239 | //---------------------------------------------------------------------- | ||
240 | |||
241 | // HelloAddress represents a (generic) peer address with expiration date | ||
242 | type HelloAddress struct { | ||
243 | Transport string // Name of transport | ||
244 | AddrSize uint16 `order:"big"` // Size of address entry | ||
245 | ExpireOn util.AbsoluteTime // Expiry date | ||
246 | Address []byte `size:"AddrSize"` // Address specification | ||
247 | } | ||
248 | |||
249 | // NewHelloAddress create a new HELLO address from the given address | ||
250 | func NewHelloAddress(a *util.Address) *HelloAddress { | ||
251 | addr := &HelloAddress{ | ||
252 | Transport: a.Transport, | ||
253 | AddrSize: uint16(len(a.Address)), | ||
254 | ExpireOn: util.AbsoluteTimeNow().Add(12 * time.Hour), | ||
255 | Address: make([]byte, len(a.Address)), | ||
256 | } | ||
257 | copy(addr.Address, a.Address) | ||
258 | return addr | ||
259 | } | ||
260 | |||
261 | // String returns a human-readable representation of the message. | ||
262 | func (a *HelloAddress) String() string { | ||
263 | return fmt.Sprintf("Address{%s,expire=%s}", | ||
264 | util.AddressString(a.Transport, a.Address), a.ExpireOn) | ||
265 | } | ||
266 | |||
267 | // HelloMsg is a message send by peers to announce their presence | ||
268 | type HelloMsg struct { | ||
269 | MsgSize uint16 `order:"big"` // total size of message | ||
270 | MsgType uint16 `order:"big"` // HELLO (17) | ||
271 | FriendOnly uint32 `order:"big"` // =1: do not gossip this HELLO | ||
272 | PeerID *util.PeerID // EdDSA public key (long-term) | ||
273 | Addresses []*HelloAddress `size:"*"` // List of end-point addressess | ||
274 | } | ||
275 | |||
276 | // NewHelloMsg creates a new HELLO msg for a given peer. | ||
277 | func NewHelloMsg(peerid *util.PeerID) *HelloMsg { | ||
278 | if peerid == nil { | ||
279 | peerid = util.NewPeerID(nil) | ||
280 | } | ||
281 | return &HelloMsg{ | ||
282 | MsgSize: 40, | ||
283 | MsgType: HELLO, | ||
284 | FriendOnly: 0, | ||
285 | PeerID: peerid, | ||
286 | Addresses: make([]*HelloAddress, 0), | ||
287 | } | ||
288 | } | ||
289 | |||
290 | // String returns a human-readable representation of the message. | ||
291 | func (m *HelloMsg) String() string { | ||
292 | return fmt.Sprintf("HelloMsg{peer=%s,friendsonly=%d,addr=%v}", | ||
293 | m.PeerID, m.FriendOnly, m.Addresses) | ||
294 | } | ||
295 | |||
296 | // AddAddress adds a new address to the HELLO message. | ||
297 | func (m *HelloMsg) AddAddress(a *HelloAddress) { | ||
298 | m.Addresses = append(m.Addresses, a) | ||
299 | m.MsgSize += uint16(len(a.Transport)) + a.AddrSize + 11 | ||
300 | } | ||
301 | |||
302 | // Header returns the message header in a separate instance. | ||
303 | func (m *HelloMsg) Header() *Header { | ||
304 | return &Header{m.MsgSize, m.MsgType} | ||
305 | } | ||
306 | |||
307 | //---------------------------------------------------------------------- | ||
308 | // TRANSPORT_SESSION_ACK | 228 | // TRANSPORT_SESSION_ACK |
309 | //---------------------------------------------------------------------- | 229 | //---------------------------------------------------------------------- |
310 | 230 | ||
diff --git a/src/gnunet/modules.go b/src/gnunet/modules.go index f4fdb1b..e47699f 100644 --- a/src/gnunet/modules.go +++ b/src/gnunet/modules.go | |||
@@ -52,29 +52,33 @@ func (inst Instances) Register() { | |||
52 | rpc.Register(inst.Revocation) | 52 | rpc.Register(inst.Revocation) |
53 | } | 53 | } |
54 | 54 | ||
55 | // Local reference to instance list | 55 | // Local reference to instance list. The list is initialized |
56 | // by core. | ||
56 | var ( | 57 | var ( |
57 | Modules Instances | 58 | Modules Instances |
58 | ) | 59 | ) |
59 | 60 | ||
61 | /* TODO: implement | ||
60 | // Initialize instance list and link module functions as required. | 62 | // Initialize instance list and link module functions as required. |
61 | func init() { | 63 | // This function is called by core on start-up. |
64 | func Init(ctx context.Context) { | ||
62 | 65 | ||
63 | // Namecache (no calls to other modules) | 66 | // Namecache (no calls to other modules) |
64 | Modules.Namecache = new(namecache.NamecacheModule) | 67 | Modules.Namecache = namecache.NewModule(ctx, c) |
65 | 68 | ||
66 | // DHT (no calls to other modules) | 69 | // DHT (no calls to other modules) |
67 | Modules.DHT = new(dht.Module) | 70 | Modules.DHT = dht.NewModule(ctx, c) |
68 | 71 | ||
69 | // Revocation (no calls to other modules) | 72 | // Revocation (no calls to other modules) |
70 | Modules.Revocation = revocation.NewModule() | 73 | Modules.Revocation = revocation.NewModule(ctx, c) |
71 | 74 | ||
72 | // GNS (calls Namecache, DHT and Identity) | 75 | // GNS (calls Namecache, DHT and Identity) |
73 | Modules.GNS = &gns.Module{ | 76 | gns := gns.NewModule(ctx, c) |
74 | LookupLocal: Modules.Namecache.Get, | 77 | Modules.GNS = gns |
75 | StoreLocal: Modules.Namecache.Put, | 78 | gns.LookupLocal = Modules.Namecache.Get |
76 | LookupRemote: Modules.DHT.Get, | 79 | gns.StoreLocal = Modules.Namecache.Put |
77 | RevocationQuery: Modules.Revocation.Query, | 80 | gns.LookupRemote = Modules.DHT.Get |
78 | RevocationRevoke: Modules.Revocation.Revoke, | 81 | gns.RevocationQuery = Modules.Revocation.Query |
79 | } | 82 | gns.RevocationRevoke = Modules.Revocation.Revoke |
80 | } | 83 | } |
84 | */ | ||
diff --git a/src/gnunet/service/client.go b/src/gnunet/service/client.go index a196c09..19dc4c4 100644 --- a/src/gnunet/service/client.go +++ b/src/gnunet/service/client.go | |||
@@ -19,39 +19,39 @@ | |||
19 | package service | 19 | package service |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | ||
22 | "gnunet/message" | 23 | "gnunet/message" |
23 | "gnunet/transport" | ||
24 | 24 | ||
25 | "github.com/bfix/gospel/logger" | 25 | "github.com/bfix/gospel/logger" |
26 | ) | 26 | ) |
27 | 27 | ||
28 | // Client type: Use to perform client-side interactions with GNUnet services. | 28 | // Client type: Use to perform client-side interactions with GNUnet services. |
29 | type Client struct { | 29 | type Client struct { |
30 | ch *transport.MsgChannel // channel for message exchange | 30 | ch *Connection // channel for message exchange |
31 | } | 31 | } |
32 | 32 | ||
33 | // NewClient creates a new client instance for the given channel endpoint. | 33 | // NewClient connects to a socket with given path |
34 | func NewClient(endp string) (*Client, error) { | 34 | func NewClient(ctx context.Context, path string) (*Client, error) { |
35 | // create a new channel to endpoint. | 35 | // create a connection |
36 | ch, err := transport.NewChannel(endp) | 36 | ch, err := NewConnection(ctx, path) |
37 | if err != nil { | 37 | if err != nil { |
38 | return nil, err | 38 | return nil, err |
39 | } | 39 | } |
40 | // wrap into a message channel for the client. | 40 | // wrap into a message channel for the client. |
41 | return &Client{ | 41 | return &Client{ |
42 | ch: transport.NewMsgChannel(ch), | 42 | ch: ch, |
43 | }, nil | 43 | }, nil |
44 | } | 44 | } |
45 | 45 | ||
46 | // SendRequest sends a give message to the service. | 46 | // SendRequest sends a message to the service. |
47 | func (c *Client) SendRequest(ctx *SessionContext, req message.Message) error { | 47 | func (c *Client) SendRequest(ctx context.Context, req message.Message) error { |
48 | return c.ch.Send(req, ctx.Signaller()) | 48 | return c.ch.Send(ctx, req) |
49 | } | 49 | } |
50 | 50 | ||
51 | // ReceiveResponse waits for a response from the service; it can be interrupted | 51 | // ReceiveResponse waits for a response from the service; it can be interrupted |
52 | // by sending "false" to the cmd channel. | 52 | // by sending "false" to the cmd channel. |
53 | func (c *Client) ReceiveResponse(ctx *SessionContext) (message.Message, error) { | 53 | func (c *Client) ReceiveResponse(ctx context.Context) (message.Message, error) { |
54 | return c.ch.Receive(ctx.Signaller()) | 54 | return c.ch.Receive(ctx) |
55 | } | 55 | } |
56 | 56 | ||
57 | // Close a client; no further message exchange is possible. | 57 | // Close a client; no further message exchange is possible. |
@@ -62,15 +62,15 @@ func (c *Client) Close() error { | |||
62 | // RequestResponse is a helper method for a one request - one response | 62 | // RequestResponse is a helper method for a one request - one response |
63 | // secenarios of client/serice interactions. | 63 | // secenarios of client/serice interactions. |
64 | func RequestResponse( | 64 | func RequestResponse( |
65 | ctx *SessionContext, | 65 | ctx context.Context, |
66 | caller string, | 66 | caller string, |
67 | callee string, | 67 | callee string, |
68 | endp string, | 68 | path string, |
69 | req message.Message) (message.Message, error) { | 69 | req message.Message) (message.Message, error) { |
70 | 70 | ||
71 | // client-connect to the service | 71 | // client-connect to the service |
72 | logger.Printf(logger.DBG, "[%s] Connecting to %s service...\n", caller, callee) | 72 | logger.Printf(logger.DBG, "[%s] Connecting to %s service...\n", caller, callee) |
73 | cl, err := NewClient(endp) | 73 | cl, err := NewClient(ctx, path) |
74 | if err != nil { | 74 | if err != nil { |
75 | return nil, err | 75 | return nil, err |
76 | } | 76 | } |
diff --git a/src/gnunet/service/connection.go b/src/gnunet/service/connection.go new file mode 100644 index 0000000..1c690c5 --- /dev/null +++ b/src/gnunet/service/connection.go | |||
@@ -0,0 +1,280 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package service | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "errors" | ||
24 | "fmt" | ||
25 | "gnunet/message" | ||
26 | "net" | ||
27 | "os" | ||
28 | "strconv" | ||
29 | |||
30 | "github.com/bfix/gospel/data" | ||
31 | "github.com/bfix/gospel/logger" | ||
32 | ) | ||
33 | |||
34 | // Error codes | ||
35 | var ( | ||
36 | ErrConnectionNotOpened = errors.New("channel not opened") | ||
37 | ErrConnectionInterrupted = errors.New("channel interrupted") | ||
38 | ) | ||
39 | |||
40 | //====================================================================== | ||
41 | |||
42 | // Connection is a channel for GNUnet message exchange (send/receive) | ||
43 | // based on Unix domain sockets. It is used locally by services and | ||
44 | // clients in the standard GNUnet environment. | ||
45 | type Connection struct { | ||
46 | path string // file name of Unix socket | ||
47 | conn net.Conn // associated connection | ||
48 | buf []byte // read/write buffer | ||
49 | } | ||
50 | |||
51 | // NewConnection creates a new connection to a socket with given path. | ||
52 | // This is used by clients to connect to a service. | ||
53 | func NewConnection(ctx context.Context, path string) (s *Connection, err error) { | ||
54 | var d net.Dialer | ||
55 | s = new(Connection) | ||
56 | s.path = path | ||
57 | s.buf = make([]byte, 65536) | ||
58 | s.conn, err = d.DialContext(ctx, "unix", path) | ||
59 | return | ||
60 | } | ||
61 | |||
62 | // Close a socket connection | ||
63 | func (s *Connection) Close() error { | ||
64 | if s.conn != nil { | ||
65 | rc := s.conn.Close() | ||
66 | s.conn = nil | ||
67 | return rc | ||
68 | } | ||
69 | return ErrConnectionNotOpened | ||
70 | } | ||
71 | |||
72 | // Send a GNUnet message over a socket. | ||
73 | func (s *Connection) Send(ctx context.Context, msg message.Message) error { | ||
74 | // convert message to binary data | ||
75 | data, err := data.Marshal(msg) | ||
76 | if err != nil { | ||
77 | return err | ||
78 | } | ||
79 | // check message header size and packet size | ||
80 | mh, err := message.GetMsgHeader(data) | ||
81 | if err != nil { | ||
82 | return err | ||
83 | } | ||
84 | if len(data) != int(mh.MsgSize) { | ||
85 | return errors.New("send: message size mismatch") | ||
86 | } | ||
87 | |||
88 | // send packet | ||
89 | n, err := s.write(ctx, data) | ||
90 | if err != nil { | ||
91 | return err | ||
92 | } | ||
93 | if n != len(data) { | ||
94 | return errors.New("incomplete send") | ||
95 | } | ||
96 | return nil | ||
97 | } | ||
98 | |||
99 | // Receive GNUnet messages from socket. | ||
100 | func (s *Connection) Receive(ctx context.Context) (message.Message, error) { | ||
101 | // get bytes from socket | ||
102 | get := func(pos, count int) error { | ||
103 | n, err := s.read(ctx, s.buf[pos:pos+count]) | ||
104 | if err != nil { | ||
105 | return err | ||
106 | } | ||
107 | if n != count { | ||
108 | return errors.New("not enough bytes on network") | ||
109 | } | ||
110 | return nil | ||
111 | } | ||
112 | // read header first | ||
113 | if err := get(0, 4); err != nil { | ||
114 | return nil, err | ||
115 | } | ||
116 | mh, err := message.GetMsgHeader(s.buf[:4]) | ||
117 | if err != nil { | ||
118 | return nil, err | ||
119 | } | ||
120 | // get rest of message | ||
121 | if err := get(4, int(mh.MsgSize)-4); err != nil { | ||
122 | return nil, err | ||
123 | } | ||
124 | msg, err := message.NewEmptyMessage(mh.MsgType) | ||
125 | if err != nil { | ||
126 | return nil, err | ||
127 | } | ||
128 | if msg == nil { | ||
129 | return nil, fmt.Errorf("message{%d} is nil", mh.MsgType) | ||
130 | } | ||
131 | if err = data.Unmarshal(msg, s.buf[:mh.MsgSize]); err != nil { | ||
132 | return nil, err | ||
133 | } | ||
134 | return msg, nil | ||
135 | } | ||
136 | |||
137 | //---------------------------------------------------------------------- | ||
138 | // internal methods | ||
139 | //---------------------------------------------------------------------- | ||
140 | |||
141 | // result of read/write operations on sockets. | ||
142 | type result struct { | ||
143 | n int // number of bytes read/written | ||
144 | err error // error (or nil) | ||
145 | } | ||
146 | |||
147 | // Read bytes from a socket into buffer: Returns the number of read | ||
148 | // bytes and an error code. Only works on open channels ;) | ||
149 | func (s *Connection) read(ctx context.Context, buf []byte) (int, error) { | ||
150 | // check if the channel is open | ||
151 | if s.conn == nil { | ||
152 | return 0, ErrConnectionNotOpened | ||
153 | } | ||
154 | // perform read operation | ||
155 | ch := make(chan *result) | ||
156 | go func() { | ||
157 | n, err := s.conn.Read(buf) | ||
158 | ch <- &result{n, err} | ||
159 | }() | ||
160 | for { | ||
161 | select { | ||
162 | // terminate on request | ||
163 | case <-ctx.Done(): | ||
164 | return 0, ErrConnectionInterrupted | ||
165 | |||
166 | // handle result of read operation | ||
167 | case res := <-ch: | ||
168 | return res.n, res.err | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | |||
173 | // Write buffer to socket and returns the number of bytes written and an | ||
174 | // optional error code. | ||
175 | func (s *Connection) write(ctx context.Context, buf []byte) (int, error) { | ||
176 | // check if we have an open socket to write to. | ||
177 | if s.conn == nil { | ||
178 | return 0, ErrConnectionNotOpened | ||
179 | } | ||
180 | // perform write operation | ||
181 | ch := make(chan *result) | ||
182 | go func() { | ||
183 | n, err := s.conn.Write(buf) | ||
184 | ch <- &result{n, err} | ||
185 | }() | ||
186 | for { | ||
187 | select { | ||
188 | // handle terminate command | ||
189 | case <-ctx.Done(): | ||
190 | return 0, ErrConnectionInterrupted | ||
191 | |||
192 | // handle result of write operation | ||
193 | case res := <-ch: | ||
194 | return res.n, res.err | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | |||
199 | //====================================================================== | ||
200 | |||
201 | // ConnectionManager to handle client connections on a socket. | ||
202 | type ConnectionManager struct { | ||
203 | listener net.Listener // reference to listener object | ||
204 | running bool // server running? | ||
205 | } | ||
206 | |||
207 | // NewConnectionManager creates a new socket connection manager. Incoming | ||
208 | // connections from clients are dispatched to a handler channel. | ||
209 | func NewConnectionManager( | ||
210 | ctx context.Context, // execution context | ||
211 | path string, // socket file name | ||
212 | params map[string]string, // connection parameters | ||
213 | hdlr chan *Connection, // handler for incoming connections | ||
214 | ) (cs *ConnectionManager, err error) { | ||
215 | |||
216 | // instantiate channel server | ||
217 | cs = &ConnectionManager{ | ||
218 | listener: nil, | ||
219 | running: false, | ||
220 | } | ||
221 | // create listener | ||
222 | var lc net.ListenConfig | ||
223 | if cs.listener, err = lc.Listen(ctx, "unix", path); err != nil { | ||
224 | return | ||
225 | } | ||
226 | // handle additional parameters | ||
227 | if params != nil { | ||
228 | for key, value := range params { | ||
229 | switch key { | ||
230 | case "perm": // set permissions on 'unix' | ||
231 | if perm, err := strconv.ParseInt(value, 8, 32); 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( | ||
241 | logger.ERROR, | ||
242 | "MsgChannelServer: Invalid permissions '%s'\n", | ||
243 | value) | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | // run go routine to handle channel requests from clients | ||
249 | cs.running = true | ||
250 | go func() { | ||
251 | for cs.running { | ||
252 | conn, err := cs.listener.Accept() | ||
253 | if err != nil { | ||
254 | break | ||
255 | } | ||
256 | // handle connection | ||
257 | c := &Connection{ | ||
258 | conn: conn, | ||
259 | path: path, | ||
260 | buf: make([]byte, 65536), | ||
261 | } | ||
262 | hdlr <- c | ||
263 | } | ||
264 | if cs.listener != nil { | ||
265 | cs.listener.Close() | ||
266 | } | ||
267 | }() | ||
268 | return cs, nil | ||
269 | } | ||
270 | |||
271 | // Close a network channel server (= stop the server) | ||
272 | func (s *ConnectionManager) Close() error { | ||
273 | s.running = false | ||
274 | if s.listener != nil { | ||
275 | err := s.listener.Close() | ||
276 | s.listener = nil | ||
277 | return err | ||
278 | } | ||
279 | return nil | ||
280 | } | ||
diff --git a/src/gnunet/service/context.go b/src/gnunet/service/context.go deleted file mode 100644 index 4ae786d..0000000 --- a/src/gnunet/service/context.go +++ /dev/null | |||
@@ -1,87 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package service | ||
20 | |||
21 | import ( | ||
22 | "sync" | ||
23 | |||
24 | "gnunet/util" | ||
25 | |||
26 | "github.com/bfix/gospel/concurrent" | ||
27 | ) | ||
28 | |||
29 | // SessionContext is used to set a context for each client connection handled | ||
30 | // by a service; the session is handled by the 'ServeClient' method of a | ||
31 | // service implementation. | ||
32 | type SessionContext struct { | ||
33 | ID int // session identifier | ||
34 | wg *sync.WaitGroup // wait group for the session | ||
35 | sig *concurrent.Signaller // signaller for the session | ||
36 | pending int // number of pending go-routines | ||
37 | active bool // is the context active (un-cancelled)? | ||
38 | onCancel *sync.Mutex // only run one Cancel() at a time | ||
39 | } | ||
40 | |||
41 | // NewSessionContext instantiates a new session context. | ||
42 | func NewSessionContext() *SessionContext { | ||
43 | return &SessionContext{ | ||
44 | ID: util.NextID(), | ||
45 | wg: new(sync.WaitGroup), | ||
46 | sig: concurrent.NewSignaller(), | ||
47 | pending: 0, | ||
48 | active: true, | ||
49 | onCancel: new(sync.Mutex), | ||
50 | } | ||
51 | } | ||
52 | |||
53 | // Cancel all go-routines associated with this context. | ||
54 | func (ctx *SessionContext) Cancel() { | ||
55 | ctx.onCancel.Lock() | ||
56 | if ctx.active { | ||
57 | // we are going out-of-business | ||
58 | ctx.active = false | ||
59 | // send signal to terminate... | ||
60 | ctx.sig.Send(true) | ||
61 | // wait for session go-routines to finish | ||
62 | ctx.wg.Wait() | ||
63 | } | ||
64 | ctx.onCancel.Unlock() | ||
65 | } | ||
66 | |||
67 | // Add a go-routine to the wait group. | ||
68 | func (ctx *SessionContext) Add() { | ||
69 | ctx.wg.Add(1) | ||
70 | ctx.pending++ | ||
71 | } | ||
72 | |||
73 | // Remove a go-routine from the wait group. | ||
74 | func (ctx *SessionContext) Remove() { | ||
75 | ctx.wg.Done() | ||
76 | ctx.pending-- | ||
77 | } | ||
78 | |||
79 | // Waiting returns the number of waiting go-routines. | ||
80 | func (ctx *SessionContext) Waiting() int { | ||
81 | return ctx.pending | ||
82 | } | ||
83 | |||
84 | // Signaller returns the working instance for the context. | ||
85 | func (ctx *SessionContext) Signaller() *concurrent.Signaller { | ||
86 | return ctx.sig | ||
87 | } | ||
diff --git a/src/gnunet/service/dht/blocks/generic.go b/src/gnunet/service/dht/blocks/generic.go new file mode 100644 index 0000000..6301e3b --- /dev/null +++ b/src/gnunet/service/dht/blocks/generic.go | |||
@@ -0,0 +1,196 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "encoding/gob" | ||
24 | "encoding/hex" | ||
25 | "fmt" | ||
26 | "gnunet/crypto" | ||
27 | "gnunet/util" | ||
28 | |||
29 | "github.com/bfix/gospel/data" | ||
30 | ) | ||
31 | |||
32 | //---------------------------------------------------------------------- | ||
33 | // Query/Block interfaces for generic DHT handling | ||
34 | //---------------------------------------------------------------------- | ||
35 | |||
36 | // DHT Query interface | ||
37 | type Query interface { | ||
38 | |||
39 | // Key returns the DHT key for a block | ||
40 | Key() *crypto.HashCode | ||
41 | |||
42 | // Get retrieves the value of a named query parameter. The value is | ||
43 | // unchanged if the key is not in the map or if the value in the map | ||
44 | // has an incompatible type. | ||
45 | Get(key string, value any) bool | ||
46 | |||
47 | // Set stores the value of a named query parameter | ||
48 | Set(key string, value any) | ||
49 | |||
50 | // Verify the integrity of a retrieved block (optional). Override in | ||
51 | // custom query types to implement block-specific integrity checks | ||
52 | // (see GNSQuery for example). | ||
53 | Verify(blk Block) error | ||
54 | |||
55 | // Decrypt block content (optional). Override in custom query types to | ||
56 | // implement block-specific encryption (see GNSQuery for example). | ||
57 | Decrypt(blk Block) error | ||
58 | |||
59 | // String returns the human-readable representation of a query | ||
60 | String() string | ||
61 | } | ||
62 | |||
63 | // DHT Block interface | ||
64 | type Block interface { | ||
65 | |||
66 | // Data returns the DHT block data (unstructured without type and | ||
67 | // expiration information. | ||
68 | Data() []byte | ||
69 | |||
70 | // Return the block type | ||
71 | Type() uint16 | ||
72 | |||
73 | // Expire returns the block expiration | ||
74 | Expire() util.AbsoluteTime | ||
75 | |||
76 | // Verify the integrity of a block (optional). Override in custom query | ||
77 | // types to implement block-specific integrity checks (see GNSBlock for | ||
78 | // example). This verification is usually weaker than the verification | ||
79 | // method from a Query (see GNSBlock.Verify for explanation). | ||
80 | Verify() error | ||
81 | |||
82 | // String returns the human-readable representation of a block | ||
83 | String() string | ||
84 | } | ||
85 | |||
86 | // Unwrap (raw) block to a specific block type | ||
87 | func Unwrap(blk Block, obj interface{}) error { | ||
88 | return data.Unmarshal(obj, blk.Data()) | ||
89 | } | ||
90 | |||
91 | //---------------------------------------------------------------------- | ||
92 | // Generic interface implementations without persistent attributes | ||
93 | //---------------------------------------------------------------------- | ||
94 | |||
95 | // GenericQuery is the binary representation of a DHT key | ||
96 | type GenericQuery struct { | ||
97 | // Key for repository queries (local/remote) | ||
98 | key *crypto.HashCode | ||
99 | |||
100 | // query parameters (binary value representation) | ||
101 | params map[string][]byte | ||
102 | } | ||
103 | |||
104 | // Key interface method implementation | ||
105 | func (q *GenericQuery) Key() *crypto.HashCode { | ||
106 | return q.key | ||
107 | } | ||
108 | |||
109 | // Get retrieves the value of a named query parameter | ||
110 | func (q *GenericQuery) Get(key string, value any) bool { | ||
111 | data, ok := q.params[key] | ||
112 | if !ok { | ||
113 | return false | ||
114 | } | ||
115 | dec := gob.NewDecoder(bytes.NewReader(data)) | ||
116 | return dec.Decode(value) != nil | ||
117 | } | ||
118 | |||
119 | // Set stores the value of a named query parameter | ||
120 | func (q *GenericQuery) Set(key string, value any) { | ||
121 | wrt := new(bytes.Buffer) | ||
122 | enc := gob.NewEncoder(wrt) | ||
123 | if enc.Encode(value) == nil { | ||
124 | q.params[key] = wrt.Bytes() | ||
125 | } | ||
126 | } | ||
127 | |||
128 | // Verify interface method implementation | ||
129 | func (q *GenericQuery) Verify(b Block) error { | ||
130 | // no verification, no errors ;) | ||
131 | return nil | ||
132 | } | ||
133 | |||
134 | // Decrypt interface method implementation | ||
135 | func (q *GenericQuery) Decrypt(b Block) error { | ||
136 | // no decryption, no errors ;) | ||
137 | return nil | ||
138 | } | ||
139 | |||
140 | // String returns the human-readable representation of a block | ||
141 | func (q *GenericQuery) String() string { | ||
142 | return fmt.Sprintf("GenericQuery{key=%s}", hex.EncodeToString(q.Key().Bits)) | ||
143 | } | ||
144 | |||
145 | // NewGenericQuery creates a simple Query from hash code. | ||
146 | func NewGenericQuery(buf []byte) *GenericQuery { | ||
147 | return &GenericQuery{ | ||
148 | key: crypto.NewHashCode(buf), | ||
149 | params: make(map[string][]byte), | ||
150 | } | ||
151 | } | ||
152 | |||
153 | //---------------------------------------------------------------------- | ||
154 | |||
155 | // GenericBlock is the block in simple binary representation | ||
156 | type GenericBlock struct { | ||
157 | block []byte // block data | ||
158 | btype uint16 // block type | ||
159 | expire util.AbsoluteTime // expiration date | ||
160 | } | ||
161 | |||
162 | // Data interface method implementation | ||
163 | func (b *GenericBlock) Data() []byte { | ||
164 | return b.block | ||
165 | } | ||
166 | |||
167 | // Type returns the block type | ||
168 | func (b *GenericBlock) Type() uint16 { | ||
169 | return b.btype | ||
170 | } | ||
171 | |||
172 | // Expire returns the block expiration | ||
173 | func (b *GenericBlock) Expire() util.AbsoluteTime { | ||
174 | return b.expire | ||
175 | } | ||
176 | |||
177 | // String returns the human-readable representation of a block | ||
178 | func (b *GenericBlock) String() string { | ||
179 | return fmt.Sprintf("GenericBlock{type=%d,expires=%s,data=[%d]}", | ||
180 | b.btype, b.expire.String(), len(b.block)) | ||
181 | } | ||
182 | |||
183 | // Verify interface method implementation | ||
184 | func (b *GenericBlock) Verify() error { | ||
185 | // no verification, no errors ;) | ||
186 | return nil | ||
187 | } | ||
188 | |||
189 | // NewGenericBlock creates a Block from binary data. | ||
190 | func NewGenericBlock(buf []byte) *GenericBlock { | ||
191 | return &GenericBlock{ | ||
192 | block: util.Clone(buf), | ||
193 | btype: DHT_BLOCK_ANY, // unknown block type | ||
194 | expire: util.AbsoluteTimeNever(), // never expires | ||
195 | } | ||
196 | } | ||
diff --git a/src/gnunet/service/dht/blocks/generic_test.go b/src/gnunet/service/dht/blocks/generic_test.go new file mode 100644 index 0000000..51ee5a1 --- /dev/null +++ b/src/gnunet/service/dht/blocks/generic_test.go | |||
@@ -0,0 +1,67 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "testing" | ||
24 | ) | ||
25 | |||
26 | // Test parameter handling for queries | ||
27 | func TestQueryParams(t *testing.T) { | ||
28 | q := NewGenericQuery(nil) | ||
29 | |||
30 | // set parameters | ||
31 | var ( | ||
32 | btype uint16 = DHT_BLOCK_ANY | ||
33 | flags uint32 = 0 | ||
34 | name string = "Test" | ||
35 | data = make([]byte, 8) | ||
36 | ) | ||
37 | q.Set("btype", btype) | ||
38 | q.Set("flags", flags) | ||
39 | q.Set("name", name) | ||
40 | q.Set("data", data) | ||
41 | |||
42 | // get parameters | ||
43 | var ( | ||
44 | t_btype uint16 | ||
45 | t_flags uint32 | ||
46 | t_name string | ||
47 | t_data []byte | ||
48 | ) | ||
49 | q.Get("btype", &t_btype) | ||
50 | q.Get("flags", &t_flags) | ||
51 | q.Get("name", &t_name) | ||
52 | q.Get("data", &t_data) | ||
53 | |||
54 | // check for unchanged data | ||
55 | if btype != t_btype { | ||
56 | t.Fatal("btype mismatch") | ||
57 | } | ||
58 | if flags != t_flags { | ||
59 | t.Fatal("flags mismatch") | ||
60 | } | ||
61 | if name != t_name { | ||
62 | t.Fatal("name mismatch") | ||
63 | } | ||
64 | if !bytes.Equal(data, t_data) { | ||
65 | t.Fatal("data mismatch") | ||
66 | } | ||
67 | } | ||
diff --git a/src/gnunet/service/dht/blocks/gns.go b/src/gnunet/service/dht/blocks/gns.go new file mode 100644 index 0000000..2085677 --- /dev/null +++ b/src/gnunet/service/dht/blocks/gns.go | |||
@@ -0,0 +1,172 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | import ( | ||
22 | "errors" | ||
23 | "fmt" | ||
24 | "gnunet/crypto" | ||
25 | "gnunet/util" | ||
26 | |||
27 | "github.com/bfix/gospel/data" | ||
28 | ) | ||
29 | |||
30 | // Error messages | ||
31 | var ( | ||
32 | ErrBlockNotDecrypted = fmt.Errorf("GNS block not decrypted") | ||
33 | ) | ||
34 | |||
35 | //---------------------------------------------------------------------- | ||
36 | // Query key for GNS lookups | ||
37 | //---------------------------------------------------------------------- | ||
38 | |||
39 | // GNSQuery specifies the context for a basic GNS name lookup of an (atomic) | ||
40 | // label in a given zone identified by its public key. | ||
41 | type GNSQuery struct { | ||
42 | GenericQuery | ||
43 | Zone *crypto.ZoneKey // Public zone key | ||
44 | Label string // Atomic label | ||
45 | derived *crypto.ZoneKey // Derived zone key from (pkey,label) | ||
46 | } | ||
47 | |||
48 | // Verify the integrity of the block data from a signature. | ||
49 | func (q *GNSQuery) Verify(b Block) (err error) { | ||
50 | switch blk := b.(type) { | ||
51 | case *GNSBlock: | ||
52 | // Integrity check performed | ||
53 | blk.checked = true | ||
54 | |||
55 | // verify derived key | ||
56 | dkey := blk.DerivedKeySig.ZoneKey | ||
57 | dkey2, _ := q.Zone.Derive(q.Label, "gns") | ||
58 | if !dkey.Equal(dkey2) { | ||
59 | return fmt.Errorf("invalid signature key for GNS Block") | ||
60 | } | ||
61 | // verify signature | ||
62 | var buf []byte | ||
63 | if buf, err = data.Marshal(blk.Body); err != nil { | ||
64 | return | ||
65 | } | ||
66 | blk.verified, err = blk.DerivedKeySig.Verify(buf) | ||
67 | |||
68 | default: | ||
69 | err = errors.New("can't verify block type") | ||
70 | } | ||
71 | return | ||
72 | } | ||
73 | |||
74 | // Decrypt block data with a key derived from zone key and label. | ||
75 | func (q *GNSQuery) Decrypt(b Block) (err error) { | ||
76 | switch blk := b.(type) { | ||
77 | case *GNSBlock: | ||
78 | // decrypt GNS payload | ||
79 | blk.data, err = q.Zone.Decrypt(blk.Body.Data, q.Label, blk.Body.Expire) | ||
80 | blk.decrypted = true | ||
81 | return | ||
82 | |||
83 | default: | ||
84 | err = errors.New("can't decrypt block type") | ||
85 | } | ||
86 | return | ||
87 | } | ||
88 | |||
89 | // NewGNSQuery assembles a new Query object for the given zone and label. | ||
90 | func NewGNSQuery(zkey *crypto.ZoneKey, label string) *GNSQuery { | ||
91 | // derive a public key from (pkey,label) and set the repository | ||
92 | // key as the SHA512 hash of the binary key representation. | ||
93 | // (key blinding) | ||
94 | pd, _ := zkey.Derive(label, "gns") | ||
95 | gq := crypto.Hash(pd.Bytes()).Bits | ||
96 | return &GNSQuery{ | ||
97 | GenericQuery: *NewGenericQuery(gq), | ||
98 | Zone: zkey, | ||
99 | Label: label, | ||
100 | derived: pd, | ||
101 | } | ||
102 | } | ||
103 | |||
104 | //---------------------------------------------------------------------- | ||
105 | // GNS blocks | ||
106 | //---------------------------------------------------------------------- | ||
107 | |||
108 | // SignedGNSBlockData represents the signed content of a GNS block | ||
109 | type SignedGNSBlockData struct { | ||
110 | Purpose *crypto.SignaturePurpose `` // Size and purpose of signature (8 bytes) | ||
111 | Expire util.AbsoluteTime `` // Expiration time of the block. | ||
112 | Data []byte `size:"*"` // Block data content | ||
113 | } | ||
114 | |||
115 | // GNSBlock is the result of GNS lookups for a given label in a zone. | ||
116 | // An encrypted and signed container for GNS resource records that represents | ||
117 | // the "atomic" data structure associated with a GNS label in a given zone. | ||
118 | type GNSBlock struct { | ||
119 | GenericBlock | ||
120 | |||
121 | // persistent | ||
122 | DerivedKeySig *crypto.ZoneSignature // Derived key used for signing | ||
123 | Body *SignedGNSBlockData | ||
124 | |||
125 | // transient data (not serialized) | ||
126 | checked bool // block integrity checked | ||
127 | verified bool // block signature verified (internal) | ||
128 | decrypted bool // block decrypted (internal) | ||
129 | data []byte // decrypted data | ||
130 | } | ||
131 | |||
132 | // Data block interface implementation | ||
133 | func (b *GNSBlock) Data() []byte { | ||
134 | buf, _ := data.Marshal(b) | ||
135 | return buf | ||
136 | } | ||
137 | |||
138 | // String returns the human-readable representation of a GNSBlock | ||
139 | func (b *GNSBlock) String() string { | ||
140 | return fmt.Sprintf("GNSBlock{Verified=%v,Decrypted=%v,data=[%d]}", | ||
141 | b.verified, b.decrypted, len(b.Body.Data)) | ||
142 | } | ||
143 | |||
144 | // NewBlock instantiates an empty GNS block | ||
145 | func NewBlock() *GNSBlock { | ||
146 | return &GNSBlock{ | ||
147 | DerivedKeySig: nil, | ||
148 | Body: &SignedGNSBlockData{ | ||
149 | Purpose: new(crypto.SignaturePurpose), | ||
150 | Expire: *new(util.AbsoluteTime), | ||
151 | Data: nil, | ||
152 | }, | ||
153 | checked: false, | ||
154 | verified: false, | ||
155 | decrypted: false, | ||
156 | data: nil, | ||
157 | } | ||
158 | } | ||
159 | |||
160 | // Verify the integrity of the block data from a signature. | ||
161 | // 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 | ||
163 | // be verified. This is only possible in Query.Verify(). | ||
164 | func (b *GNSBlock) Verify() (err error) { | ||
165 | // verify signature | ||
166 | var buf []byte | ||
167 | if buf, err = data.Marshal(b.Body); err != nil { | ||
168 | return | ||
169 | } | ||
170 | _, err = b.DerivedKeySig.Verify(buf) | ||
171 | return | ||
172 | } | ||
diff --git a/src/gnunet/service/dht/blocks/hello.go b/src/gnunet/service/dht/blocks/hello.go new file mode 100644 index 0000000..77fc2ae --- /dev/null +++ b/src/gnunet/service/dht/blocks/hello.go | |||
@@ -0,0 +1,226 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "encoding/binary" | ||
24 | "fmt" | ||
25 | "gnunet/util" | ||
26 | "net/url" | ||
27 | "strconv" | ||
28 | "strings" | ||
29 | |||
30 | "github.com/bfix/gospel/crypto/ed25519" | ||
31 | "github.com/bfix/gospel/data" | ||
32 | ) | ||
33 | |||
34 | //---------------------------------------------------------------------- | ||
35 | // HELLO URLs are used for bootstrapping a node and for adding nodes | ||
36 | // outside of GNUnet message exchange (e.g. command-line tools) | ||
37 | //---------------------------------------------------------------------- | ||
38 | |||
39 | const helloPrefix = "gnunet://hello/" | ||
40 | |||
41 | // HelloBlock is the DHT-managed block type for HELLO information. | ||
42 | // It is used to create and parse HELLO URLs. | ||
43 | // All addresses expire at the same time /this different from HELLO | ||
44 | // messages (see message.HeeloMsg). | ||
45 | type HelloBlock struct { | ||
46 | PeerID *util.PeerID `` // peer identifier | ||
47 | Signature *ed25519.EdSignature `` // signature | ||
48 | Expire util.AbsoluteTime `` // Expiration date | ||
49 | AddrBin []byte `size:"*"` // raw address data | ||
50 | |||
51 | // transient attributes | ||
52 | addrs []*util.Address // cooked address data | ||
53 | } | ||
54 | |||
55 | // SetAddresses adds a bulk of addresses for this HELLO block. | ||
56 | func (h *HelloBlock) SetAddresses(a []*util.Address) { | ||
57 | h.addrs = util.Clone(a) | ||
58 | h.finalize() | ||
59 | } | ||
60 | |||
61 | // Addresses returns the list of addresses | ||
62 | func (h *HelloBlock) Addresses() []*util.Address { | ||
63 | return util.Clone(h.addrs) | ||
64 | } | ||
65 | |||
66 | // ParseHelloURL parses a HELLO URL of the following form: | ||
67 | // gnunet://hello/<PeerID>/<signature>/<expire>?<addrs> | ||
68 | // The addresses are encoded. | ||
69 | func ParseHelloURL(u string) (h *HelloBlock, err error) { | ||
70 | // check and trim prefix | ||
71 | if !strings.HasPrefix(u, helloPrefix) { | ||
72 | err = fmt.Errorf("invalid HELLO-URL prefix: '%s'", u) | ||
73 | return | ||
74 | } | ||
75 | u = u[len(helloPrefix):] | ||
76 | |||
77 | // split remainder into parts | ||
78 | p := strings.Split(u, "/") | ||
79 | if len(p) != 3 { | ||
80 | err = fmt.Errorf("invalid HELLO-URL: '%s'", u) | ||
81 | return | ||
82 | } | ||
83 | |||
84 | // assemble HELLO data | ||
85 | h = new(HelloBlock) | ||
86 | |||
87 | // (1) parse peer public key (peer ID) | ||
88 | var buf []byte | ||
89 | if buf, err = util.DecodeStringToBinary(p[0], 32); err != nil { | ||
90 | return | ||
91 | } | ||
92 | h.PeerID = util.NewPeerID(buf) | ||
93 | |||
94 | // (2) parse signature | ||
95 | if buf, err = util.DecodeStringToBinary(p[1], 64); err != nil { | ||
96 | return | ||
97 | } | ||
98 | if h.Signature, err = ed25519.NewEdSignatureFromBytes(buf); err != nil { | ||
99 | return | ||
100 | } | ||
101 | |||
102 | // (3) split last element into parts | ||
103 | q := strings.SplitN(p[2], "?", 2) | ||
104 | |||
105 | // (4) parse expiration date | ||
106 | var exp uint64 | ||
107 | if exp, err = strconv.ParseUint(q[0], 10, 64); err != nil { | ||
108 | return | ||
109 | } | ||
110 | h.Expire = util.NewAbsoluteTimeEpoch(exp) | ||
111 | |||
112 | // (5) process addresses. | ||
113 | h.addrs = make([]*util.Address, 0) | ||
114 | var ua string | ||
115 | for _, a := range strings.Split(q[1], "&") { | ||
116 | // unescape URL query | ||
117 | if ua, err = url.QueryUnescape(a); err != nil { | ||
118 | return | ||
119 | } | ||
120 | // parse address and append it to list | ||
121 | var addr *util.Address | ||
122 | if addr, err = util.ParseAddress(ua); err != nil { | ||
123 | return | ||
124 | } | ||
125 | h.addrs = append(h.addrs, addr) | ||
126 | } | ||
127 | |||
128 | // (6) generate raw address data so block is complete | ||
129 | h.finalize() | ||
130 | return | ||
131 | } | ||
132 | |||
133 | // ParseHelloFromBytes converts a byte array into a HelloBlock instance. | ||
134 | func ParseHelloFromBytes(buf []byte) (h *HelloBlock, err error) { | ||
135 | h = new(HelloBlock) | ||
136 | if err = data.Unmarshal(h, buf); err == nil { | ||
137 | err = h.finalize() | ||
138 | } | ||
139 | return | ||
140 | } | ||
141 | |||
142 | // finalize block data (generate dependent fields) | ||
143 | func (h *HelloBlock) finalize() (err error) { | ||
144 | if h.addrs == nil { | ||
145 | err = data.Unmarshal(h.addrs, h.AddrBin) | ||
146 | } else if h.AddrBin == nil { | ||
147 | wrt := new(bytes.Buffer) | ||
148 | for _, a := range h.addrs { | ||
149 | wrt.WriteString(a.String()) | ||
150 | wrt.WriteByte(0) | ||
151 | } | ||
152 | h.AddrBin = wrt.Bytes() | ||
153 | } | ||
154 | return | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | // Message returns the corresponding HELLO message to be sent to peers. | ||
159 | func (h *HelloBlock) Message() *message.HelloMsg { | ||
160 | msg := message.NewHelloMsg(h.PeerID) | ||
161 | for _, a := range h.addrs { | ||
162 | msg.AddAddress(message.NewHelloAddress(a, h.Expire)) | ||
163 | } | ||
164 | return msg | ||
165 | } | ||
166 | */ | ||
167 | |||
168 | // URL returns the HELLO URL for the data. | ||
169 | func (h *HelloBlock) URL() string { | ||
170 | u := fmt.Sprintf("%s%s/%s/%d?", | ||
171 | helloPrefix, | ||
172 | h.PeerID.String(), | ||
173 | util.EncodeBinaryToString(h.Signature.Bytes()), | ||
174 | h.Expire.Epoch(), | ||
175 | ) | ||
176 | for i, a := range h.addrs { | ||
177 | if i > 0 { | ||
178 | u += "&" | ||
179 | } | ||
180 | u += url.QueryEscape(a.String()) | ||
181 | } | ||
182 | return u | ||
183 | } | ||
184 | |||
185 | // Equals returns true if two HELLOs are the same. The expiration | ||
186 | // timestamp is ignored in the comparision. | ||
187 | func (h *HelloBlock) Equals(g *HelloBlock) bool { | ||
188 | if !h.PeerID.Equals(g.PeerID) || | ||
189 | !util.Equals(h.Signature.Bytes(), g.Signature.Bytes()) || | ||
190 | len(h.addrs) != len(g.addrs) { | ||
191 | return false | ||
192 | } | ||
193 | for i, a := range h.addrs { | ||
194 | if !a.Equals(g.addrs[i]) { | ||
195 | return false | ||
196 | } | ||
197 | } | ||
198 | return true | ||
199 | } | ||
200 | |||
201 | // Verify the integrity of the HELLO data | ||
202 | func (h *HelloBlock) Verify() (bool, error) { | ||
203 | // assemble signed data and public key | ||
204 | sd := h.signedData() | ||
205 | pub := ed25519.NewPublicKeyFromBytes(h.PeerID.Key) | ||
206 | return pub.EdVerify(sd, h.Signature) | ||
207 | } | ||
208 | |||
209 | // Sign the HELLO data with private key | ||
210 | func (h *HelloBlock) Sign(prv *ed25519.PrivateKey) (err error) { | ||
211 | // assemble signed data | ||
212 | sd := h.signedData() | ||
213 | h.Signature, err = prv.EdSign(sd) | ||
214 | return | ||
215 | } | ||
216 | |||
217 | // signedData assembles a data block for sign and verify operations. | ||
218 | func (h *HelloBlock) signedData() []byte { | ||
219 | buf := new(bytes.Buffer) | ||
220 | buf.Write(h.PeerID.Key) | ||
221 | binary.Write(buf, binary.BigEndian, h.Expire) | ||
222 | for _, a := range h.addrs { | ||
223 | buf.Write(a.Address) | ||
224 | } | ||
225 | return buf.Bytes() | ||
226 | } | ||
diff --git a/src/gnunet/service/dht/blocks/hello_test.go b/src/gnunet/service/dht/blocks/hello_test.go new file mode 100644 index 0000000..089259a --- /dev/null +++ b/src/gnunet/service/dht/blocks/hello_test.go | |||
@@ -0,0 +1,44 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package blocks | ||
20 | |||
21 | import "testing" | ||
22 | |||
23 | const ( | ||
24 | helloURL = "gnunet://hello" + | ||
25 | "/7KTBJ90340HF1Q2GB0A57E2XJER4FDHX8HP5GHEB9125VPWPD27G" + | ||
26 | |||
27 | "/BNMDFN6HJCPWSPNBSEC06MC1K8QN1Z2DHRQSRXDTFR7FTBD4JHN" + | ||
28 | "BJ2RJAAEZ31FWG1Q3PMN3PXGZQ3Q7NTNEKQZFA7TE2Y46FM8E20R" + | ||
29 | "/1653499308" + | ||
30 | "?r5n%2Bip%2Budp%3A1.2.3.4%3A6789" + | ||
31 | "&gnunet%2Btcp%3A12.3.4.5" | ||
32 | ) | ||
33 | |||
34 | func TestHelloURL(t *testing.T) { | ||
35 | |||
36 | hd, err := ParseHelloURL(helloURL) | ||
37 | if err != nil { | ||
38 | t.Fatal(err) | ||
39 | } | ||
40 | u := hd.URL() | ||
41 | if u != helloURL { | ||
42 | t.Fatal("urls don't match") | ||
43 | } | ||
44 | } | ||
diff --git a/src/gnunet/transport/session.go b/src/gnunet/service/dht/blocks/types.go index f5a0787..04edb6e 100644 --- a/src/gnunet/transport/session.go +++ b/src/gnunet/service/dht/blocks/types.go | |||
@@ -1,5 +1,5 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | 1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. |
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | 2 | // Copyright (C) 2019-2022 Bernd Fix >Y< |
3 | // | 3 | // |
4 | // gnunet-go is free software: you can redistribute it and/or modify it | 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 | 5 | // under the terms of the GNU Affero General Public License as published |
@@ -16,14 +16,11 @@ | |||
16 | // | 16 | // |
17 | // SPDX-License-Identifier: AGPL3.0-or-later | 17 | // SPDX-License-Identifier: AGPL3.0-or-later |
18 | 18 | ||
19 | package transport | 19 | package blocks |
20 | 20 | ||
21 | // Session states | 21 | // DHT Block types |
22 | const ( | 22 | const ( |
23 | KxStateDown = iota // No handshake yet. | 23 | DHT_BLOCK_ANY = 0 |
24 | KxStateKeySent // We've sent our session key. | 24 | DHT_BLOCK_HELLO = 7 // Type of a block that contains a HELLO for a peer |
25 | KxStateKeyReceived // We've received the other peers session key. | 25 | DHT_BLOCK_GNS = 11 // Block for storing record data |
26 | KxStateUp // Key exchange is done. | ||
27 | KxStateRekeySent // We're rekeying (or had a timeout). | ||
28 | KxPeerDisconnect // Last state of a KX (when it is being terminated). | ||
29 | ) | 26 | ) |
diff --git a/src/gnunet/service/dht/bloomfilter.go b/src/gnunet/service/dht/bloomfilter.go new file mode 100644 index 0000000..dcfd935 --- /dev/null +++ b/src/gnunet/service/dht/bloomfilter.go | |||
@@ -0,0 +1,123 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package dht | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "crypto/sha512" | ||
24 | "encoding/binary" | ||
25 | ) | ||
26 | |||
27 | //====================================================================== | ||
28 | // Generic BloomFilter | ||
29 | //====================================================================== | ||
30 | |||
31 | // BloomFilter parameter | ||
32 | var ( | ||
33 | bfNumBits = 128 | ||
34 | bfHash = sha512.New | ||
35 | ) | ||
36 | |||
37 | // BloomFilter is a space-efficient probabilistic datastructure to test if | ||
38 | // an element is part of a set of elementsis defined as a string of bits | ||
39 | // always initially empty. | ||
40 | type BloomFilter struct { | ||
41 | data []byte // filter bits | ||
42 | salt []byte // salt for hashing | ||
43 | } | ||
44 | |||
45 | // NewBloomFilter cretes a new filter using the specified salt. An unused | ||
46 | // salt is set to nil. | ||
47 | func NewBloomFilter(salt []byte) *BloomFilter { | ||
48 | return &BloomFilter{ | ||
49 | data: make([]byte, (bfNumBits+7)/8), | ||
50 | salt: salt, | ||
51 | } | ||
52 | } | ||
53 | |||
54 | // Add entry (binary representation): | ||
55 | // When adding an element to the Bloom filter bf using BF-SET(bf,e), each | ||
56 | // integer n of the mapping M(e) is interpreted as a bit offset n mod L | ||
57 | // within bf and set to 1. | ||
58 | func (bf *BloomFilter) Add(e []byte) { | ||
59 | for _, idx := range bf.indices(e) { | ||
60 | bf.data[idx/8] |= (1 << (idx % 7)) | ||
61 | } | ||
62 | } | ||
63 | |||
64 | // Contains returns true if the entry is most likely to be included: | ||
65 | // When testing if an element may be in the Bloom filter bf using | ||
66 | // BF-TEST(bf,e), each bit offset n mod L within bf MUST have been set to 1. | ||
67 | // Otherwise, the element is not considered to be in the Bloom filter. | ||
68 | func (bf *BloomFilter) Contains(e []byte) bool { | ||
69 | for _, idx := range bf.indices(e) { | ||
70 | if bf.data[idx/8]&(1<<(idx%7)) == 0 { | ||
71 | return false | ||
72 | } | ||
73 | } | ||
74 | return true | ||
75 | } | ||
76 | |||
77 | // indices returns the list of bit indices for antry e: | ||
78 | // The element e is prepended with a salt (pütional) and hashed using SHA-512. | ||
79 | // The resulting byte string is interpreted as a list of 16 32-bit integers | ||
80 | // in network byte order. | ||
81 | func (bf *BloomFilter) indices(e []byte) []int { | ||
82 | // hash the entry (with optional salt prepended) | ||
83 | hsh := bfHash() | ||
84 | if bf.salt != nil { | ||
85 | hsh.Write(bf.salt) | ||
86 | } | ||
87 | hsh.Write(e) | ||
88 | h := hsh.Sum(nil) | ||
89 | |||
90 | // compute the indices for the entry | ||
91 | idx := make([]int, len(h)/2) | ||
92 | buf := bytes.NewReader(h) | ||
93 | for i := range idx { | ||
94 | binary.Read(buf, binary.BigEndian, &idx[i]) | ||
95 | } | ||
96 | return idx | ||
97 | } | ||
98 | |||
99 | //====================================================================== | ||
100 | // BloomFilter for peer addresses | ||
101 | //====================================================================== | ||
102 | |||
103 | // PeerBloomFilter implements specific Add/Contains functions. | ||
104 | type PeerBloomFilter struct { | ||
105 | BloomFilter | ||
106 | } | ||
107 | |||
108 | // NewPeerBloomFilter creates a new filter for peer addresses. | ||
109 | func NewPeerBloomFilter() *PeerBloomFilter { | ||
110 | return &PeerBloomFilter{ | ||
111 | BloomFilter: *NewBloomFilter(nil), | ||
112 | } | ||
113 | } | ||
114 | |||
115 | // Add peer address to the filter. | ||
116 | func (bf *PeerBloomFilter) Add(p *PeerAddress) { | ||
117 | bf.BloomFilter.Add(p.addr[:]) | ||
118 | } | ||
119 | |||
120 | // Contains returns true if the peer address is most likely to be included. | ||
121 | func (bf *PeerBloomFilter) Contains(p *PeerAddress) bool { | ||
122 | return bf.BloomFilter.Contains(p.addr[:]) | ||
123 | } | ||
diff --git a/src/gnunet/service/dht/dhtstore_test.go b/src/gnunet/service/dht/dhtstore_test.go new file mode 100644 index 0000000..3cb8080 --- /dev/null +++ b/src/gnunet/service/dht/dhtstore_test.go | |||
@@ -0,0 +1,89 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package dht | ||
20 | |||
21 | import ( | ||
22 | "encoding/hex" | ||
23 | "gnunet/crypto" | ||
24 | "gnunet/service" | ||
25 | blocks "gnunet/service/dht/blocks" | ||
26 | "math/rand" | ||
27 | "testing" | ||
28 | ) | ||
29 | |||
30 | // test constants | ||
31 | const ( | ||
32 | fsNumBlocks = 5 | ||
33 | ) | ||
34 | |||
35 | // TestDHTFileStore generates 'fsNumBlocks' fully-random blocks | ||
36 | // and stores them under their SHA512 key. It than retrieves | ||
37 | // each block from storage and checks for matching hash. | ||
38 | func TestDHTFilesStore(t *testing.T) { | ||
39 | |||
40 | // create file store | ||
41 | fs, err := service.NewFileCache("/var/lib/gnunet/dht/cache", "100") | ||
42 | if err != nil { | ||
43 | t.Fatal(err) | ||
44 | } | ||
45 | // allocate keys | ||
46 | keys := make([]blocks.Query, 0, fsNumBlocks) | ||
47 | |||
48 | // First round: save blocks | ||
49 | for i := 0; i < fsNumBlocks; i++ { | ||
50 | // generate random block | ||
51 | size := 20 // 1024 + rand.Intn(62000) | ||
52 | buf := make([]byte, size) | ||
53 | rand.Read(buf) | ||
54 | val := blocks.NewGenericBlock(buf) | ||
55 | // generate associated key | ||
56 | k := crypto.Hash(buf).Bits | ||
57 | key := blocks.NewGenericQuery(k) | ||
58 | t.Logf("> %d: %s -- %s", i, hex.EncodeToString(k), hex.EncodeToString(buf)) | ||
59 | |||
60 | // store block | ||
61 | if err := fs.Put(key, val); err != nil { | ||
62 | t.Fatal(err) | ||
63 | } | ||
64 | |||
65 | // remember key | ||
66 | keys = append(keys, key) | ||
67 | } | ||
68 | |||
69 | // Second round: retrieve blocks and check | ||
70 | for i, key := range keys { | ||
71 | // get block | ||
72 | val, err := fs.Get(key) | ||
73 | if err != nil { | ||
74 | t.Fatal(err) | ||
75 | } | ||
76 | buf := val.Data() | ||
77 | t.Logf("< %d: %s -- %s", i, hex.EncodeToString(key.Key().Bits), hex.EncodeToString(buf)) | ||
78 | |||
79 | // re-create key | ||
80 | k := crypto.Hash(buf) | ||
81 | |||
82 | // do the keys match? | ||
83 | if !k.Equals(key.Key()) { | ||
84 | t.Log(hex.EncodeToString(k.Bits)) | ||
85 | t.Log(hex.EncodeToString(key.Key().Bits)) | ||
86 | t.Fatal("key/value mismatch") | ||
87 | } | ||
88 | } | ||
89 | } | ||
diff --git a/src/gnunet/service/dht/module.go b/src/gnunet/service/dht/module.go index 1588a06..d369f3f 100644 --- a/src/gnunet/service/dht/module.go +++ b/src/gnunet/service/dht/module.go | |||
@@ -19,9 +19,13 @@ | |||
19 | package dht | 19 | package dht |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | ||
23 | "gnunet/config" | ||
24 | "gnunet/core" | ||
22 | "gnunet/message" | 25 | "gnunet/message" |
23 | "gnunet/service" | 26 | "gnunet/service" |
24 | "gnunet/service/gns" | 27 | "gnunet/service/dht/blocks" |
28 | "net/http" | ||
25 | ) | 29 | ) |
26 | 30 | ||
27 | //====================================================================== | 31 | //====================================================================== |
@@ -32,16 +36,99 @@ import ( | |||
32 | // Put and get blocks into/from a DHT. | 36 | // Put and get blocks into/from a DHT. |
33 | //---------------------------------------------------------------------- | 37 | //---------------------------------------------------------------------- |
34 | 38 | ||
35 | // Module handles the permanent storage of blocks under the query key. | 39 | // Module handles the permanent storage of blocks under a query key. |
36 | type Module struct { | 40 | type Module struct { |
41 | service.ModuleImpl | ||
42 | |||
43 | store service.DHTStore // reference to the block storage mechanism | ||
44 | cache service.DHTStore // transient block cache | ||
45 | core *core.Core // reference to core services | ||
46 | |||
47 | rtable *RoutingTable // routing table | ||
48 | } | ||
49 | |||
50 | // NewModule returns a new module instance. It initializes the storage | ||
51 | // mechanism for persistence. | ||
52 | func NewModule(ctx context.Context, c *core.Core) (m *Module) { | ||
53 | // create permanent storage handler | ||
54 | store, err := service.NewDHTStore(config.Cfg.DHT.Storage) | ||
55 | if err != nil { | ||
56 | return nil | ||
57 | } | ||
58 | // create cache handler | ||
59 | cache, err := service.NewDHTStore(config.Cfg.DHT.Cache) | ||
60 | if err != nil { | ||
61 | return nil | ||
62 | } | ||
63 | // create routing table | ||
64 | rt := NewRoutingTable(NewPeerAddress(c.PeerID())) | ||
65 | |||
66 | // return module instance | ||
67 | m = &Module{ | ||
68 | ModuleImpl: *service.NewModuleImpl(), | ||
69 | store: store, | ||
70 | cache: cache, | ||
71 | core: c, | ||
72 | rtable: rt, | ||
73 | } | ||
74 | // register as listener for core events | ||
75 | listener := m.Run(ctx, m.event, m.Filter()) | ||
76 | c.Register("dht", listener) | ||
77 | |||
78 | return | ||
37 | } | 79 | } |
38 | 80 | ||
39 | // Get a GNS block from the DHT | 81 | //---------------------------------------------------------------------- |
40 | func (nc *Module) Get(ctx *service.SessionContext, query *gns.Query) (*message.Block, error) { | 82 | |
83 | // Get a block from the DHT | ||
84 | func (nc *Module) Get(ctx context.Context, query blocks.Query) (block blocks.Block, err error) { | ||
85 | |||
86 | // check if we have the requested block in cache or permanent storage. | ||
87 | block, err = nc.cache.Get(query) | ||
88 | if err == nil { | ||
89 | // yes: we are done | ||
90 | return | ||
91 | } | ||
92 | block, err = nc.store.Get(query) | ||
93 | if err == nil { | ||
94 | // yes: we are done | ||
95 | return | ||
96 | } | ||
97 | // retrieve the block from the DHT | ||
98 | |||
41 | return nil, nil | 99 | return nil, nil |
42 | } | 100 | } |
43 | 101 | ||
44 | // Put a GNS block into the DHT | 102 | // Put a block into the DHT |
45 | func (nc *Module) Put(ctx *service.SessionContext, block *message.Block) error { | 103 | func (nc *Module) Put(ctx context.Context, key blocks.Query, block blocks.Block) error { |
46 | return nil | 104 | return nil |
47 | } | 105 | } |
106 | |||
107 | //---------------------------------------------------------------------- | ||
108 | |||
109 | // Filter returns the event filter for the module | ||
110 | func (m *Module) Filter() *core.EventFilter { | ||
111 | f := core.NewEventFilter() | ||
112 | f.AddEvent(core.EV_CONNECT) | ||
113 | f.AddEvent(core.EV_DISCONNECT) | ||
114 | f.AddMsgType(message.DHT_CLIENT_GET) | ||
115 | f.AddMsgType(message.DHT_CLIENT_GET_RESULTS_KNOWN) | ||
116 | f.AddMsgType(message.DHT_CLIENT_GET_STOP) | ||
117 | f.AddMsgType(message.DHT_CLIENT_PUT) | ||
118 | f.AddMsgType(message.DHT_CLIENT_RESULT) | ||
119 | return f | ||
120 | } | ||
121 | |||
122 | // Event handler | ||
123 | func (nc *Module) event(ctx context.Context, ev *core.Event) { | ||
124 | |||
125 | } | ||
126 | |||
127 | //---------------------------------------------------------------------- | ||
128 | |||
129 | // RPC returns the route and handler function for a JSON-RPC request | ||
130 | func (m *Module) RPC() (string, func(http.ResponseWriter, *http.Request)) { | ||
131 | return "/gns/", func(wrt http.ResponseWriter, req *http.Request) { | ||
132 | wrt.Write([]byte(`{"msg": "This is DHT" }`)) | ||
133 | } | ||
134 | } | ||
diff --git a/src/gnunet/service/dht/routingtable.go b/src/gnunet/service/dht/routingtable.go new file mode 100644 index 0000000..895a1b2 --- /dev/null +++ b/src/gnunet/service/dht/routingtable.go | |||
@@ -0,0 +1,305 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package dht | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "crypto/sha512" | ||
24 | "encoding/hex" | ||
25 | "gnunet/util" | ||
26 | "math/rand" | ||
27 | "sync" | ||
28 | |||
29 | "github.com/bfix/gospel/math" | ||
30 | ) | ||
31 | |||
32 | var ( | ||
33 | // routing table hash function: defines number of | ||
34 | // buckets and size of peer addresses | ||
35 | rtHash = sha512.New | ||
36 | ) | ||
37 | |||
38 | // Routing table contants (adjust with changing hash function) | ||
39 | const ( | ||
40 | numBuckets = 512 // number of bits of hash function result | ||
41 | numK = 20 // number of entries per k-bucket | ||
42 | sizeAddr = 64 // size of peer address in bytes | ||
43 | ) | ||
44 | |||
45 | //====================================================================== | ||
46 | //====================================================================== | ||
47 | |||
48 | // PeerAddress is the identifier for a peer in the DHT network. | ||
49 | // It is the SHA-512 hash of the PeerID (public Ed25519 key). | ||
50 | type PeerAddress struct { | ||
51 | addr [sizeAddr]byte | ||
52 | } | ||
53 | |||
54 | // NewPeerAddress returns the DHT address of a peer. | ||
55 | func NewPeerAddress(peer *util.PeerID) *PeerAddress { | ||
56 | r := new(PeerAddress) | ||
57 | h := rtHash() | ||
58 | h.Write(peer.Key) | ||
59 | copy(r.addr[:], h.Sum(nil)) | ||
60 | return r | ||
61 | } | ||
62 | |||
63 | // String returns a human-readble representation of an address. | ||
64 | func (addr *PeerAddress) String() string { | ||
65 | return hex.EncodeToString(addr.addr[:]) | ||
66 | } | ||
67 | |||
68 | // Equals returns true if two peer addresses are the same. | ||
69 | func (addr *PeerAddress) Equals(p *PeerAddress) bool { | ||
70 | return bytes.Equal(addr.addr[:], p.addr[:]) | ||
71 | } | ||
72 | |||
73 | // Distance between two addresses: returns a distance value and a | ||
74 | // bucket index (smaller index = less distant). | ||
75 | func (addr *PeerAddress) Distance(p *PeerAddress) (*math.Int, int) { | ||
76 | var d PeerAddress | ||
77 | for i := range d.addr { | ||
78 | d.addr[i] = addr.addr[i] ^ p.addr[i] | ||
79 | } | ||
80 | r := math.NewIntFromBytes(d.addr[:]) | ||
81 | return r, numBuckets - r.BitLen() | ||
82 | } | ||
83 | |||
84 | //====================================================================== | ||
85 | // Routing table implementation | ||
86 | //====================================================================== | ||
87 | |||
88 | // RoutingTable holds the (local) routing table for a node. | ||
89 | // The index of of an address is the number of bits in the | ||
90 | // distance to the reference address, so smaller index means | ||
91 | // "nearer" to the reference address. | ||
92 | type RoutingTable struct { | ||
93 | ref *PeerAddress // reference address for distance | ||
94 | buckets []*Bucket // list of buckets | ||
95 | list map[*PeerAddress]bool // keep list of peers | ||
96 | rwlock sync.RWMutex // lock for write operations | ||
97 | l2nse float64 // log2 of estimated network size | ||
98 | } | ||
99 | |||
100 | // NewRoutingTable creates a new routing table for the reference address. | ||
101 | func NewRoutingTable(ref *PeerAddress) *RoutingTable { | ||
102 | rt := new(RoutingTable) | ||
103 | rt.ref = ref | ||
104 | rt.list = make(map[*PeerAddress]bool) | ||
105 | rt.buckets = make([]*Bucket, numBuckets) | ||
106 | for i := range rt.buckets { | ||
107 | rt.buckets[i] = NewBucket(numK) | ||
108 | } | ||
109 | return rt | ||
110 | } | ||
111 | |||
112 | // Add new peer address to routing table. | ||
113 | // Returns true if the entry was added, false otherwise. | ||
114 | func (rt *RoutingTable) Add(p *PeerAddress, connected bool) bool { | ||
115 | // ensure one write and no readers | ||
116 | rt.rwlock.Lock() | ||
117 | defer rt.rwlock.Unlock() | ||
118 | |||
119 | // compute distance (bucket index) and insert address. | ||
120 | _, idx := p.Distance(rt.ref) | ||
121 | if rt.buckets[idx].Add(p, connected) { | ||
122 | rt.list[p] = true | ||
123 | return true | ||
124 | } | ||
125 | // Full bucket: we did not add the address to the routing table. | ||
126 | return false | ||
127 | } | ||
128 | |||
129 | // Remove peer address from routing table. | ||
130 | // Returns true if the entry was removed, false otherwise. | ||
131 | func (rt *RoutingTable) Remove(p *PeerAddress) bool { | ||
132 | // ensure one write and no readers | ||
133 | rt.rwlock.Lock() | ||
134 | defer rt.rwlock.Unlock() | ||
135 | |||
136 | // compute distance (bucket index) and remove entry from bucket | ||
137 | _, idx := p.Distance(rt.ref) | ||
138 | if rt.buckets[idx].Remove(p) { | ||
139 | delete(rt.list, p) | ||
140 | return true | ||
141 | } | ||
142 | return false | ||
143 | } | ||
144 | |||
145 | //---------------------------------------------------------------------- | ||
146 | // routing functions | ||
147 | //---------------------------------------------------------------------- | ||
148 | |||
149 | // SelectClosestPeer for a given peer address and bloomfilter. | ||
150 | func (rt *RoutingTable) SelectClosestPeer(p *PeerAddress, bf *PeerBloomFilter) (n *PeerAddress) { | ||
151 | // no writer allowed | ||
152 | rt.rwlock.RLock() | ||
153 | defer rt.rwlock.RUnlock() | ||
154 | |||
155 | // find closest address | ||
156 | var dist *math.Int | ||
157 | for _, b := range rt.buckets { | ||
158 | if k, d := b.SelectClosestPeer(p, bf); n == nil || (d != nil && d.Cmp(dist) < 0) { | ||
159 | dist = d | ||
160 | n = k | ||
161 | } | ||
162 | } | ||
163 | return | ||
164 | } | ||
165 | |||
166 | // SelectRandomPeer returns a random address from table (that is not | ||
167 | // included in the bloomfilter) | ||
168 | func (rt *RoutingTable) SelectRandomPeer(bf *PeerBloomFilter) *PeerAddress { | ||
169 | // no writer allowed | ||
170 | rt.rwlock.RLock() | ||
171 | defer rt.rwlock.RUnlock() | ||
172 | |||
173 | // select random entry from list | ||
174 | if size := len(rt.list); size > 0 { | ||
175 | idx := rand.Intn(size) | ||
176 | for k := range rt.list { | ||
177 | if idx == 0 { | ||
178 | return k | ||
179 | } | ||
180 | idx-- | ||
181 | } | ||
182 | } | ||
183 | return nil | ||
184 | } | ||
185 | |||
186 | // SelectPeer selects a neighbor depending on the number of hops parameter. | ||
187 | // If hops < NSE this function MUST return SelectRandomPeer() and | ||
188 | // SelectClosestpeer() otherwise. | ||
189 | func (rt *RoutingTable) SelectPeer(p *PeerAddress, hops int, bf *PeerBloomFilter) *PeerAddress { | ||
190 | if float64(hops) < rt.l2nse { | ||
191 | return rt.SelectRandomPeer(bf) | ||
192 | } | ||
193 | return rt.SelectClosestPeer(p, bf) | ||
194 | } | ||
195 | |||
196 | // IsClosestPeer returns true if p is the closest peer for k. Peers with a | ||
197 | // positive test in the Bloom filter are not considered. | ||
198 | func (rt *RoutingTable) IsClosestPeer(p, k *PeerAddress, bf *PeerBloomFilter) bool { | ||
199 | n := rt.SelectClosestPeer(k, bf) | ||
200 | return n.Equals(p) | ||
201 | } | ||
202 | |||
203 | // ComputeOutDegree computes the number of neighbors that a message should be forwarded to. | ||
204 | // The arguments are the desired replication level, the hop count of the message so far, | ||
205 | // and the base-2 logarithm of the current network size estimate (L2NSE) as provided by the | ||
206 | // underlay. The result is the non-negative number of next hops to select. | ||
207 | func (rt *RoutingTable) ComputeOutDegree(repl, hop int) int { | ||
208 | hf := float64(hop) | ||
209 | if hf > 4*rt.l2nse { | ||
210 | return 0 | ||
211 | } | ||
212 | if hf > 2*rt.l2nse { | ||
213 | return 1 | ||
214 | } | ||
215 | if repl == 0 { | ||
216 | repl = 1 | ||
217 | } else if repl > 16 { | ||
218 | repl = 16 | ||
219 | } | ||
220 | rm1 := float64(repl - 1) | ||
221 | return 1 + int(rm1/(rt.l2nse+rm1*hf)) | ||
222 | } | ||
223 | |||
224 | //====================================================================== | ||
225 | // Routing table buckets | ||
226 | //====================================================================== | ||
227 | |||
228 | // PeerEntry in a k-Bucket: use routing specific attributes | ||
229 | // for book-keeping | ||
230 | type PeerEntry struct { | ||
231 | addr *PeerAddress // peer address | ||
232 | connected bool // is peer connected? | ||
233 | } | ||
234 | |||
235 | // Bucket holds peer entries with approx. same distance from node | ||
236 | type Bucket struct { | ||
237 | list []*PeerEntry | ||
238 | rwlock sync.RWMutex | ||
239 | } | ||
240 | |||
241 | // NewBucket creates a new entry list of given size | ||
242 | func NewBucket(n int) *Bucket { | ||
243 | return &Bucket{ | ||
244 | list: make([]*PeerEntry, 0, n), | ||
245 | } | ||
246 | } | ||
247 | |||
248 | // Add peer address to the bucket if there is free space. | ||
249 | // Returns true if entry is added, false otherwise. | ||
250 | func (b *Bucket) Add(p *PeerAddress, connected bool) bool { | ||
251 | // only one writer and no readers | ||
252 | b.rwlock.Lock() | ||
253 | defer b.rwlock.Unlock() | ||
254 | |||
255 | // check for free space in bucket | ||
256 | if len(b.list) < numK { | ||
257 | // append entry at the end | ||
258 | pe := &PeerEntry{ | ||
259 | addr: p, | ||
260 | connected: connected, | ||
261 | } | ||
262 | b.list = append(b.list, pe) | ||
263 | return true | ||
264 | } | ||
265 | return false | ||
266 | } | ||
267 | |||
268 | // Remove peer address from the bucket. | ||
269 | // Returns true if entry is removed (found), false otherwise. | ||
270 | func (b *Bucket) Remove(p *PeerAddress) bool { | ||
271 | // only one writer and no readers | ||
272 | b.rwlock.Lock() | ||
273 | defer b.rwlock.Unlock() | ||
274 | |||
275 | for i, pe := range b.list { | ||
276 | if pe.addr.Equals(p) { | ||
277 | // found entry: remove it | ||
278 | b.list = append(b.list[:i], b.list[i+1:]...) | ||
279 | return true | ||
280 | } | ||
281 | } | ||
282 | return false | ||
283 | } | ||
284 | |||
285 | // SelectClosestPeer returns the entry with minimal distance to the given | ||
286 | // peer address; entries included in the bloom flter are ignored. | ||
287 | func (b *Bucket) SelectClosestPeer(p *PeerAddress, bf *PeerBloomFilter) (n *PeerAddress, dist *math.Int) { | ||
288 | // no writer allowed | ||
289 | b.rwlock.RLock() | ||
290 | defer b.rwlock.RUnlock() | ||
291 | |||
292 | for _, pe := range b.list { | ||
293 | // skip addresses in bloomfilter | ||
294 | if bf.Contains(pe.addr) { | ||
295 | continue | ||
296 | } | ||
297 | // check for shorter distance | ||
298 | if d, _ := p.Distance(pe.addr); n == nil || d.Cmp(dist) < 0 { | ||
299 | // remember best match | ||
300 | dist = d | ||
301 | n = pe.addr | ||
302 | } | ||
303 | } | ||
304 | return | ||
305 | } | ||
diff --git a/src/gnunet/service/dht/routingtable_test.go b/src/gnunet/service/dht/routingtable_test.go new file mode 100644 index 0000000..2579356 --- /dev/null +++ b/src/gnunet/service/dht/routingtable_test.go | |||
@@ -0,0 +1,140 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package dht | ||
20 | |||
21 | import ( | ||
22 | "gnunet/config" | ||
23 | "gnunet/core" | ||
24 | "gnunet/util" | ||
25 | "math/rand" | ||
26 | "testing" | ||
27 | ) | ||
28 | |||
29 | const ( | ||
30 | NUMP = 1000 // Total number of peers | ||
31 | EPOCHS = 10000 // number of epochs to run | ||
32 | ) | ||
33 | |||
34 | type Entry struct { | ||
35 | addr *PeerAddress // address of peer | ||
36 | ttl int64 // time to live (in epochs) | ||
37 | born int64 // epoch of birth | ||
38 | last int64 // last action | ||
39 | drop int64 // drop (in epochs) | ||
40 | revive int64 // revive dropped (in epochs) | ||
41 | online bool // peer connected? | ||
42 | } | ||
43 | |||
44 | // test data | ||
45 | var ( | ||
46 | cfg = &config.NodeConfig{ | ||
47 | PrivateSeed: "YGoe6XFH3XdvFRl+agx9gIzPTvxA229WFdkazEMdcOs=", | ||
48 | Endpoints: []string{ | ||
49 | "r5n+ip+udp://127.0.0.1:6666", | ||
50 | }, | ||
51 | } | ||
52 | ) | ||
53 | |||
54 | // TestRT connects and disconnects random peers to test the base | ||
55 | // functionality of the routing table algorithms. | ||
56 | func TestRT(t *testing.T) { | ||
57 | // start deterministic randomizer | ||
58 | rand.Seed(19031962) | ||
59 | |||
60 | // helper functions | ||
61 | genRemotePeer := func() *PeerAddress { | ||
62 | d := make([]byte, 32) | ||
63 | if _, err := rand.Read(d); err != nil { | ||
64 | panic(err) | ||
65 | } | ||
66 | return NewPeerAddress(util.NewPeerID(d)) | ||
67 | } | ||
68 | |||
69 | // create routing table and start command handler | ||
70 | local, err := core.NewLocalPeer(cfg) | ||
71 | if err != nil { | ||
72 | t.Fatal(err) | ||
73 | } | ||
74 | rt := NewRoutingTable(NewPeerAddress(local.GetID())) | ||
75 | |||
76 | // create a task list | ||
77 | tasks := make([]*Entry, NUMP) | ||
78 | for i := range tasks { | ||
79 | tasks[i] = new(Entry) | ||
80 | tasks[i].addr = genRemotePeer() | ||
81 | tasks[i].born = rand.Int63n(EPOCHS) | ||
82 | tasks[i].ttl = 1000 + rand.Int63n(7000) | ||
83 | tasks[i].drop = 2000 + rand.Int63n(3000) | ||
84 | tasks[i].revive = rand.Int63n(2000) | ||
85 | tasks[i].online = false | ||
86 | } | ||
87 | |||
88 | // actions: | ||
89 | connected := func(task *Entry, e int64, msg string) { | ||
90 | rt.Add(task.addr, true) | ||
91 | task.online = true | ||
92 | task.last = e | ||
93 | t.Logf("[%6d] %s %s\n", e, task.addr, msg) | ||
94 | } | ||
95 | disconnected := func(task *Entry, e int64, msg string) { | ||
96 | rt.Remove(task.addr) | ||
97 | task.online = false | ||
98 | task.last = e | ||
99 | t.Logf("[%6d] %s %s\n", e, task.addr, msg) | ||
100 | } | ||
101 | |||
102 | // run epochs | ||
103 | var e int64 | ||
104 | for e = 0; e < EPOCHS; e++ { | ||
105 | for _, task := range tasks { | ||
106 | // birth | ||
107 | if task.born == e { | ||
108 | connected(task, e, "connected") | ||
109 | continue | ||
110 | } | ||
111 | // death | ||
112 | if task.born+task.ttl == e { | ||
113 | disconnected(task, e, "disconnected") | ||
114 | continue | ||
115 | } | ||
116 | if task.online { | ||
117 | // drop out | ||
118 | if task.last+task.drop == e { | ||
119 | disconnected(task, e, "dropped out") | ||
120 | continue | ||
121 | } | ||
122 | } else { | ||
123 | // drop in | ||
124 | if task.last+task.drop == e { | ||
125 | connected(task, e, "dropped in") | ||
126 | continue | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | |||
132 | // execute some routing functions on remaining table | ||
133 | k := genRemotePeer() | ||
134 | bf := NewPeerBloomFilter() | ||
135 | n := rt.SelectClosestPeer(k, bf) | ||
136 | t.Logf("Closest: %s -> %s\n", k, n) | ||
137 | |||
138 | n = rt.SelectRandomPeer(bf) | ||
139 | t.Logf("Random: %s\n", n) | ||
140 | } | ||
diff --git a/src/gnunet/service/dht/service.go b/src/gnunet/service/dht/service.go new file mode 100644 index 0000000..2a189bb --- /dev/null +++ b/src/gnunet/service/dht/service.go | |||
@@ -0,0 +1,134 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package dht | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "fmt" | ||
24 | "io" | ||
25 | |||
26 | "gnunet/core" | ||
27 | "gnunet/message" | ||
28 | "gnunet/service" | ||
29 | |||
30 | "github.com/bfix/gospel/logger" | ||
31 | ) | ||
32 | |||
33 | // Error codes | ||
34 | var ( | ||
35 | ErrInvalidID = fmt.Errorf("invalid/unassociated ID") | ||
36 | ErrBlockExpired = fmt.Errorf("block expired") | ||
37 | ErrInvalidResponseType = fmt.Errorf("invald response type") | ||
38 | ) | ||
39 | |||
40 | //---------------------------------------------------------------------- | ||
41 | // "GNUnet R5N DHT" service implementation | ||
42 | //---------------------------------------------------------------------- | ||
43 | |||
44 | // Service implements a DHT service | ||
45 | type Service struct { | ||
46 | Module | ||
47 | } | ||
48 | |||
49 | // NewService creates a new DHT service instance | ||
50 | func NewService(ctx context.Context, c *core.Core) service.Service { | ||
51 | return &Service{ | ||
52 | Module: *NewModule(ctx, c), | ||
53 | } | ||
54 | } | ||
55 | |||
56 | // ServeClient processes a client channel. | ||
57 | func (s *Service) ServeClient(ctx context.Context, id int, mc *service.Connection) { | ||
58 | reqID := 0 | ||
59 | var cancel context.CancelFunc | ||
60 | ctx, cancel = context.WithCancel(ctx) | ||
61 | |||
62 | loop: | ||
63 | for { | ||
64 | // receive next message from client | ||
65 | reqID++ | ||
66 | logger.Printf(logger.DBG, "[dht:%d:%d] Waiting for client request...\n", id, reqID) | ||
67 | msg, err := mc.Receive(ctx) | ||
68 | if err != nil { | ||
69 | if err == io.EOF { | ||
70 | logger.Printf(logger.INFO, "[dht:%d:%d] Client channel closed.\n", id, reqID) | ||
71 | } else if err == service.ErrConnectionInterrupted { | ||
72 | logger.Printf(logger.INFO, "[dht:%d:%d] Service operation interrupted.\n", id, reqID) | ||
73 | } else { | ||
74 | logger.Printf(logger.ERROR, "[dht:%d:%d] Message-receive failed: %s\n", id, reqID, err.Error()) | ||
75 | } | ||
76 | break loop | ||
77 | } | ||
78 | logger.Printf(logger.INFO, "[dht:%d:%d] Received request: %v\n", id, reqID, msg) | ||
79 | |||
80 | // handle message | ||
81 | s.HandleMessage(context.WithValue(ctx, "label", fmt.Sprintf(":%d:%d", id, reqID)), msg, mc) | ||
82 | } | ||
83 | // close client connection | ||
84 | mc.Close() | ||
85 | |||
86 | // cancel all tasks running for this session/connection | ||
87 | logger.Printf(logger.INFO, "[dht:%d] Start closing session...\n", id) | ||
88 | cancel() | ||
89 | } | ||
90 | |||
91 | // HandleMessage handles a DHT request/response message. If the transport channel | ||
92 | // is nil, responses are send directly via the transport layer. | ||
93 | func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back service.Responder) bool { | ||
94 | // assemble log label | ||
95 | label := "" | ||
96 | if v := ctx.Value("label"); v != nil { | ||
97 | label = v.(string) | ||
98 | } | ||
99 | // process message | ||
100 | switch msg.(type) { | ||
101 | case *message.DHTClientPutMsg: | ||
102 | //---------------------------------------------------------- | ||
103 | // DHT PUT | ||
104 | //---------------------------------------------------------- | ||
105 | |||
106 | case *message.DHTClientGetMsg: | ||
107 | //---------------------------------------------------------- | ||
108 | // DHT GET | ||
109 | //---------------------------------------------------------- | ||
110 | |||
111 | case *message.DHTClientGetResultsKnownMsg: | ||
112 | //---------------------------------------------------------- | ||
113 | // DHT GET-RESULTS-KNOWN | ||
114 | //---------------------------------------------------------- | ||
115 | |||
116 | case *message.DHTClientGetStopMsg: | ||
117 | //---------------------------------------------------------- | ||
118 | // DHT GET-STOP | ||
119 | //---------------------------------------------------------- | ||
120 | |||
121 | case *message.DHTClientResultMsg: | ||
122 | //---------------------------------------------------------- | ||
123 | // DHT RESULT | ||
124 | //---------------------------------------------------------- | ||
125 | |||
126 | default: | ||
127 | //---------------------------------------------------------- | ||
128 | // UNKNOWN message type received | ||
129 | //---------------------------------------------------------- | ||
130 | logger.Printf(logger.ERROR, "[dht%s] Unhandled message of type (%d)\n", label, msg.Header().MsgType) | ||
131 | return false | ||
132 | } | ||
133 | return true | ||
134 | } | ||
diff --git a/src/gnunet/service/gns/block_handler.go b/src/gnunet/service/gns/block_handler.go index b05c59e..c93fca1 100644 --- a/src/gnunet/service/gns/block_handler.go +++ b/src/gnunet/service/gns/block_handler.go | |||
@@ -76,7 +76,7 @@ type BlockHandler interface { | |||
76 | // resource records in the same block. 'cm' maps the resource type | 76 | // resource records in the same block. 'cm' maps the resource type |
77 | // to an integer count (how many records of a type are present in the | 77 | // to an integer count (how many records of a type are present in the |
78 | // GNS block). | 78 | // GNS block). |
79 | Coexist(cm util.CounterMap) bool | 79 | Coexist(cm util.Counter[int]) bool |
80 | 80 | ||
81 | // Records returns a list of RR of the given types associated with | 81 | // Records returns a list of RR of the given types associated with |
82 | // the custom handler | 82 | // the custom handler |
@@ -103,7 +103,7 @@ type BlockHandler interface { | |||
103 | // BlockHandlerList is a list of block handlers instantiated. | 103 | // BlockHandlerList is a list of block handlers instantiated. |
104 | type BlockHandlerList struct { | 104 | type BlockHandlerList struct { |
105 | list map[int]BlockHandler // list of handler instances | 105 | list map[int]BlockHandler // list of handler instances |
106 | counts util.CounterMap // count number of RRs by type | 106 | counts util.Counter[int] // count number of RRs by type |
107 | } | 107 | } |
108 | 108 | ||
109 | // NewBlockHandlerList instantiates an a list of active block handlers | 109 | // NewBlockHandlerList instantiates an a list of active block handlers |
@@ -112,7 +112,7 @@ func NewBlockHandlerList(records []*message.ResourceRecord, labels []string) (*B | |||
112 | // initialize block handler list | 112 | // initialize block handler list |
113 | hl := &BlockHandlerList{ | 113 | hl := &BlockHandlerList{ |
114 | list: make(map[int]BlockHandler), | 114 | list: make(map[int]BlockHandler), |
115 | counts: make(util.CounterMap), | 115 | counts: make(util.Counter[int]), |
116 | } | 116 | } |
117 | 117 | ||
118 | // first pass: build list of shadow records in this block | 118 | // first pass: build list of shadow records in this block |
@@ -260,7 +260,7 @@ func (h *ZoneKeyHandler) AddRecord(rec *message.ResourceRecord, labels []string) | |||
260 | 260 | ||
261 | // Coexist return a flag indicating how a resource record of a given type | 261 | // Coexist return a flag indicating how a resource record of a given type |
262 | // is to be treated (see BlockHandler interface) | 262 | // is to be treated (see BlockHandler interface) |
263 | func (h *ZoneKeyHandler) Coexist(cm util.CounterMap) bool { | 263 | func (h *ZoneKeyHandler) Coexist(cm util.Counter[int]) bool { |
264 | // only one type (GNS_TYPE_PKEY) is present | 264 | // only one type (GNS_TYPE_PKEY) is present |
265 | return len(cm) == 1 && cm.Num(enums.GNS_TYPE_PKEY) == 1 | 265 | return len(cm) == 1 && cm.Num(enums.GNS_TYPE_PKEY) == 1 |
266 | } | 266 | } |
@@ -335,7 +335,7 @@ func (h *Gns2DnsHandler) AddRecord(rec *message.ResourceRecord, labels []string) | |||
335 | 335 | ||
336 | // Coexist return a flag indicating how a resource record of a given type | 336 | // Coexist return a flag indicating how a resource record of a given type |
337 | // is to be treated (see BlockHandler interface) | 337 | // is to be treated (see BlockHandler interface) |
338 | func (h *Gns2DnsHandler) Coexist(cm util.CounterMap) bool { | 338 | func (h *Gns2DnsHandler) Coexist(cm util.Counter[int]) bool { |
339 | // only one type (GNS_TYPE_GNS2DNS) is present | 339 | // only one type (GNS_TYPE_GNS2DNS) is present |
340 | return len(cm) == 1 && cm.Num(enums.GNS_TYPE_GNS2DNS) > 0 | 340 | return len(cm) == 1 && cm.Num(enums.GNS_TYPE_GNS2DNS) > 0 |
341 | } | 341 | } |
@@ -405,7 +405,7 @@ func (h *BoxHandler) AddRecord(rec *message.ResourceRecord, labels []string) err | |||
405 | 405 | ||
406 | // Coexist return a flag indicating how a resource record of a given type | 406 | // Coexist return a flag indicating how a resource record of a given type |
407 | // is to be treated (see BlockHandler interface) | 407 | // is to be treated (see BlockHandler interface) |
408 | func (h *BoxHandler) Coexist(cm util.CounterMap) bool { | 408 | func (h *BoxHandler) Coexist(cm util.Counter[int]) bool { |
409 | // anything goes... | 409 | // anything goes... |
410 | return true | 410 | return true |
411 | } | 411 | } |
@@ -469,7 +469,7 @@ func (h *LehoHandler) AddRecord(rec *message.ResourceRecord, labels []string) er | |||
469 | 469 | ||
470 | // Coexist return a flag indicating how a resource record of a given type | 470 | // Coexist return a flag indicating how a resource record of a given type |
471 | // is to be treated (see BlockHandler interface) | 471 | // is to be treated (see BlockHandler interface) |
472 | func (h *LehoHandler) Coexist(cm util.CounterMap) bool { | 472 | func (h *LehoHandler) Coexist(cm util.Counter[int]) bool { |
473 | // requires exactly one LEHO and any number of other records. | 473 | // requires exactly one LEHO and any number of other records. |
474 | return cm.Num(enums.GNS_TYPE_LEHO) == 1 | 474 | return cm.Num(enums.GNS_TYPE_LEHO) == 1 |
475 | } | 475 | } |
@@ -527,7 +527,7 @@ func (h *CnameHandler) AddRecord(rec *message.ResourceRecord, labels []string) e | |||
527 | 527 | ||
528 | // Coexist return a flag indicating how a resource record of a given type | 528 | // Coexist return a flag indicating how a resource record of a given type |
529 | // is to be treated (see BlockHandler interface) | 529 | // is to be treated (see BlockHandler interface) |
530 | func (h *CnameHandler) Coexist(cm util.CounterMap) bool { | 530 | func (h *CnameHandler) Coexist(cm util.Counter[int]) bool { |
531 | // only a single CNAME allowed | 531 | // only a single CNAME allowed |
532 | return len(cm) == 1 && cm.Num(enums.GNS_TYPE_DNS_CNAME) == 1 | 532 | return len(cm) == 1 && cm.Num(enums.GNS_TYPE_DNS_CNAME) == 1 |
533 | } | 533 | } |
@@ -581,7 +581,7 @@ func (h *VpnHandler) AddRecord(rec *message.ResourceRecord, labels []string) err | |||
581 | 581 | ||
582 | // Coexist return a flag indicating how a resource record of a given type | 582 | // Coexist return a flag indicating how a resource record of a given type |
583 | // is to be treated (see BlockHandler interface) | 583 | // is to be treated (see BlockHandler interface) |
584 | func (h *VpnHandler) Coexist(cm util.CounterMap) bool { | 584 | func (h *VpnHandler) Coexist(cm util.Counter[int]) bool { |
585 | // anything goes | 585 | // anything goes |
586 | return true | 586 | return true |
587 | } | 587 | } |
diff --git a/src/gnunet/service/gns/dns.go b/src/gnunet/service/gns/dns.go index 3bcc35e..8818ca6 100644 --- a/src/gnunet/service/gns/dns.go +++ b/src/gnunet/service/gns/dns.go | |||
@@ -19,6 +19,7 @@ | |||
19 | package gns | 19 | package gns |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | ||
22 | "fmt" | 23 | "fmt" |
23 | "net" | 24 | "net" |
24 | "strings" | 25 | "strings" |
@@ -27,7 +28,6 @@ import ( | |||
27 | "gnunet/crypto" | 28 | "gnunet/crypto" |
28 | "gnunet/enums" | 29 | "gnunet/enums" |
29 | "gnunet/message" | 30 | "gnunet/message" |
30 | "gnunet/service" | ||
31 | "gnunet/util" | 31 | "gnunet/util" |
32 | 32 | ||
33 | "github.com/bfix/gospel/logger" | 33 | "github.com/bfix/gospel/logger" |
@@ -205,7 +205,7 @@ func QueryDNS(id int, name string, server net.IP, kind RRTypeList) *message.Reco | |||
205 | // parallel; the first result delivered by any of the servers is returned | 205 | // parallel; the first result delivered by any of the servers is returned |
206 | // as the result list of matching resource records. | 206 | // as the result list of matching resource records. |
207 | func (gns *Module) ResolveDNS( | 207 | func (gns *Module) ResolveDNS( |
208 | ctx *service.SessionContext, | 208 | ctx context.Context, |
209 | name string, | 209 | name string, |
210 | servers []string, | 210 | servers []string, |
211 | kind RRTypeList, | 211 | kind RRTypeList, |
diff --git a/src/gnunet/service/gns/module.go b/src/gnunet/service/gns/module.go index 5ff81f2..31ad6c7 100644 --- a/src/gnunet/service/gns/module.go +++ b/src/gnunet/service/gns/module.go | |||
@@ -19,18 +19,22 @@ | |||
19 | package gns | 19 | package gns |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | ||
22 | "fmt" | 23 | "fmt" |
23 | "net/http" | 24 | "net/http" |
24 | "strings" | 25 | "strings" |
25 | 26 | ||
26 | "gnunet/config" | 27 | "gnunet/config" |
28 | "gnunet/core" | ||
27 | "gnunet/crypto" | 29 | "gnunet/crypto" |
28 | "gnunet/enums" | 30 | "gnunet/enums" |
29 | "gnunet/message" | 31 | "gnunet/message" |
30 | "gnunet/service" | 32 | "gnunet/service" |
33 | "gnunet/service/dht/blocks" | ||
31 | "gnunet/service/revocation" | 34 | "gnunet/service/revocation" |
32 | "gnunet/util" | 35 | "gnunet/util" |
33 | 36 | ||
37 | "github.com/bfix/gospel/data" | ||
34 | "github.com/bfix/gospel/logger" | 38 | "github.com/bfix/gospel/logger" |
35 | ) | 39 | ) |
36 | 40 | ||
@@ -45,34 +49,6 @@ var ( | |||
45 | ) | 49 | ) |
46 | 50 | ||
47 | //---------------------------------------------------------------------- | 51 | //---------------------------------------------------------------------- |
48 | // Query for simple GNS lookups | ||
49 | //---------------------------------------------------------------------- | ||
50 | |||
51 | // Query specifies the context for a basic GNS name lookup of an (atomic) | ||
52 | // label in a given zone identified by its public key. | ||
53 | type Query struct { | ||
54 | Zone *crypto.ZoneKey // Public zone key | ||
55 | Label string // Atomic label | ||
56 | Derived *crypto.ZoneKey // Derived key from (pkey,label) | ||
57 | Key *crypto.HashCode // Key for repository queries (local/remote) | ||
58 | } | ||
59 | |||
60 | // NewQuery assembles a new Query object for the given zone and label. | ||
61 | func NewQuery(zkey *crypto.ZoneKey, label string) *Query { | ||
62 | // derive a public key from (pkey,label) and set the repository | ||
63 | // key as the SHA512 hash of the binary key representation. | ||
64 | // (key blinding) | ||
65 | pd, _ := zkey.Derive(label, "gns") | ||
66 | key := crypto.Hash(pd.Bytes()) | ||
67 | return &Query{ | ||
68 | Zone: zkey, | ||
69 | Label: label, | ||
70 | Derived: pd, | ||
71 | Key: key, | ||
72 | } | ||
73 | } | ||
74 | |||
75 | //---------------------------------------------------------------------- | ||
76 | // The GNS module (recursively) resolves GNS names: | 52 | // The GNS module (recursively) resolves GNS names: |
77 | // Resolves DNS-like names (e.g. "minecraft.servers.bob.games"; a name is | 53 | // Resolves DNS-like names (e.g. "minecraft.servers.bob.games"; a name is |
78 | // a list of labels with '.' as separator) to the requested resource | 54 | // a list of labels with '.' as separator) to the requested resource |
@@ -112,25 +88,50 @@ func NewQuery(zkey *crypto.ZoneKey, label string) *Query { | |||
112 | 88 | ||
113 | // Module handles the resolution of GNS names to RRs bundled in a block. | 89 | // Module handles the resolution of GNS names to RRs bundled in a block. |
114 | type Module struct { | 90 | type Module struct { |
91 | service.ModuleImpl | ||
92 | |||
115 | // Use function references for calls to methods in other modules: | 93 | // Use function references for calls to methods in other modules: |
116 | LookupLocal func(ctx *service.SessionContext, query *Query) (*message.Block, error) | 94 | LookupLocal func(ctx context.Context, query *blocks.GNSQuery) (*blocks.GNSBlock, error) |
117 | StoreLocal func(ctx *service.SessionContext, block *message.Block) error | 95 | StoreLocal func(ctx context.Context, query *blocks.GNSQuery, block *blocks.GNSBlock) error |
118 | LookupRemote func(ctx *service.SessionContext, query *Query) (*message.Block, error) | 96 | LookupRemote func(ctx context.Context, query blocks.Query) (blocks.Block, error) |
119 | RevocationQuery func(ctx *service.SessionContext, zkey *crypto.ZoneKey) (valid bool, err error) | 97 | RevocationQuery func(ctx context.Context, zkey *crypto.ZoneKey) (valid bool, err error) |
120 | RevocationRevoke func(ctx *service.SessionContext, rd *revocation.RevData) (success bool, err error) | 98 | RevocationRevoke func(ctx context.Context, rd *revocation.RevData) (success bool, err error) |
121 | } | 99 | } |
122 | 100 | ||
123 | // RPC returns the route and handler function for a JSON-RPC request | 101 | func NewModule(ctx context.Context, c *core.Core) (m *Module) { |
124 | func (m *Module) RPC() (string, func(http.ResponseWriter, *http.Request)) { | 102 | m = &Module{ |
125 | return "/gns/", func(wrt http.ResponseWriter, req *http.Request) { | 103 | ModuleImpl: *service.NewModuleImpl(), |
126 | wrt.Write([]byte(`{"msg": "This is GNS" }`)) | ||
127 | } | 104 | } |
105 | // register as listener for core events | ||
106 | listener := m.Run(ctx, m.event, m.Filter()) | ||
107 | c.Register("gns", listener) | ||
108 | |||
109 | return | ||
110 | } | ||
111 | |||
112 | //---------------------------------------------------------------------- | ||
113 | |||
114 | // Filter returns the event filter for the service | ||
115 | func (m *Module) Filter() *core.EventFilter { | ||
116 | f := core.NewEventFilter() | ||
117 | f.AddMsgType(message.GNS_LOOKUP) | ||
118 | f.AddMsgType(message.GNS_LOOKUP_RESULT) | ||
119 | f.AddMsgType(message.GNS_REVERSE_LOOKUP) | ||
120 | f.AddMsgType(message.GNS_REVERSE_LOOKUP_RESULT) | ||
121 | return f | ||
128 | } | 122 | } |
129 | 123 | ||
124 | // Event handler | ||
125 | func (m *Module) event(ctx context.Context, ev *core.Event) { | ||
126 | |||
127 | } | ||
128 | |||
129 | //---------------------------------------------------------------------- | ||
130 | |||
130 | // Resolve a GNS name with multiple labels. If pkey is not nil, the name | 131 | // Resolve a GNS name with multiple labels. If pkey is not nil, the name |
131 | // is interpreted as "relative to current zone". | 132 | // is interpreted as "relative to current zone". |
132 | func (m *Module) Resolve( | 133 | func (m *Module) Resolve( |
133 | ctx *service.SessionContext, | 134 | ctx context.Context, |
134 | path string, | 135 | path string, |
135 | zkey *crypto.ZoneKey, | 136 | zkey *crypto.ZoneKey, |
136 | kind RRTypeList, | 137 | kind RRTypeList, |
@@ -142,7 +143,7 @@ func (m *Module) Resolve( | |||
142 | return nil, ErrGNSRecursionExceeded | 143 | return nil, ErrGNSRecursionExceeded |
143 | } | 144 | } |
144 | // get the labels in reverse order | 145 | // get the labels in reverse order |
145 | names := util.ReverseStringList(strings.Split(path, ".")) | 146 | names := util.Reverse(strings.Split(path, ".")) |
146 | logger.Printf(logger.DBG, "[gns] Resolver called for %v\n", names) | 147 | logger.Printf(logger.DBG, "[gns] Resolver called for %v\n", names) |
147 | 148 | ||
148 | // check for relative path | 149 | // check for relative path |
@@ -157,7 +158,7 @@ func (m *Module) Resolve( | |||
157 | // ResolveAbsolute resolves a fully qualified GNS absolute name | 158 | // ResolveAbsolute resolves a fully qualified GNS absolute name |
158 | // (with multiple labels). | 159 | // (with multiple labels). |
159 | func (m *Module) ResolveAbsolute( | 160 | func (m *Module) ResolveAbsolute( |
160 | ctx *service.SessionContext, | 161 | ctx context.Context, |
161 | labels []string, | 162 | labels []string, |
162 | kind RRTypeList, | 163 | kind RRTypeList, |
163 | mode int, | 164 | mode int, |
@@ -184,7 +185,7 @@ func (m *Module) ResolveAbsolute( | |||
184 | // processing simple (PKEY,Label) lookups in sequence and handle intermediate | 185 | // processing simple (PKEY,Label) lookups in sequence and handle intermediate |
185 | // GNS record types | 186 | // GNS record types |
186 | func (m *Module) ResolveRelative( | 187 | func (m *Module) ResolveRelative( |
187 | ctx *service.SessionContext, | 188 | ctx context.Context, |
188 | labels []string, | 189 | labels []string, |
189 | zkey *crypto.ZoneKey, | 190 | zkey *crypto.ZoneKey, |
190 | kind RRTypeList, | 191 | kind RRTypeList, |
@@ -200,7 +201,7 @@ func (m *Module) ResolveRelative( | |||
200 | logger.Printf(logger.DBG, "[gns] ResolveRelative '%s' in '%s'\n", labels[0], util.EncodeBinaryToString(zkey.Bytes())) | 201 | logger.Printf(logger.DBG, "[gns] ResolveRelative '%s' in '%s'\n", labels[0], util.EncodeBinaryToString(zkey.Bytes())) |
201 | 202 | ||
202 | // resolve next level | 203 | // resolve next level |
203 | var block *message.Block | 204 | var block *blocks.GNSBlock |
204 | if block, err = m.Lookup(ctx, zkey, labels[0], mode); err != nil { | 205 | if block, err = m.Lookup(ctx, zkey, labels[0], mode); err != nil { |
205 | // failed to resolve name | 206 | // failed to resolve name |
206 | return | 207 | return |
@@ -221,7 +222,7 @@ func (m *Module) ResolveRelative( | |||
221 | } | 222 | } |
222 | // post-process block by inspecting contained resource records for | 223 | // post-process block by inspecting contained resource records for |
223 | // special GNS types | 224 | // special GNS types |
224 | if records, err = block.Records(); err != nil { | 225 | if records, err = m.records(block.Body.Data); err != nil { |
225 | return | 226 | return |
226 | } | 227 | } |
227 | // assemble a list of block handlers for this block: if multiple | 228 | // assemble a list of block handlers for this block: if multiple |
@@ -238,10 +239,7 @@ func (m *Module) ResolveRelative( | |||
238 | // handle special block cases in priority order: | 239 | // handle special block cases in priority order: |
239 | //-------------------------------------------------------------- | 240 | //-------------------------------------------------------------- |
240 | 241 | ||
241 | if hdlr := hdlrs.GetHandler( | 242 | if hdlr := hdlrs.GetHandler(crypto.ZoneTypes...); hdlr != nil { |
242 | enums.GNS_TYPE_PKEY, | ||
243 | enums.GNS_TYPE_EDKEY, | ||
244 | ); hdlr != nil { | ||
245 | // (1) zone key record: | 243 | // (1) zone key record: |
246 | inst := hdlr.(*ZoneKeyHandler) | 244 | inst := hdlr.(*ZoneKeyHandler) |
247 | // if labels are pending, set new zone and continue resolution; | 245 | // if labels are pending, set new zone and continue resolution; |
@@ -267,7 +265,7 @@ func (m *Module) ResolveRelative( | |||
267 | } | 265 | } |
268 | // ... otherwise we need to handle delegation to DNS: returns a | 266 | // ... otherwise we need to handle delegation to DNS: returns a |
269 | // list of found resource records in DNS (filter by 'kind') | 267 | // list of found resource records in DNS (filter by 'kind') |
270 | lbls := strings.Join(util.ReverseStringList(labels[1:]), ".") | 268 | lbls := strings.Join(util.Reverse(labels[1:]), ".") |
271 | if len(lbls) > 0 { | 269 | if len(lbls) > 0 { |
272 | lbls += "." | 270 | lbls += "." |
273 | } | 271 | } |
@@ -361,7 +359,7 @@ func (m *Module) ResolveRelative( | |||
361 | // a PKEY TLD), it is also resolved with GNS. All other names are resolved | 359 | // a PKEY TLD), it is also resolved with GNS. All other names are resolved |
362 | // via DNS queries. | 360 | // via DNS queries. |
363 | func (m *Module) ResolveUnknown( | 361 | func (m *Module) ResolveUnknown( |
364 | ctx *service.SessionContext, | 362 | ctx context.Context, |
365 | name string, | 363 | name string, |
366 | labels []string, | 364 | labels []string, |
367 | zkey *crypto.ZoneKey, | 365 | zkey *crypto.ZoneKey, |
@@ -372,7 +370,7 @@ func (m *Module) ResolveUnknown( | |||
372 | if strings.HasSuffix(name, ".+") { | 370 | if strings.HasSuffix(name, ".+") { |
373 | // resolve server name relative to current zone | 371 | // resolve server name relative to current zone |
374 | name = strings.TrimSuffix(name, ".+") | 372 | name = strings.TrimSuffix(name, ".+") |
375 | for _, label := range util.ReverseStringList(labels) { | 373 | for _, label := range util.Reverse(labels) { |
376 | name += "." + label | 374 | name += "." + label |
377 | } | 375 | } |
378 | if set, err = m.Resolve(ctx, name, zkey, kind, enums.GNS_LO_DEFAULT, depth+1); err != nil { | 376 | if set, err = m.Resolve(ctx, name, zkey, kind, enums.GNS_LO_DEFAULT, depth+1); err != nil { |
@@ -397,7 +395,7 @@ func (m *Module) ResolveUnknown( | |||
397 | 395 | ||
398 | // GetZoneKey returns the zone key (or nil) from an absolute GNS path. | 396 | // GetZoneKey returns the zone key (or nil) from an absolute GNS path. |
399 | func (m *Module) GetZoneKey(path string) *crypto.ZoneKey { | 397 | func (m *Module) GetZoneKey(path string) *crypto.ZoneKey { |
400 | labels := util.ReverseStringList(strings.Split(path, ".")) | 398 | labels := util.Reverse(strings.Split(path, ".")) |
401 | if len(labels[0]) == 52 { | 399 | if len(labels[0]) == 52 { |
402 | if data, err := util.DecodeStringToBinary(labels[0], 32); err == nil { | 400 | if data, err := util.DecodeStringToBinary(labels[0], 32); err == nil { |
403 | if zkey, err := crypto.NewZoneKey(data); err == nil { | 401 | if zkey, err := crypto.NewZoneKey(data); err == nil { |
@@ -410,13 +408,13 @@ func (m *Module) GetZoneKey(path string) *crypto.ZoneKey { | |||
410 | 408 | ||
411 | // Lookup name in GNS. | 409 | // Lookup name in GNS. |
412 | func (m *Module) Lookup( | 410 | func (m *Module) Lookup( |
413 | ctx *service.SessionContext, | 411 | ctx context.Context, |
414 | zkey *crypto.ZoneKey, | 412 | zkey *crypto.ZoneKey, |
415 | label string, | 413 | label string, |
416 | mode int) (block *message.Block, err error) { | 414 | mode int) (block *blocks.GNSBlock, err error) { |
417 | 415 | ||
418 | // create query (lookup key) | 416 | // create query (lookup key) |
419 | query := NewQuery(zkey, label) | 417 | query := blocks.NewGNSQuery(zkey, label) |
420 | 418 | ||
421 | // try local lookup first | 419 | // try local lookup first |
422 | if block, err = m.LookupLocal(ctx, query); err != nil { | 420 | if block, err = m.LookupLocal(ctx, query); err != nil { |
@@ -427,7 +425,8 @@ func (m *Module) Lookup( | |||
427 | if block == nil { | 425 | if block == nil { |
428 | if mode == enums.GNS_LO_DEFAULT { | 426 | if mode == enums.GNS_LO_DEFAULT { |
429 | // get the block from a remote lookup | 427 | // get the block from a remote lookup |
430 | if block, err = m.LookupRemote(ctx, query); err != nil || block == nil { | 428 | var blk blocks.Block |
429 | if blk, err = m.LookupRemote(ctx, query); err != nil || blk == nil { | ||
431 | if err != nil { | 430 | if err != nil { |
432 | logger.Printf(logger.ERROR, "[gns] remote Lookup failed: %s\n", err.Error()) | 431 | logger.Printf(logger.ERROR, "[gns] remote Lookup failed: %s\n", err.Error()) |
433 | block = nil | 432 | block = nil |
@@ -437,8 +436,13 @@ func (m *Module) Lookup( | |||
437 | // lookup fails completely -- no result | 436 | // lookup fails completely -- no result |
438 | return | 437 | return |
439 | } | 438 | } |
439 | // convert to GNSBlock | ||
440 | if err = blocks.Unwrap(blk, &block); err != nil { | ||
441 | logger.Println(logger.DBG, "[gns] remote Lookup: GNS unwrap failed") | ||
442 | return | ||
443 | } | ||
440 | // store RRs from remote locally. | 444 | // store RRs from remote locally. |
441 | m.StoreLocal(ctx, block) | 445 | m.StoreLocal(ctx, query, block) |
442 | } | 446 | } |
443 | } | 447 | } |
444 | return | 448 | return |
@@ -456,3 +460,22 @@ func (m *Module) newLEHORecord(name string, expires util.AbsoluteTime) *message. | |||
456 | rr.Data[len(name)] = 0 | 460 | rr.Data[len(name)] = 0 |
457 | return rr | 461 | return rr |
458 | } | 462 | } |
463 | |||
464 | // Records returns the list of resource records from binary data. | ||
465 | func (m *Module) records(buf []byte) ([]*message.ResourceRecord, error) { | ||
466 | // parse data into record set | ||
467 | rs := message.NewRecordSet() | ||
468 | if err := data.Unmarshal(rs, buf); err != nil { | ||
469 | return nil, err | ||
470 | } | ||
471 | return rs.Records, nil | ||
472 | } | ||
473 | |||
474 | //---------------------------------------------------------------------- | ||
475 | |||
476 | // RPC returns the route and handler function for a JSON-RPC request | ||
477 | func (m *Module) RPC() (string, func(http.ResponseWriter, *http.Request)) { | ||
478 | return "/gns/", func(wrt http.ResponseWriter, req *http.Request) { | ||
479 | wrt.Write([]byte(`{"msg": "This is GNS" }`)) | ||
480 | } | ||
481 | } | ||
diff --git a/src/gnunet/service/gns/service.go b/src/gnunet/service/gns/service.go index 6fa1eb2..326d831 100644 --- a/src/gnunet/service/gns/service.go +++ b/src/gnunet/service/gns/service.go | |||
@@ -19,6 +19,7 @@ | |||
19 | package gns | 19 | package gns |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | ||
22 | "encoding/hex" | 23 | "encoding/hex" |
23 | "fmt" | 24 | "fmt" |
24 | "io" | 25 | "io" |
@@ -28,8 +29,8 @@ import ( | |||
28 | "gnunet/enums" | 29 | "gnunet/enums" |
29 | "gnunet/message" | 30 | "gnunet/message" |
30 | "gnunet/service" | 31 | "gnunet/service" |
32 | "gnunet/service/dht/blocks" | ||
31 | "gnunet/service/revocation" | 33 | "gnunet/service/revocation" |
32 | "gnunet/transport" | ||
33 | "gnunet/util" | 34 | "gnunet/util" |
34 | 35 | ||
35 | "github.com/bfix/gospel/data" | 36 | "github.com/bfix/gospel/data" |
@@ -64,115 +65,117 @@ func NewService() service.Service { | |||
64 | return inst | 65 | return inst |
65 | } | 66 | } |
66 | 67 | ||
67 | // Start the GNS service | ||
68 | func (s *Service) Start(spec string) error { | ||
69 | return nil | ||
70 | } | ||
71 | |||
72 | // Stop the GNS service | ||
73 | func (s *Service) Stop() error { | ||
74 | return nil | ||
75 | } | ||
76 | |||
77 | // ServeClient processes a client channel. | 68 | // ServeClient processes a client channel. |
78 | func (s *Service) ServeClient(ctx *service.SessionContext, mc *transport.MsgChannel) { | 69 | func (s *Service) ServeClient(ctx context.Context, id int, mc *service.Connection) { |
79 | reqID := 0 | 70 | reqID := 0 |
80 | loop: | 71 | var cancel context.CancelFunc |
72 | ctx, cancel = context.WithCancel(ctx) | ||
73 | |||
81 | for { | 74 | for { |
82 | // receive next message from client | 75 | // receive next message from client |
83 | reqID++ | 76 | reqID++ |
84 | logger.Printf(logger.DBG, "[gns:%d:%d] Waiting for client request...\n", ctx.ID, reqID) | 77 | logger.Printf(logger.DBG, "[gns:%d:%d] Waiting for client request...\n", id, reqID) |
85 | msg, err := mc.Receive(ctx.Signaller()) | 78 | msg, err := mc.Receive(ctx) |
86 | if err != nil { | 79 | if err != nil { |
87 | if err == io.EOF { | 80 | if err == io.EOF { |
88 | logger.Printf(logger.INFO, "[gns:%d:%d] Client channel closed.\n", ctx.ID, reqID) | 81 | logger.Printf(logger.INFO, "[gns:%d:%d] Client channel closed.\n", id, reqID) |
89 | } else if err == transport.ErrChannelInterrupted { | 82 | } else if err == service.ErrConnectionInterrupted { |
90 | logger.Printf(logger.INFO, "[gns:%d:%d] Service operation interrupted.\n", ctx.ID, reqID) | 83 | logger.Printf(logger.INFO, "[gns:%d:%d] Service operation interrupted.\n", id, reqID) |
91 | } else { | 84 | } else { |
92 | logger.Printf(logger.ERROR, "[gns:%d:%d] Message-receive failed: %s\n", ctx.ID, reqID, err.Error()) | 85 | logger.Printf(logger.ERROR, "[gns:%d:%d] Message-receive failed: %s\n", id, reqID, err.Error()) |
93 | } | 86 | } |
94 | break loop | 87 | break |
95 | } | 88 | } |
96 | logger.Printf(logger.INFO, "[gns:%d:%d] Received request: %v\n", ctx.ID, reqID, msg) | 89 | logger.Printf(logger.INFO, "[gns:%d:%d] Received request: %v\n", id, reqID, msg) |
97 | 90 | ||
98 | // perform lookup | 91 | // handle message |
99 | switch m := msg.(type) { | 92 | s.HandleMessage(context.WithValue(ctx, "label", fmt.Sprintf(":%d:%d", id, reqID)), msg, mc) |
100 | case *message.LookupMsg: | 93 | } |
101 | //---------------------------------------------------------- | 94 | // close client connection |
102 | // GNS_LOOKUP | 95 | mc.Close() |
103 | //---------------------------------------------------------- | 96 | |
104 | 97 | // cancel all tasks running for this session/connection | |
105 | // perform lookup on block (locally and remote) | 98 | logger.Printf(logger.INFO, "[gns:%d] Start closing session...\n", id) |
106 | go func(id int, m *message.LookupMsg) { | 99 | cancel() |
107 | logger.Printf(logger.INFO, "[gns:%d:%d] Lookup request received.\n", ctx.ID, id) | 100 | } |
108 | resp := message.NewGNSLookupResultMsg(m.ID) | 101 | |
109 | ctx.Add() | 102 | // Handle a single incoming message |
110 | defer func() { | 103 | func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back service.Responder) bool { |
111 | // send response | 104 | // assemble log label |
112 | if resp != nil { | 105 | label := "" |
113 | if err := mc.Send(resp, ctx.Signaller()); err != nil { | 106 | if v := ctx.Value("label"); v != nil { |
114 | logger.Printf(logger.ERROR, "[gns:%d:%d] Failed to send response: %s\n", ctx.ID, id, err.Error()) | 107 | label = v.(string) |
115 | } | 108 | } |
116 | } | 109 | // perform lookup |
117 | // go-routine finished | 110 | switch m := msg.(type) { |
118 | logger.Printf(logger.DBG, "[gns:%d:%d] Lookup request finished.\n", ctx.ID, id) | 111 | case *message.LookupMsg: |
119 | ctx.Remove() | 112 | //---------------------------------------------------------- |
120 | }() | 113 | // GNS_LOOKUP |
121 | 114 | //---------------------------------------------------------- | |
122 | label := m.GetName() | 115 | |
123 | kind := NewRRTypeList(int(m.Type)) | 116 | // perform lookup on block (locally and remote) |
124 | recset, err := s.Resolve(ctx, label, m.Zone, kind, int(m.Options), 0) | 117 | go func(m *message.LookupMsg) { |
125 | if err != nil { | 118 | logger.Printf(logger.INFO, "[gns%s] Lookup request received.\n", label) |
126 | logger.Printf(logger.ERROR, "[gns:%d:%d] Failed to lookup block: %s\n", ctx.ID, id, err.Error()) | 119 | resp := message.NewGNSLookupResultMsg(m.ID) |
127 | if err == transport.ErrChannelInterrupted { | 120 | defer func() { |
128 | resp = nil | 121 | // send response |
122 | if resp != nil { | ||
123 | if err := back.Send(ctx, resp); err != nil { | ||
124 | logger.Printf(logger.ERROR, "[gns%s] Failed to send response: %s\n", label, err.Error()) | ||
129 | } | 125 | } |
126 | } | ||
127 | // go-routine finished | ||
128 | logger.Printf(logger.DBG, "[gns%s] Lookup request finished.\n", label) | ||
129 | }() | ||
130 | |||
131 | label := m.GetName() | ||
132 | kind := NewRRTypeList(int(m.Type)) | ||
133 | recset, err := s.Resolve(ctx, label, m.Zone, kind, int(m.Options), 0) | ||
134 | if err != nil { | ||
135 | logger.Printf(logger.ERROR, "[gns%s] Failed to lookup block: %s\n", label, err.Error()) | ||
136 | if err == service.ErrConnectionInterrupted { | ||
137 | resp = nil | ||
138 | } | ||
139 | return | ||
140 | } | ||
141 | // handle records | ||
142 | if recset != nil { | ||
143 | logger.Printf(logger.DBG, "[gns%s] Received record set with %d entries\n", label, recset.Count) | ||
144 | |||
145 | // get records from block | ||
146 | if recset.Count == 0 { | ||
147 | logger.Printf(logger.WARN, "[gns%s] No records in block\n", label) | ||
130 | return | 148 | return |
131 | } | 149 | } |
132 | // handle records | 150 | // process records |
133 | if recset != nil { | 151 | for i, rec := range recset.Records { |
134 | logger.Printf(logger.DBG, "[gns:%d:%d] Received record set with %d entries\n", ctx.ID, id, recset.Count) | 152 | logger.Printf(logger.DBG, "[gns%s] Record #%d: %v\n", label, i, rec) |
135 | 153 | ||
136 | // get records from block | 154 | // is this the record type we are looking for? |
137 | if recset.Count == 0 { | 155 | if rec.Type == m.Type || int(m.Type) == enums.GNS_TYPE_ANY { |
138 | logger.Printf(logger.WARN, "[gns:%d:%d] No records in block\n", ctx.ID, id) | 156 | // add it to the response message |
139 | return | 157 | resp.AddRecord(rec) |
140 | } | ||
141 | // process records | ||
142 | for i, rec := range recset.Records { | ||
143 | logger.Printf(logger.DBG, "[gns:%d:%d] Record #%d: %v\n", ctx.ID, id, i, rec) | ||
144 | |||
145 | // is this the record type we are looking for? | ||
146 | if rec.Type == m.Type || int(m.Type) == enums.GNS_TYPE_ANY { | ||
147 | // add it to the response message | ||
148 | resp.AddRecord(rec) | ||
149 | } | ||
150 | } | 158 | } |
151 | } | 159 | } |
152 | }(reqID, m) | 160 | } |
153 | 161 | }(m) | |
154 | default: | ||
155 | //---------------------------------------------------------- | ||
156 | // UNKNOWN message type received | ||
157 | //---------------------------------------------------------- | ||
158 | logger.Printf(logger.ERROR, "[gns:%d:%d] Unhandled message of type (%d)\n", ctx.ID, reqID, msg.Header().MsgType) | ||
159 | break loop | ||
160 | } | ||
161 | } | ||
162 | // close client connection | ||
163 | mc.Close() | ||
164 | 162 | ||
165 | // cancel all tasks running for this session/connection | 163 | default: |
166 | logger.Printf(logger.INFO, "[gns:%d] Start closing session... [%d]\n", ctx.ID, ctx.Waiting()) | 164 | //---------------------------------------------------------- |
167 | ctx.Cancel() | 165 | // UNKNOWN message type received |
166 | //---------------------------------------------------------- | ||
167 | logger.Printf(logger.ERROR, "[gns%s] Unhandled message of type (%d)\n", label, msg.Header().MsgType) | ||
168 | return false | ||
169 | } | ||
170 | return true | ||
168 | } | 171 | } |
169 | 172 | ||
170 | //====================================================================== | 173 | //====================================================================== |
171 | // Revocationrelated methods | 174 | // Revocation-related methods |
172 | //====================================================================== | 175 | //====================================================================== |
173 | 176 | ||
174 | // QueryKeyRevocation checks if a key has been revoked | 177 | // QueryKeyRevocation checks if a key has been revoked |
175 | func (s *Service) QueryKeyRevocation(ctx *service.SessionContext, zkey *crypto.ZoneKey) (valid bool, err error) { | 178 | func (s *Service) QueryKeyRevocation(ctx context.Context, zkey *crypto.ZoneKey) (valid bool, err error) { |
176 | logger.Printf(logger.DBG, "[gns] QueryKeyRev(%s)...\n", util.EncodeBinaryToString(zkey.Bytes())) | 179 | logger.Printf(logger.DBG, "[gns] QueryKeyRev(%s)...\n", util.EncodeBinaryToString(zkey.Bytes())) |
177 | 180 | ||
178 | // assemble request | 181 | // assemble request |
@@ -180,7 +183,7 @@ func (s *Service) QueryKeyRevocation(ctx *service.SessionContext, zkey *crypto.Z | |||
180 | 183 | ||
181 | // get response from Revocation service | 184 | // get response from Revocation service |
182 | var resp message.Message | 185 | var resp message.Message |
183 | if resp, err = service.RequestResponse(ctx, "gns", "Revocation", config.Cfg.Revocation.Endpoint, req); err != nil { | 186 | if resp, err = service.RequestResponse(ctx, "gns", "Revocation", config.Cfg.Revocation.Service.Socket, req); err != nil { |
184 | return | 187 | return |
185 | } | 188 | } |
186 | 189 | ||
@@ -195,7 +198,7 @@ func (s *Service) QueryKeyRevocation(ctx *service.SessionContext, zkey *crypto.Z | |||
195 | } | 198 | } |
196 | 199 | ||
197 | // RevokeKey revokes a key with given revocation data | 200 | // RevokeKey revokes a key with given revocation data |
198 | func (s *Service) RevokeKey(ctx *service.SessionContext, rd *revocation.RevData) (success bool, err error) { | 201 | func (s *Service) RevokeKey(ctx context.Context, rd *revocation.RevData) (success bool, err error) { |
199 | logger.Printf(logger.DBG, "[gns] RevokeKey(%s)...\n", rd.ZoneKeySig.ID()) | 202 | logger.Printf(logger.DBG, "[gns] RevokeKey(%s)...\n", rd.ZoneKeySig.ID()) |
200 | 203 | ||
201 | // assemble request | 204 | // assemble request |
@@ -206,7 +209,7 @@ func (s *Service) RevokeKey(ctx *service.SessionContext, rd *revocation.RevData) | |||
206 | 209 | ||
207 | // get response from Revocation service | 210 | // get response from Revocation service |
208 | var resp message.Message | 211 | var resp message.Message |
209 | if resp, err = service.RequestResponse(ctx, "gns", "Revocation", config.Cfg.Revocation.Endpoint, req); err != nil { | 212 | if resp, err = service.RequestResponse(ctx, "gns", "Revocation", config.Cfg.Revocation.Service.Socket, req); err != nil { |
210 | return | 213 | return |
211 | } | 214 | } |
212 | 215 | ||
@@ -225,17 +228,17 @@ func (s *Service) RevokeKey(ctx *service.SessionContext, rd *revocation.RevData) | |||
225 | //====================================================================== | 228 | //====================================================================== |
226 | 229 | ||
227 | // LookupNamecache returns a cached lookup (if available) | 230 | // LookupNamecache returns a cached lookup (if available) |
228 | func (s *Service) LookupNamecache(ctx *service.SessionContext, query *Query) (block *message.Block, err error) { | 231 | func (s *Service) LookupNamecache(ctx context.Context, query *blocks.GNSQuery) (block *blocks.GNSBlock, err error) { |
229 | logger.Printf(logger.DBG, "[gns] LookupNamecache(%s)...\n", hex.EncodeToString(query.Key.Bits)) | 232 | logger.Printf(logger.DBG, "[gns] LookupNamecache(%s)...\n", hex.EncodeToString(query.Key().Bits)) |
230 | 233 | ||
231 | // assemble Namecache request | 234 | // assemble Namecache request |
232 | req := message.NewNamecacheLookupMsg(query.Key) | 235 | req := message.NewNamecacheLookupMsg(query.Key()) |
233 | req.ID = uint32(util.NextID()) | 236 | req.ID = uint32(util.NextID()) |
234 | block = nil | 237 | block = nil |
235 | 238 | ||
236 | // get response from Namecache service | 239 | // get response from Namecache service |
237 | var resp message.Message | 240 | var resp message.Message |
238 | if resp, err = service.RequestResponse(ctx, "gns", "Namecache", config.Cfg.Namecache.Endpoint, req); err != nil { | 241 | if resp, err = service.RequestResponse(ctx, "gns", "Namecache", config.Cfg.Namecache.Service.Socket, req); err != nil { |
239 | return | 242 | return |
240 | } | 243 | } |
241 | 244 | ||
@@ -250,7 +253,7 @@ func (s *Service) LookupNamecache(ctx *service.SessionContext, query *Query) (bl | |||
250 | break | 253 | break |
251 | } | 254 | } |
252 | // check if block was found | 255 | // check if block was found |
253 | if len(m.EncData) == 0 || util.IsNull(m.EncData) { | 256 | if len(m.EncData) == 0 || util.IsAll(m.EncData, 0) { |
254 | logger.Println(logger.DBG, "[gns] block not found in namecache") | 257 | logger.Println(logger.DBG, "[gns] block not found in namecache") |
255 | break | 258 | break |
256 | } | 259 | } |
@@ -262,21 +265,21 @@ func (s *Service) LookupNamecache(ctx *service.SessionContext, query *Query) (bl | |||
262 | } | 265 | } |
263 | 266 | ||
264 | // assemble GNSBlock from message | 267 | // assemble GNSBlock from message |
265 | block = new(message.Block) | 268 | block = new(blocks.GNSBlock) |
266 | block.DerivedKeySig = m.DerivedKeySig | 269 | block.DerivedKeySig = m.DerivedKeySig |
267 | sb := new(message.SignedBlockData) | 270 | sb := new(blocks.SignedGNSBlockData) |
268 | sb.Purpose = new(crypto.SignaturePurpose) | 271 | sb.Purpose = new(crypto.SignaturePurpose) |
269 | sb.Purpose.Purpose = enums.SIG_GNS_RECORD_SIGN | 272 | sb.Purpose.Purpose = enums.SIG_GNS_RECORD_SIGN |
270 | sb.Purpose.Size = uint32(16 + len(m.EncData)) | 273 | sb.Purpose.Size = uint32(16 + len(m.EncData)) |
271 | sb.Expire = m.Expire | 274 | sb.Expire = m.Expire |
272 | sb.EncData = m.EncData | 275 | sb.Data = m.EncData |
273 | block.Block = sb | 276 | block.Body = sb |
274 | 277 | ||
275 | // verify and decrypt block | 278 | // verify and decrypt block |
276 | if err = block.Verify(query.Zone, query.Label); err != nil { | 279 | if err = query.Verify(block); err != nil { |
277 | break | 280 | break |
278 | } | 281 | } |
279 | if err = block.Decrypt(query.Zone, query.Label); err != nil { | 282 | if err = query.Decrypt(block); err != nil { |
280 | break | 283 | break |
281 | } | 284 | } |
282 | default: | 285 | default: |
@@ -287,7 +290,7 @@ func (s *Service) LookupNamecache(ctx *service.SessionContext, query *Query) (bl | |||
287 | } | 290 | } |
288 | 291 | ||
289 | // StoreNamecache stores a lookup in the local namecache. | 292 | // StoreNamecache stores a lookup in the local namecache. |
290 | func (s *Service) StoreNamecache(ctx *service.SessionContext, block *message.Block) (err error) { | 293 | func (s *Service) StoreNamecache(ctx context.Context, query *blocks.GNSQuery, block *blocks.GNSBlock) (err error) { |
291 | logger.Println(logger.DBG, "[gns] StoreNamecache()...") | 294 | logger.Println(logger.DBG, "[gns] StoreNamecache()...") |
292 | 295 | ||
293 | // assemble Namecache request | 296 | // assemble Namecache request |
@@ -296,7 +299,7 @@ func (s *Service) StoreNamecache(ctx *service.SessionContext, block *message.Blo | |||
296 | 299 | ||
297 | // get response from Namecache service | 300 | // get response from Namecache service |
298 | var resp message.Message | 301 | var resp message.Message |
299 | if resp, err = service.RequestResponse(ctx, "gns", "Namecache", config.Cfg.Namecache.Endpoint, req); err != nil { | 302 | if resp, err = service.RequestResponse(ctx, "gns", "Namecache", config.Cfg.Namecache.Service.Socket, req); err != nil { |
300 | return | 303 | return |
301 | } | 304 | } |
302 | 305 | ||
@@ -327,13 +330,13 @@ func (s *Service) StoreNamecache(ctx *service.SessionContext, block *message.Blo | |||
327 | //====================================================================== | 330 | //====================================================================== |
328 | 331 | ||
329 | // LookupDHT gets a GNS block from the DHT for the given query key. | 332 | // LookupDHT gets a GNS block from the DHT for the given query key. |
330 | func (s *Service) LookupDHT(ctx *service.SessionContext, query *Query) (block *message.Block, err error) { | 333 | func (s *Service) LookupDHT(ctx context.Context, query blocks.Query) (block blocks.Block, err error) { |
331 | logger.Printf(logger.DBG, "[gns] LookupDHT(%s)...\n", hex.EncodeToString(query.Key.Bits)) | 334 | logger.Printf(logger.DBG, "[gns] LookupDHT(%s)...\n", hex.EncodeToString(query.Key().Bits)) |
332 | block = nil | 335 | block = nil |
333 | 336 | ||
334 | // client-connect to the DHT service | 337 | // client-connect to the DHT service |
335 | logger.Println(logger.DBG, "[gns] Connecting to DHT service...") | 338 | logger.Println(logger.DBG, "[gns] Connecting to DHT service...") |
336 | cl, err := service.NewClient(config.Cfg.DHT.Endpoint) | 339 | cl, err := service.NewClient(ctx, config.Cfg.DHT.Service.Socket) |
337 | if err != nil { | 340 | if err != nil { |
338 | return nil, err | 341 | return nil, err |
339 | } | 342 | } |
@@ -360,7 +363,7 @@ func (s *Service) LookupDHT(ctx *service.SessionContext, query *Query) (block *m | |||
360 | ) | 363 | ) |
361 | 364 | ||
362 | // send DHT GET request and wait for response | 365 | // send DHT GET request and wait for response |
363 | reqGet := message.NewDHTClientGetMsg(query.Key) | 366 | reqGet := message.NewDHTClientGetMsg(query.Key()) |
364 | reqGet.ID = uint64(util.NextID()) | 367 | reqGet.ID = uint64(util.NextID()) |
365 | reqGet.ReplLevel = uint32(enums.DHT_GNS_REPLICATION_LEVEL) | 368 | reqGet.ReplLevel = uint32(enums.DHT_GNS_REPLICATION_LEVEL) |
366 | reqGet.Type = uint32(enums.BLOCK_TYPE_GNS_NAMERECORD) | 369 | reqGet.Type = uint32(enums.BLOCK_TYPE_GNS_NAMERECORD) |
@@ -368,16 +371,16 @@ func (s *Service) LookupDHT(ctx *service.SessionContext, query *Query) (block *m | |||
368 | 371 | ||
369 | if err = interact(reqGet, true); err != nil { | 372 | if err = interact(reqGet, true); err != nil { |
370 | // check for aborted remote lookup: we need to cancel the query | 373 | // check for aborted remote lookup: we need to cancel the query |
371 | if err == transport.ErrChannelInterrupted { | 374 | if err == service.ErrConnectionInterrupted { |
372 | logger.Println(logger.WARN, "[gns] remote Lookup aborted -- cleaning up.") | 375 | logger.Println(logger.WARN, "[gns] remote Lookup aborted -- cleaning up.") |
373 | 376 | ||
374 | // send DHT GET_STOP request and terminate | 377 | // send DHT GET_STOP request and terminate |
375 | reqStop := message.NewDHTClientGetStopMsg(query.Key) | 378 | reqStop := message.NewDHTClientGetStopMsg(query.Key()) |
376 | reqStop.ID = reqGet.ID | 379 | reqStop.ID = reqGet.ID |
377 | if err = interact(reqStop, false); err != nil { | 380 | if err = interact(reqStop, false); err != nil { |
378 | logger.Printf(logger.ERROR, "[gns] remote Lookup abort failed: %s\n", err.Error()) | 381 | logger.Printf(logger.ERROR, "[gns] remote Lookup abort failed: %s\n", err.Error()) |
379 | } | 382 | } |
380 | return nil, transport.ErrChannelInterrupted | 383 | return nil, service.ErrConnectionInterrupted |
381 | } | 384 | } |
382 | } | 385 | } |
383 | 386 | ||
@@ -407,22 +410,24 @@ func (s *Service) LookupDHT(ctx *service.SessionContext, query *Query) (block *m | |||
407 | } | 410 | } |
408 | 411 | ||
409 | // get GNSBlock from message | 412 | // get GNSBlock from message |
410 | block = message.NewBlock() | 413 | qGNS := query.(*blocks.GNSQuery) |
414 | block = new(blocks.GNSBlock) | ||
411 | if err = data.Unmarshal(block, m.Data); err != nil { | 415 | if err = data.Unmarshal(block, m.Data); err != nil { |
412 | logger.Printf(logger.ERROR, "[gns] can't read GNS block: %s\n", err.Error()) | 416 | logger.Printf(logger.ERROR, "[gns] can't read GNS block: %s\n", err.Error()) |
413 | break | 417 | break |
414 | } | 418 | } |
419 | |||
415 | // verify and decrypt block | 420 | // verify and decrypt block |
416 | if err = block.Verify(query.Zone, query.Label); err != nil { | 421 | if err = qGNS.Verify(block); err != nil { |
417 | break | 422 | break |
418 | } | 423 | } |
419 | if err = block.Decrypt(query.Zone, query.Label); err != nil { | 424 | if err = qGNS.Decrypt(block); err != nil { |
420 | break | 425 | break |
421 | } | 426 | } |
422 | 427 | ||
423 | // we got a result from DHT that was not in the namecache, | 428 | // we got a result from DHT that was not in the namecache, |
424 | // so store it there now. | 429 | // so store it there now. |
425 | if err = s.StoreNamecache(ctx, block); err != nil { | 430 | if err = s.StoreNamecache(ctx, qGNS, block.(*blocks.GNSBlock)); err != nil { |
426 | logger.Printf(logger.ERROR, "[gns] can't store block in Namecache: %s\n", err.Error()) | 431 | logger.Printf(logger.ERROR, "[gns] can't store block in Namecache: %s\n", err.Error()) |
427 | } | 432 | } |
428 | } | 433 | } |
diff --git a/src/gnunet/service/module.go b/src/gnunet/service/module.go new file mode 100644 index 0000000..98307d6 --- /dev/null +++ b/src/gnunet/service/module.go | |||
@@ -0,0 +1,70 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package service | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "gnunet/core" | ||
24 | "net/http" | ||
25 | ) | ||
26 | |||
27 | // Module is an interface for GNUnet service modules (workers). | ||
28 | type Module interface { | ||
29 | // RPC returns the route and handler for JSON-RPC requests | ||
30 | RPC() (string, func(http.ResponseWriter, *http.Request)) | ||
31 | |||
32 | // Filter returns the event filter for the module | ||
33 | Filter() *core.EventFilter | ||
34 | } | ||
35 | |||
36 | // EventHandler is a function prototype for event handling | ||
37 | type EventHandler func(context.Context, *core.Event) | ||
38 | |||
39 | // ModuleImpl is an event-handling type used by Module implementations. | ||
40 | type ModuleImpl struct { | ||
41 | ch chan *core.Event // channel for core events. | ||
42 | } | ||
43 | |||
44 | // NewModuleImplementation returns a new base module and starts | ||
45 | func NewModuleImpl() (m *ModuleImpl) { | ||
46 | return &ModuleImpl{ | ||
47 | ch: make(chan *core.Event), | ||
48 | } | ||
49 | } | ||
50 | |||
51 | // Run event handling loop | ||
52 | func (m *ModuleImpl) Run(ctx context.Context, hdlr EventHandler, filter *core.EventFilter) (listener *core.Listener) { | ||
53 | // listener for registration | ||
54 | listener = core.NewListener(m.ch, filter) | ||
55 | // run event loop | ||
56 | go func() { | ||
57 | for { | ||
58 | select { | ||
59 | // Handle events | ||
60 | case event := <-m.ch: | ||
61 | hdlr(ctx, event) | ||
62 | |||
63 | // wait for terminate signal | ||
64 | case <-ctx.Done(): | ||
65 | return | ||
66 | } | ||
67 | } | ||
68 | }() | ||
69 | return | ||
70 | } | ||
diff --git a/src/gnunet/service/namecache/module.go b/src/gnunet/service/namecache/module.go index d5aa014..9d5bca1 100644 --- a/src/gnunet/service/namecache/module.go +++ b/src/gnunet/service/namecache/module.go | |||
@@ -19,9 +19,11 @@ | |||
19 | package namecache | 19 | package namecache |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "gnunet/message" | 22 | "context" |
23 | "gnunet/config" | ||
24 | "gnunet/core" | ||
23 | "gnunet/service" | 25 | "gnunet/service" |
24 | "gnunet/service/gns" | 26 | "gnunet/service/dht/blocks" |
25 | ) | 27 | ) |
26 | 28 | ||
27 | //====================================================================== | 29 | //====================================================================== |
@@ -34,12 +36,29 @@ import ( | |||
34 | 36 | ||
35 | // Namecache handles the transient storage of GNS blocks under the query key. | 37 | // Namecache handles the transient storage of GNS blocks under the query key. |
36 | type NamecacheModule struct { | 38 | type NamecacheModule struct { |
39 | service.ModuleImpl | ||
40 | |||
41 | cache service.DHTStore // transient block cache | ||
42 | } | ||
43 | |||
44 | // NewModule creates a new module instance. | ||
45 | func NewModule(ctx context.Context, c *core.Core) (m *NamecacheModule) { | ||
46 | m = &NamecacheModule{ | ||
47 | ModuleImpl: *service.NewModuleImpl(), | ||
48 | } | ||
49 | m.cache, _ = service.NewDHTStore(config.Cfg.Namecache.Storage) | ||
50 | return | ||
37 | } | 51 | } |
38 | 52 | ||
39 | func (nc *NamecacheModule) Get(ctx *service.SessionContext, query *gns.Query) (*message.Block, error) { | 53 | // Get an entry from the cache if available. |
40 | return nil, nil | 54 | func (m *NamecacheModule) Get(ctx context.Context, query *blocks.GNSQuery) (block *blocks.GNSBlock, err error) { |
55 | var b blocks.Block | ||
56 | b, err = m.cache.Get(query) | ||
57 | err = blocks.Unwrap(b, block) | ||
58 | return | ||
41 | } | 59 | } |
42 | 60 | ||
43 | func (nc *NamecacheModule) Put(ctx *service.SessionContext, block *message.Block) error { | 61 | // Put entry into the cache. |
44 | return nil | 62 | func (m *NamecacheModule) Put(ctx context.Context, query *blocks.GNSQuery, block *blocks.GNSBlock) error { |
63 | return m.cache.Put(query, block) | ||
45 | } | 64 | } |
diff --git a/src/gnunet/service/revocation/module.go b/src/gnunet/service/revocation/module.go index d13c069..37b57ab 100644 --- a/src/gnunet/service/revocation/module.go +++ b/src/gnunet/service/revocation/module.go | |||
@@ -19,8 +19,11 @@ | |||
19 | package revocation | 19 | package revocation |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | ||
22 | "gnunet/config" | 23 | "gnunet/config" |
24 | "gnunet/core" | ||
23 | "gnunet/crypto" | 25 | "gnunet/crypto" |
26 | "gnunet/message" | ||
24 | "gnunet/service" | 27 | "gnunet/service" |
25 | "gnunet/util" | 28 | "gnunet/util" |
26 | "net/http" | 29 | "net/http" |
@@ -33,55 +36,71 @@ import ( | |||
33 | // "GNUnet Revocation" implementation | 36 | // "GNUnet Revocation" implementation |
34 | //====================================================================== | 37 | //====================================================================== |
35 | 38 | ||
39 | // The minimum average difficulty acceptable for a set of revocation PoWs | ||
40 | const MinAvgDifficulty = 23 | ||
41 | |||
36 | // Module handles the revocation-related calls to other modules. | 42 | // Module handles the revocation-related calls to other modules. |
37 | type Module struct { | 43 | type Module struct { |
38 | bloomf *data.BloomFilter // bloomfilter for fast revocation check | 44 | service.ModuleImpl |
39 | kvs util.KeyValueStore // storage for known revocations | 45 | |
46 | bloomf *data.BloomFilter // bloomfilter for fast revocation check | ||
47 | kvs service.KVStore // storage for known revocations | ||
40 | } | 48 | } |
41 | 49 | ||
42 | // Init a revocation module | 50 | // NewModule returns an initialized revocation module |
43 | func (m *Module) Init() error { | 51 | func NewModule(ctx context.Context, c *core.Core) (m *Module) { |
44 | // Initialize access to revocation data storage | 52 | // create and init instance |
45 | var err error | 53 | m = &Module{ |
46 | if m.kvs, err = util.OpenKVStore(config.Cfg.Revocation.Storage); err != nil { | 54 | ModuleImpl: *service.NewModuleImpl(), |
47 | return err | ||
48 | } | ||
49 | // traverse the storage and build bloomfilter for all keys | ||
50 | m.bloomf = data.NewBloomFilter(1000000, 1e-8) | ||
51 | keys, err := m.kvs.List() | ||
52 | if err != nil { | ||
53 | return err | ||
54 | } | 55 | } |
55 | for _, key := range keys { | 56 | init := func() (err error) { |
56 | buf, err := util.DecodeStringToBinary(key, 32) | 57 | // Initialize access to revocation data storage |
57 | if err != nil { | 58 | if m.kvs, err = service.NewKVStore(config.Cfg.Revocation.Storage); err != nil { |
58 | return err | 59 | return |
59 | } | 60 | } |
60 | m.bloomf.Add(buf) | 61 | // traverse the storage and build bloomfilter for all keys |
62 | m.bloomf = data.NewBloomFilter(1000000, 1e-8) | ||
63 | var keys []string | ||
64 | if keys, err = m.kvs.List(); err != nil { | ||
65 | return | ||
66 | } | ||
67 | for _, key := range keys { | ||
68 | m.bloomf.Add([]byte(key)) | ||
69 | } | ||
70 | return | ||
61 | } | 71 | } |
62 | return nil | 72 | if err := init(); err != nil { |
63 | } | ||
64 | |||
65 | // NewModule returns an initialized revocation module | ||
66 | func NewModule() *Module { | ||
67 | m := new(Module) | ||
68 | if err := m.Init(); err != nil { | ||
69 | logger.Printf(logger.ERROR, "[revocation] Failed to initialize module: %s\n", err.Error()) | 73 | logger.Printf(logger.ERROR, "[revocation] Failed to initialize module: %s\n", err.Error()) |
70 | return nil | 74 | return nil |
71 | } | 75 | } |
76 | // register as listener for core events | ||
77 | listener := m.Run(ctx, m.event, m.Filter()) | ||
78 | c.Register("gns", listener) | ||
72 | return m | 79 | return m |
73 | } | 80 | } |
74 | 81 | ||
75 | // RPC returns the route and handler function for a JSON-RPC request | 82 | //---------------------------------------------------------------------- |
76 | func (m *Module) RPC() (string, func(http.ResponseWriter, *http.Request)) { | 83 | |
77 | return "/revocation/", func(wrt http.ResponseWriter, req *http.Request) { | 84 | // Filter returns the event filter for the service |
78 | wrt.Write([]byte(`{"msg": "This is REVOCATION" }`)) | 85 | func (m *Module) Filter() *core.EventFilter { |
79 | } | 86 | f := core.NewEventFilter() |
87 | f.AddMsgType(message.REVOCATION_QUERY) | ||
88 | f.AddMsgType(message.REVOCATION_QUERY_RESPONSE) | ||
89 | f.AddMsgType(message.REVOCATION_REVOKE) | ||
90 | f.AddMsgType(message.REVOCATION_REVOKE_RESPONSE) | ||
91 | return f | ||
80 | } | 92 | } |
81 | 93 | ||
94 | // Event handler | ||
95 | func (m *Module) event(ctx context.Context, ev *core.Event) { | ||
96 | |||
97 | } | ||
98 | |||
99 | //---------------------------------------------------------------------- | ||
100 | |||
82 | // Query return true if the pkey is valid (not revoked) and false | 101 | // Query return true if the pkey is valid (not revoked) and false |
83 | // if the pkey has been revoked. | 102 | // if the pkey has been revoked. |
84 | func (m *Module) Query(ctx *service.SessionContext, zkey *crypto.ZoneKey) (valid bool, err error) { | 103 | func (m *Module) Query(ctx context.Context, zkey *crypto.ZoneKey) (valid bool, err error) { |
85 | // fast check first: is the key in the bloomfilter? | 104 | // fast check first: is the key in the bloomfilter? |
86 | data := zkey.Bytes() | 105 | data := zkey.Bytes() |
87 | if !m.bloomf.Contains(data) { | 106 | if !m.bloomf.Contains(data) { |
@@ -100,9 +119,9 @@ func (m *Module) Query(ctx *service.SessionContext, zkey *crypto.ZoneKey) (valid | |||
100 | } | 119 | } |
101 | 120 | ||
102 | // Revoke a key with given revocation data | 121 | // Revoke a key with given revocation data |
103 | func (m *Module) Revoke(ctx *service.SessionContext, rd *RevData) (success bool, err error) { | 122 | func (m *Module) Revoke(ctx context.Context, rd *RevData) (success bool, err error) { |
104 | // verify the revocation data | 123 | // verify the revocation data |
105 | rc := rd.Verify(true) | 124 | diff, rc := rd.Verify(true) |
106 | switch { | 125 | switch { |
107 | case rc == -1: | 126 | case rc == -1: |
108 | logger.Println(logger.WARN, "[revocation] Revoke: Missing/invalid signature") | 127 | logger.Println(logger.WARN, "[revocation] Revoke: Missing/invalid signature") |
@@ -113,10 +132,12 @@ func (m *Module) Revoke(ctx *service.SessionContext, rd *RevData) (success bool, | |||
113 | case rc == -3: | 132 | case rc == -3: |
114 | logger.Println(logger.WARN, "[revocation] Revoke: Wrong PoW sequence order") | 133 | logger.Println(logger.WARN, "[revocation] Revoke: Wrong PoW sequence order") |
115 | return false, nil | 134 | return false, nil |
116 | case rc < 25: | 135 | } |
136 | if diff < float64(MinAvgDifficulty) { | ||
117 | logger.Println(logger.WARN, "[revocation] Revoke: Difficulty to small") | 137 | logger.Println(logger.WARN, "[revocation] Revoke: Difficulty to small") |
118 | return false, nil | 138 | return false, nil |
119 | } | 139 | } |
140 | |||
120 | // store the revocation data | 141 | // store the revocation data |
121 | // (1) add it to the bloomfilter | 142 | // (1) add it to the bloomfilter |
122 | m.bloomf.Add(rd.ZoneKeySig.KeyData) | 143 | m.bloomf.Add(rd.ZoneKeySig.KeyData) |
@@ -129,3 +150,12 @@ func (m *Module) Revoke(ctx *service.SessionContext, rd *RevData) (success bool, | |||
129 | err = m.kvs.Put(rd.ZoneKeySig.ID(), value) | 150 | err = m.kvs.Put(rd.ZoneKeySig.ID(), value) |
130 | return true, err | 151 | return true, err |
131 | } | 152 | } |
153 | |||
154 | //---------------------------------------------------------------------- | ||
155 | |||
156 | // RPC returns the route and handler function for a JSON-RPC request | ||
157 | func (m *Module) RPC() (string, func(http.ResponseWriter, *http.Request)) { | ||
158 | return "/revocation/", func(wrt http.ResponseWriter, req *http.Request) { | ||
159 | wrt.Write([]byte(`{"msg": "This is REVOCATION" }`)) | ||
160 | } | ||
161 | } | ||
diff --git a/src/gnunet/service/revocation/pow.go b/src/gnunet/service/revocation/pow.go index 04bbb13..57ddad7 100644 --- a/src/gnunet/service/revocation/pow.go +++ b/src/gnunet/service/revocation/pow.go | |||
@@ -40,6 +40,11 @@ import ( | |||
40 | // Proof-of-Work data | 40 | // Proof-of-Work data |
41 | //---------------------------------------------------------------------- | 41 | //---------------------------------------------------------------------- |
42 | 42 | ||
43 | const ( | ||
44 | // MinDifficulty for revocations -> expires in ~1year | ||
45 | MinDifficulty = 23 | ||
46 | ) | ||
47 | |||
43 | // PoWData is the proof-of-work data | 48 | // PoWData is the proof-of-work data |
44 | type PoWData struct { | 49 | type PoWData struct { |
45 | PoW uint64 `order:"big"` // start with this PoW value | 50 | PoW uint64 `order:"big"` // start with this PoW value |
@@ -143,6 +148,11 @@ func NewRevDataFromMsg(m *message.RevocationRevokeMsg) *RevData { | |||
143 | return rd | 148 | return rd |
144 | } | 149 | } |
145 | 150 | ||
151 | // Size of a serialized RevData object. | ||
152 | func (rd *RevData) Size() int { | ||
153 | return 16 + 8*len(rd.PoWs) + int(rd.ZoneKeySig.SigSize()) | ||
154 | } | ||
155 | |||
146 | // Sign the revocation data | 156 | // Sign the revocation data |
147 | func (rd *RevData) Sign(skey *crypto.ZonePrivate) (err error) { | 157 | func (rd *RevData) Sign(skey *crypto.ZonePrivate) (err error) { |
148 | sigBlock := &SignedRevData{ | 158 | sigBlock := &SignedRevData{ |
@@ -160,12 +170,10 @@ func (rd *RevData) Sign(skey *crypto.ZonePrivate) (err error) { | |||
160 | return | 170 | return |
161 | } | 171 | } |
162 | 172 | ||
163 | // Verify a revocation object: returns the (smallest) number of leading | 173 | // Verify a revocation object and return the average difficulty of the PoWs |
164 | // zero-bits in the PoWs of this revocation; a number > 0, but smaller | 174 | // in this revocation and a verification status (-1=failed signature, -2= |
165 | // than the minimum (25) indicates invalid PoWs; a value of -1 indicates | 175 | // expired revocation, -3="out-of-order" PoW sequence). |
166 | // a failed signature; -2 indicates an expired revocation and -3 for a | 176 | func (rd *RevData) Verify(withSig bool) (zbits float64, rc int) { |
167 | // "out-of-order" PoW sequence. | ||
168 | func (rd *RevData) Verify(withSig bool) int { | ||
169 | 177 | ||
170 | // (1) check signature | 178 | // (1) check signature |
171 | if withSig { | 179 | if withSig { |
@@ -179,39 +187,36 @@ func (rd *RevData) Verify(withSig bool) int { | |||
179 | } | 187 | } |
180 | sigData, err := data.Marshal(sigBlock) | 188 | sigData, err := data.Marshal(sigBlock) |
181 | if err != nil { | 189 | if err != nil { |
182 | return -1 | 190 | return 0., -1 |
183 | } | 191 | } |
184 | valid, err := rd.ZoneKeySig.Verify(sigData) | 192 | valid, err := rd.ZoneKeySig.Verify(sigData) |
185 | if err != nil || !valid { | 193 | if err != nil || !valid { |
186 | return -1 | 194 | return 0., -1 |
187 | } | 195 | } |
188 | } | 196 | } |
189 | 197 | ||
190 | // (2) check PoWs | 198 | // (2) check PoWs |
191 | var ( | 199 | var last uint64 = 0 |
192 | zbits float64 = 0 | ||
193 | last uint64 = 0 | ||
194 | ) | ||
195 | for _, pow := range rd.PoWs { | 200 | for _, pow := range rd.PoWs { |
196 | // check sequence order | 201 | // check sequence order |
197 | if pow <= last { | 202 | if pow <= last { |
198 | return -3 | 203 | return 0., -3 |
199 | } | 204 | } |
200 | last = pow | 205 | last = pow |
201 | // compute number of leading zero-bits | 206 | // compute number of leading zero-bits |
202 | work := NewPoWData(pow, rd.Timestamp, &rd.ZoneKeySig.ZoneKey) | 207 | work := NewPoWData(pow, rd.Timestamp, &rd.ZoneKeySig.ZoneKey) |
203 | zbits += float64(512 - work.Compute().BitLen()) | 208 | zbits += float64(512 - work.Compute().BitLen()) |
204 | } | 209 | } |
205 | zbits /= 32.0 | 210 | zbits /= float64(len(rd.PoWs)) |
206 | 211 | ||
207 | // (3) check expiration | 212 | // (3) check expiration |
208 | if zbits > 24.0 { | 213 | if zbits >= 23.0 { |
209 | ttl := time.Duration(int((zbits-24)*365*24)) * time.Hour | 214 | ttl := time.Duration(int((zbits-22)*365*24*1.1)) * time.Hour |
210 | if util.AbsoluteTimeNow().Add(ttl).Expired() { | 215 | if util.AbsoluteTimeNow().Add(ttl).Expired() { |
211 | return -2 | 216 | return zbits, -2 |
212 | } | 217 | } |
213 | } | 218 | } |
214 | return int(zbits) | 219 | return zbits, 0 |
215 | } | 220 | } |
216 | 221 | ||
217 | //---------------------------------------------------------------------- | 222 | //---------------------------------------------------------------------- |
@@ -240,6 +245,11 @@ func NewRevDataCalc(zkey *crypto.ZoneKey) *RevDataCalc { | |||
240 | return rd | 245 | return rd |
241 | } | 246 | } |
242 | 247 | ||
248 | // Size of a serialized RevData object. | ||
249 | func (rdc *RevDataCalc) Size() int { | ||
250 | return rdc.RevData.Size() + 2*len(rdc.Bits) + 1 | ||
251 | } | ||
252 | |||
243 | // Average number of leading zero-bits in current list | 253 | // Average number of leading zero-bits in current list |
244 | func (rdc *RevDataCalc) Average() float64 { | 254 | func (rdc *RevDataCalc) Average() float64 { |
245 | var sum uint16 = 0 | 255 | var sum uint16 = 0 |
diff --git a/src/gnunet/service/revocation/pow_test.go b/src/gnunet/service/revocation/pow_test.go index 8280402..a59f92b 100644 --- a/src/gnunet/service/revocation/pow_test.go +++ b/src/gnunet/service/revocation/pow_test.go | |||
@@ -16,7 +16,6 @@ func TestRevocationRFC(t *testing.T) { | |||
16 | var ( | 16 | var ( |
17 | D = "6fea32c05af58bfa979553d188605fd57d8bf9cc263b78d5f7478c07b998ed70" | 17 | D = "6fea32c05af58bfa979553d188605fd57d8bf9cc263b78d5f7478c07b998ed70" |
18 | ZKEY = "000100002ca223e879ecc4bbdeb5da17319281d63b2e3b6955f1c3775c804a98d5f8ddaa" | 18 | ZKEY = "000100002ca223e879ecc4bbdeb5da17319281d63b2e3b6955f1c3775c804a98d5f8ddaa" |
19 | DIFF = 7 | ||
20 | PROOF = "" + | 19 | PROOF = "" + |
21 | "0005d66da3598127" + | 20 | "0005d66da3598127" + |
22 | "0000395d1827c000" + | 21 | "0000395d1827c000" + |
@@ -142,8 +141,11 @@ func TestRevocationRFC(t *testing.T) { | |||
142 | } | 141 | } |
143 | 142 | ||
144 | // verify revocation data object | 143 | // verify revocation data object |
145 | rc := revData.Verify(true) | 144 | diff, rc := revData.Verify(true) |
146 | if rc != DIFF { | 145 | if testing.Verbose() { |
146 | t.Logf("Average difficulty of PoWs = %f\n", diff) | ||
147 | } | ||
148 | if rc != 0 { | ||
147 | t.Fatalf("REV_Verify (pkey): %d\n", rc) | 149 | t.Fatalf("REV_Verify (pkey): %d\n", rc) |
148 | } | 150 | } |
149 | } | 151 | } |
diff --git a/src/gnunet/service/revocation/service.go b/src/gnunet/service/revocation/service.go index a82e582..4d48d40 100644 --- a/src/gnunet/service/revocation/service.go +++ b/src/gnunet/service/revocation/service.go | |||
@@ -19,11 +19,12 @@ | |||
19 | package revocation | 19 | package revocation |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | ||
23 | "fmt" | ||
22 | "io" | 24 | "io" |
23 | 25 | ||
24 | "gnunet/message" | 26 | "gnunet/message" |
25 | "gnunet/service" | 27 | "gnunet/service" |
26 | "gnunet/transport" | ||
27 | 28 | ||
28 | "github.com/bfix/gospel/logger" | 29 | "github.com/bfix/gospel/logger" |
29 | ) | 30 | ) |
@@ -44,114 +45,113 @@ func NewService() service.Service { | |||
44 | return inst | 45 | return inst |
45 | } | 46 | } |
46 | 47 | ||
47 | // Start the Revocation service | ||
48 | func (s *Service) Start(spec string) error { | ||
49 | return nil | ||
50 | } | ||
51 | |||
52 | // Stop the Revocation service | ||
53 | func (s *Service) Stop() error { | ||
54 | return nil | ||
55 | } | ||
56 | |||
57 | // ServeClient processes a client channel. | 48 | // ServeClient processes a client channel. |
58 | func (s *Service) ServeClient(ctx *service.SessionContext, mc *transport.MsgChannel) { | 49 | func (s *Service) ServeClient(ctx context.Context, id int, mc *service.Connection) { |
59 | reqID := 0 | 50 | reqID := 0 |
60 | loop: | 51 | var cancel context.CancelFunc |
52 | ctx, cancel = context.WithCancel(ctx) | ||
53 | |||
61 | for { | 54 | for { |
62 | // receive next message from client | 55 | // receive next message from client |
63 | reqID++ | 56 | reqID++ |
64 | logger.Printf(logger.DBG, "[revocation:%d:%d] Waiting for client request...\n", ctx.ID, reqID) | 57 | logger.Printf(logger.DBG, "[revocation:%d:%d] Waiting for client request...\n", id, reqID) |
65 | msg, err := mc.Receive(ctx.Signaller()) | 58 | msg, err := mc.Receive(ctx) |
66 | if err != nil { | 59 | if err != nil { |
67 | if err == io.EOF { | 60 | if err == io.EOF { |
68 | logger.Printf(logger.INFO, "[revocation:%d:%d] Client channel closed.\n", ctx.ID, reqID) | 61 | logger.Printf(logger.INFO, "[revocation:%d:%d] Client channel closed.\n", id, reqID) |
69 | } else if err == transport.ErrChannelInterrupted { | 62 | } else if err == service.ErrConnectionInterrupted { |
70 | logger.Printf(logger.INFO, "[revocation:%d:%d] Service operation interrupted.\n", ctx.ID, reqID) | 63 | logger.Printf(logger.INFO, "[revocation:%d:%d] Service operation interrupted.\n", id, reqID) |
71 | } else { | 64 | } else { |
72 | logger.Printf(logger.ERROR, "[revocation:%d:%d] Message-receive failed: %s\n", ctx.ID, reqID, err.Error()) | 65 | logger.Printf(logger.ERROR, "[revocation:%d:%d] Message-receive failed: %s\n", id, reqID, err.Error()) |
73 | } | 66 | } |
74 | break loop | 67 | break |
75 | } | ||
76 | logger.Printf(logger.INFO, "[revocation:%d:%d] Received request: %v\n", ctx.ID, reqID, msg) | ||
77 | |||
78 | // handle request | ||
79 | switch m := msg.(type) { | ||
80 | case *message.RevocationQueryMsg: | ||
81 | //---------------------------------------------------------- | ||
82 | // REVOCATION_QUERY | ||
83 | //---------------------------------------------------------- | ||
84 | go func(id int, m *message.RevocationQueryMsg) { | ||
85 | logger.Printf(logger.INFO, "[revocation:%d:%d] Query request received.\n", ctx.ID, id) | ||
86 | var resp *message.RevocationQueryResponseMsg | ||
87 | ctx.Add() | ||
88 | defer func() { | ||
89 | // send response | ||
90 | if resp != nil { | ||
91 | if err := mc.Send(resp, ctx.Signaller()); err != nil { | ||
92 | logger.Printf(logger.ERROR, "[revocation:%d:%d] Failed to send response: %s\n", ctx.ID, id, err.Error()) | ||
93 | } | ||
94 | } | ||
95 | // go-routine finished | ||
96 | logger.Printf(logger.DBG, "[revocation:%d:%d] Query request finished.\n", ctx.ID, id) | ||
97 | ctx.Remove() | ||
98 | }() | ||
99 | |||
100 | valid, err := s.Query(ctx, m.Zone) | ||
101 | if err != nil { | ||
102 | logger.Printf(logger.ERROR, "[revocation:%d:%d] Failed to query revocation status: %s\n", ctx.ID, id, err.Error()) | ||
103 | if err == transport.ErrChannelInterrupted { | ||
104 | resp = nil | ||
105 | } | ||
106 | return | ||
107 | } | ||
108 | resp = message.NewRevocationQueryResponseMsg(valid) | ||
109 | }(reqID, m) | ||
110 | |||
111 | case *message.RevocationRevokeMsg: | ||
112 | //---------------------------------------------------------- | ||
113 | // REVOCATION_REVOKE | ||
114 | //---------------------------------------------------------- | ||
115 | go func(id int, m *message.RevocationRevokeMsg) { | ||
116 | logger.Printf(logger.INFO, "[revocation:%d:%d] Revoke request received.\n", ctx.ID, id) | ||
117 | var resp *message.RevocationRevokeResponseMsg | ||
118 | ctx.Add() | ||
119 | defer func() { | ||
120 | // send response | ||
121 | if resp != nil { | ||
122 | if err := mc.Send(resp, ctx.Signaller()); err != nil { | ||
123 | logger.Printf(logger.ERROR, "[revocation:%d:%d] Failed to send response: %s\n", ctx.ID, id, err.Error()) | ||
124 | } | ||
125 | } | ||
126 | // go-routine finished | ||
127 | logger.Printf(logger.DBG, "[revocation:%d:%d] Revoke request finished.\n", ctx.ID, id) | ||
128 | ctx.Remove() | ||
129 | }() | ||
130 | |||
131 | rd := NewRevDataFromMsg(m) | ||
132 | valid, err := s.Revoke(ctx, rd) | ||
133 | if err != nil { | ||
134 | logger.Printf(logger.ERROR, "[revocation:%d:%d] Failed to revoke key: %s\n", ctx.ID, id, err.Error()) | ||
135 | if err == transport.ErrChannelInterrupted { | ||
136 | resp = nil | ||
137 | } | ||
138 | return | ||
139 | } | ||
140 | resp = message.NewRevocationRevokeResponseMsg(valid) | ||
141 | }(reqID, m) | ||
142 | |||
143 | default: | ||
144 | //---------------------------------------------------------- | ||
145 | // UNKNOWN message type received | ||
146 | //---------------------------------------------------------- | ||
147 | logger.Printf(logger.ERROR, "[revocation:%d:%d] Unhandled message of type (%d)\n", ctx.ID, reqID, msg.Header().MsgType) | ||
148 | break loop | ||
149 | } | 68 | } |
69 | logger.Printf(logger.INFO, "[revocation:%d:%d] Received request: %v\n", id, reqID, msg) | ||
70 | |||
71 | // handle message | ||
72 | s.HandleMessage(context.WithValue(ctx, "label", fmt.Sprintf(":%d:%d", id, reqID)), msg, mc) | ||
150 | } | 73 | } |
151 | // close client connection | 74 | // close client connection |
152 | mc.Close() | 75 | mc.Close() |
153 | 76 | ||
154 | // cancel all tasks running for this session/connection | 77 | // cancel all tasks running for this session/connection |
155 | logger.Printf(logger.INFO, "[revocation:%d] Start closing session... [%d]\n", ctx.ID, ctx.Waiting()) | 78 | logger.Printf(logger.INFO, "[revocation:%d] Start closing session...\n", id) |
156 | ctx.Cancel() | 79 | cancel() |
80 | } | ||
81 | |||
82 | // Handle a single incoming message | ||
83 | func (s *Service) HandleMessage(ctx context.Context, msg message.Message, back service.Responder) bool { | ||
84 | // assemble log label | ||
85 | label := "" | ||
86 | if v := ctx.Value("label"); v != nil { | ||
87 | label = v.(string) | ||
88 | } | ||
89 | switch m := msg.(type) { | ||
90 | case *message.RevocationQueryMsg: | ||
91 | //---------------------------------------------------------- | ||
92 | // REVOCATION_QUERY | ||
93 | //---------------------------------------------------------- | ||
94 | go func(m *message.RevocationQueryMsg) { | ||
95 | logger.Printf(logger.INFO, "[revocation%s] Query request received.\n", label) | ||
96 | var resp *message.RevocationQueryResponseMsg | ||
97 | defer func() { | ||
98 | // send response | ||
99 | if resp != nil { | ||
100 | if err := back.Send(ctx, resp); err != nil { | ||
101 | logger.Printf(logger.ERROR, "[revocation%s] Failed to send response: %s\n", label, err.Error()) | ||
102 | } | ||
103 | } | ||
104 | // go-routine finished | ||
105 | logger.Printf(logger.DBG, "[revocation%s] Query request finished.\n", label) | ||
106 | }() | ||
107 | |||
108 | valid, err := s.Query(ctx, m.Zone) | ||
109 | if err != nil { | ||
110 | logger.Printf(logger.ERROR, "[revocation%s] Failed to query revocation status: %s\n", label, err.Error()) | ||
111 | if err == service.ErrConnectionInterrupted { | ||
112 | resp = nil | ||
113 | } | ||
114 | return | ||
115 | } | ||
116 | resp = message.NewRevocationQueryResponseMsg(valid) | ||
117 | }(m) | ||
118 | |||
119 | case *message.RevocationRevokeMsg: | ||
120 | //---------------------------------------------------------- | ||
121 | // REVOCATION_REVOKE | ||
122 | //---------------------------------------------------------- | ||
123 | go func(m *message.RevocationRevokeMsg) { | ||
124 | logger.Printf(logger.INFO, "[revocation%s] Revoke request received.\n", label) | ||
125 | var resp *message.RevocationRevokeResponseMsg | ||
126 | defer func() { | ||
127 | // send response | ||
128 | if resp != nil { | ||
129 | if err := back.Send(ctx, resp); err != nil { | ||
130 | logger.Printf(logger.ERROR, "[revocation%s] Failed to send response: %s\n", label, err.Error()) | ||
131 | } | ||
132 | } | ||
133 | // go-routine finished | ||
134 | logger.Printf(logger.DBG, "[revocation%s] Revoke request finished.\n", label) | ||
135 | }() | ||
136 | |||
137 | rd := NewRevDataFromMsg(m) | ||
138 | valid, err := s.Revoke(ctx, rd) | ||
139 | if err != nil { | ||
140 | logger.Printf(logger.ERROR, "[revocation%s] Failed to revoke key: %s\n", label, err.Error()) | ||
141 | if err == service.ErrConnectionInterrupted { | ||
142 | resp = nil | ||
143 | } | ||
144 | return | ||
145 | } | ||
146 | resp = message.NewRevocationRevokeResponseMsg(valid) | ||
147 | }(m) | ||
148 | |||
149 | default: | ||
150 | //---------------------------------------------------------- | ||
151 | // UNKNOWN message type received | ||
152 | //---------------------------------------------------------- | ||
153 | logger.Printf(logger.ERROR, "[revocation%s] Unhandled message of type (%d)\n", label, msg.Header().MsgType) | ||
154 | return false | ||
155 | } | ||
156 | return true | ||
157 | } | 157 | } |
diff --git a/src/gnunet/service/service.go b/src/gnunet/service/service.go index c996e0c..32ccf67 100644 --- a/src/gnunet/service/service.go +++ b/src/gnunet/service/service.go | |||
@@ -1,5 +1,5 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | 1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. |
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | 2 | // Copyright (C) 2019-2022 Bernd Fix >Y< |
3 | // | 3 | // |
4 | // gnunet-go is free software: you can redistribute it and/or modify it | 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 | 5 | // under the terms of the GNU Affero General Public License as published |
@@ -19,145 +19,133 @@ | |||
19 | package service | 19 | package service |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | ||
23 | "errors" | ||
22 | "fmt" | 24 | "fmt" |
23 | "net/http" | 25 | "gnunet/message" |
24 | "sync" | 26 | "gnunet/util" |
25 | |||
26 | "gnunet/transport" | ||
27 | 27 | ||
28 | "github.com/bfix/gospel/logger" | 28 | "github.com/bfix/gospel/logger" |
29 | ) | 29 | ) |
30 | 30 | ||
31 | // Module is an interface for GNUnet service modules (workers). | 31 | //---------------------------------------------------------------------- |
32 | type Module interface { | 32 | |
33 | // RPC returns the route and handler for JSON-RPC requests | 33 | // Responder is a back-channel for messages generated during |
34 | RPC() (string, func(http.ResponseWriter, *http.Request)) | 34 | // message processing. The Connection type is a responder |
35 | // and used as such in ServeClient(). | ||
36 | type Responder interface { | ||
37 | // Handle outgoing message | ||
38 | Send(ctx context.Context, msg message.Message) error | ||
39 | } | ||
40 | |||
41 | // TransportResponder is used as a responder in message handling for | ||
42 | // messages received from Transport. | ||
43 | type TransportResponder struct { | ||
44 | Peer *util.PeerID | ||
45 | SendFcn func(context.Context, *util.PeerID, message.Message) error | ||
35 | } | 46 | } |
36 | 47 | ||
37 | // Service is an interface for GNUnet services. Every service has one channel | 48 | // Send a message back to caller. |
38 | // end-point it listens to for incoming channel requests (network-based | 49 | func (r *TransportResponder) Send(ctx context.Context, msg message.Message) error { |
39 | // channels established by service clients). The end-point is specified in | 50 | if r.SendFcn == nil { |
40 | // Channel semantics in the specification string. | 51 | return errors.New("no send function defined") |
52 | } | ||
53 | return r.SendFcn(ctx, r.Peer, msg) | ||
54 | } | ||
55 | |||
56 | //---------------------------------------------------------------------- | ||
57 | |||
58 | // Service is an interface for GNUnet services | ||
41 | type Service interface { | 59 | type Service interface { |
42 | Module | 60 | Module |
43 | // Start a service on the given endpoint | 61 | |
44 | Start(spec string) error | 62 | // Serve a client session: A service has a socket it listens to for |
45 | // Serve a client session | 63 | // incoming connections (sessions) which are used for message exchange |
46 | ServeClient(ctx *SessionContext, ch *transport.MsgChannel) | 64 | // with local GNUnet services or clients. |
47 | // Stop the service | 65 | ServeClient(ctx context.Context, id int, mc *Connection) |
48 | Stop() error | 66 | |
67 | // Handle a single incoming message (either locally from a socket | ||
68 | // connection or from Transport). Response messages can be send | ||
69 | // via a Responder. Returns true if message was processed. | ||
70 | HandleMessage(ctx context.Context, msg message.Message, resp Responder) bool | ||
49 | } | 71 | } |
50 | 72 | ||
51 | // Impl is an implementation of generic service functionality. | 73 | // SocketHandler handles incoming connections on the local service socket. |
52 | type Impl struct { | 74 | // It delegates calls to ServeClient() and HandleMessage() methods |
53 | impl Service // Specific service implementation | 75 | // to a custom service 'srv'. |
54 | hdlr chan transport.Channel // Channel from listener | 76 | type SocketHandler struct { |
55 | ctrl chan bool // Control channel | 77 | srv Service // Specific service implementation |
56 | drop chan int // Channel to drop a session from pending list | 78 | hdlr chan *Connection // handler for incoming connections |
57 | srvc transport.ChannelServer // multi-user service | 79 | cmgr *ConnectionManager // manager for client connections |
58 | wg *sync.WaitGroup // wait group for go routine synchronization | 80 | name string // service name |
59 | name string // service name | ||
60 | running bool // service currently running? | ||
61 | pending map[int]*SessionContext // list of pending sessions | ||
62 | } | 81 | } |
63 | 82 | ||
64 | // NewServiceImpl instantiates a new ServiceImpl object. | 83 | // NewSocketHandler instantiates a new socket handler. |
65 | func NewServiceImpl(name string, srv Service) *Impl { | 84 | func NewSocketHandler(name string, srv Service) *SocketHandler { |
66 | return &Impl{ | 85 | return &SocketHandler{ |
67 | impl: srv, | 86 | srv: srv, |
68 | hdlr: make(chan transport.Channel), | 87 | hdlr: make(chan *Connection), |
69 | ctrl: make(chan bool), | 88 | cmgr: nil, |
70 | drop: make(chan int), | 89 | name: name, |
71 | srvc: nil, | ||
72 | wg: new(sync.WaitGroup), | ||
73 | name: name, | ||
74 | running: false, | ||
75 | pending: make(map[int]*SessionContext), | ||
76 | } | 90 | } |
77 | } | 91 | } |
78 | 92 | ||
79 | // Start a service | 93 | // Start the socket handler by listening on a Unix domain socket specified |
80 | func (si *Impl) Start(spec string) (err error) { | 94 | // by its path and additional parameters. Incoming connections from clients |
95 | // are dispatched to 'hdlr'. Stopped socket handlers can be re-started. | ||
96 | func (h *SocketHandler) Start(ctx context.Context, path string, params map[string]string) (err error) { | ||
81 | // check if we are already running | 97 | // check if we are already running |
82 | if si.running { | 98 | if h.cmgr != nil { |
83 | logger.Printf(logger.ERROR, "Service '%s' already running.\n", si.name) | 99 | logger.Printf(logger.ERROR, "Service '%s' already running.\n", h.name) |
84 | return fmt.Errorf("service already running") | 100 | return fmt.Errorf("service already running") |
85 | } | 101 | } |
86 | 102 | // start connection manager | |
87 | // start channel server | 103 | logger.Printf(logger.INFO, "[%s] Service starting.\n", h.name) |
88 | logger.Printf(logger.INFO, "[%s] Service starting.\n", si.name) | 104 | if h.cmgr, err = NewConnectionManager(ctx, path, params, h.hdlr); err != nil { |
89 | if si.srvc, err = transport.NewChannelServer(spec, si.hdlr); err != nil { | ||
90 | return | 105 | return |
91 | } | 106 | } |
92 | si.running = true | ||
93 | 107 | ||
94 | // handle clients | 108 | // handle client connections |
95 | si.wg.Add(1) | ||
96 | go func() { | 109 | go func() { |
97 | defer si.wg.Done() | ||
98 | loop: | 110 | loop: |
99 | for si.running { | 111 | for { |
100 | select { | 112 | select { |
101 | 113 | ||
102 | // handle incoming connections | 114 | // handle incoming connection |
103 | case in := <-si.hdlr: | 115 | case conn := <-h.hdlr: |
104 | if in == nil { | 116 | // run a new session with context |
105 | logger.Printf(logger.INFO, "[%s] Listener terminated.\n", si.name) | 117 | id := util.NextID() |
106 | break loop | 118 | logger.Printf(logger.INFO, "[%s] Session '%d' started.\n", h.name, id) |
107 | } | 119 | |
108 | switch ch := in.(type) { | 120 | go func() { |
109 | case transport.Channel: | 121 | // serve client on the message channel |
110 | // run a new session with context | 122 | h.srv.ServeClient(ctx, id, conn) |
111 | ctx := NewSessionContext() | 123 | // session is done now. |
112 | sessID := ctx.ID | 124 | logger.Printf(logger.INFO, "[%s] Session with client '%d' ended.\n", h.name, id) |
113 | si.pending[sessID] = ctx | 125 | }() |
114 | logger.Printf(logger.INFO, "[%s] Session '%d' started.\n", si.name, sessID) | 126 | |
115 | 127 | // handle termination | |
116 | go func() { | 128 | case <-ctx.Done(): |
117 | // serve client on the message channel | 129 | logger.Printf(logger.INFO, "[%s] Listener terminated.\n", h.name) |
118 | si.impl.ServeClient(ctx, transport.NewMsgChannel(ch)) | ||
119 | // session is done now. | ||
120 | logger.Printf(logger.INFO, "[%s] Session with client '%d' ended.\n", si.name, sessID) | ||
121 | si.drop <- sessID | ||
122 | }() | ||
123 | } | ||
124 | |||
125 | // handle session removal | ||
126 | case sessID := <-si.drop: | ||
127 | delete(si.pending, sessID) | ||
128 | |||
129 | // handle cancelation signal on listener. | ||
130 | case <-si.ctrl: | ||
131 | break loop | 130 | break loop |
132 | } | 131 | } |
133 | } | 132 | } |
134 | 133 | ||
135 | // terminate pending sessions | ||
136 | for _, ctx := range si.pending { | ||
137 | logger.Printf(logger.DBG, "[%s] Session '%d' closing...\n", si.name, ctx.ID) | ||
138 | ctx.Cancel() | ||
139 | } | ||
140 | |||
141 | // close-down service | 134 | // close-down service |
142 | logger.Printf(logger.INFO, "[%s] Service closing.\n", si.name) | 135 | logger.Printf(logger.INFO, "[%s] Service closing.\n", h.name) |
143 | si.srvc.Close() | 136 | h.cmgr.Close() |
144 | si.running = false | ||
145 | }() | 137 | }() |
146 | 138 | return nil | |
147 | return si.impl.Start(spec) | ||
148 | } | 139 | } |
149 | 140 | ||
150 | // Stop a service | 141 | // Stop socket handler. |
151 | func (si *Impl) Stop() error { | 142 | func (h *SocketHandler) Stop() error { |
152 | if !si.running { | 143 | if h.cmgr == nil { |
153 | logger.Printf(logger.WARN, "Service '%s' not running.\n", si.name) | 144 | logger.Printf(logger.WARN, "Service '%s' not running.\n", h.name) |
154 | return fmt.Errorf("service not running") | 145 | return fmt.Errorf("service not running") |
155 | } | 146 | } |
156 | si.running = false | 147 | logger.Printf(logger.INFO, "[%s] Service terminating.\n", h.name) |
157 | si.ctrl <- true | 148 | h.cmgr.Close() |
158 | logger.Printf(logger.INFO, "[%s] Service terminating.\n", si.name) | 149 | h.cmgr = nil |
159 | 150 | return nil | |
160 | err := si.impl.Stop() | ||
161 | si.wg.Wait() | ||
162 | return err | ||
163 | } | 151 | } |
diff --git a/src/gnunet/service/store.go b/src/gnunet/service/store.go new file mode 100644 index 0000000..1e5af8b --- /dev/null +++ b/src/gnunet/service/store.go | |||
@@ -0,0 +1,379 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package service | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "database/sql" | ||
24 | "encoding/binary" | ||
25 | "encoding/hex" | ||
26 | "errors" | ||
27 | "fmt" | ||
28 | "gnunet/crypto" | ||
29 | "gnunet/service/dht/blocks" | ||
30 | "gnunet/util" | ||
31 | "io/ioutil" | ||
32 | "os" | ||
33 | "strconv" | ||
34 | "strings" | ||
35 | |||
36 | redis "github.com/go-redis/redis/v8" | ||
37 | ) | ||
38 | |||
39 | // Error messages related to the key/value-store implementations | ||
40 | var ( | ||
41 | ErrStoreInvalidSpec = fmt.Errorf("Invalid Store specification") | ||
42 | ErrStoreUnknown = fmt.Errorf("Unknown Store type") | ||
43 | ErrStoreNotAvailable = fmt.Errorf("Store not available") | ||
44 | ) | ||
45 | |||
46 | //------------------------------------------------------------ | ||
47 | // Generic storage interface. Can be used for persistent or | ||
48 | // transient (caching) storage of key/value data. | ||
49 | // One set of methods (Get/Put) work on DHT queries and blocks, | ||
50 | // the other set (GetS, PutS) work on key/value strings. | ||
51 | // Each custom implementation can decide which sets to support. | ||
52 | //------------------------------------------------------------ | ||
53 | |||
54 | // Store is a key/value storage where the type of the key is either | ||
55 | // a SHA512 hash value or a string and the value is either a DHT | ||
56 | // block or a string. | ||
57 | type Store[K, V any] interface { | ||
58 | // Put block into storage under given key | ||
59 | Put(key K, val V) error | ||
60 | |||
61 | // Get block with given key from storage | ||
62 | Get(key K) (V, error) | ||
63 | |||
64 | // List all store queries | ||
65 | List() ([]K, error) | ||
66 | } | ||
67 | |||
68 | //------------------------------------------------------------ | ||
69 | // Types for custom store requirements | ||
70 | //------------------------------------------------------------ | ||
71 | |||
72 | // DHTStore for DHT queries and blocks | ||
73 | type DHTStore Store[blocks.Query, blocks.Block] | ||
74 | |||
75 | // KVStore for key/value string pairs | ||
76 | type KVStore Store[string, string] | ||
77 | |||
78 | //------------------------------------------------------------ | ||
79 | // NewDHTStore creates a new storage handler with given spec | ||
80 | // for use with DHT queries and blocks | ||
81 | func NewDHTStore(spec string) (DHTStore, error) { | ||
82 | specs := strings.SplitN(spec, ":", 2) | ||
83 | if len(specs) < 2 { | ||
84 | return nil, ErrStoreInvalidSpec | ||
85 | } | ||
86 | switch specs[0] { | ||
87 | //------------------------------------------------------------------ | ||
88 | // File-base storage | ||
89 | //------------------------------------------------------------------ | ||
90 | case "file_store": | ||
91 | return NewFileStore(specs[1]) | ||
92 | case "file_cache": | ||
93 | if len(specs) < 3 { | ||
94 | return nil, ErrStoreInvalidSpec | ||
95 | } | ||
96 | return NewFileCache(specs[1], specs[2]) | ||
97 | } | ||
98 | return nil, ErrStoreUnknown | ||
99 | } | ||
100 | |||
101 | //------------------------------------------------------------ | ||
102 | // NewKVStore creates a new storage handler with given spec | ||
103 | // for use with key/value string pairs. | ||
104 | func NewKVStore(spec string) (KVStore, error) { | ||
105 | specs := strings.SplitN(spec, ":", 2) | ||
106 | if len(specs) < 2 { | ||
107 | return nil, ErrStoreInvalidSpec | ||
108 | } | ||
109 | switch specs[0] { | ||
110 | //-------------------------------------------------------------- | ||
111 | // Redis service | ||
112 | //-------------------------------------------------------------- | ||
113 | case "redis": | ||
114 | if len(specs) < 4 { | ||
115 | return nil, ErrStoreInvalidSpec | ||
116 | } | ||
117 | return NewRedisStore(specs[1], specs[2], specs[3]) | ||
118 | |||
119 | //-------------------------------------------------------------- | ||
120 | // SQL database service | ||
121 | //-------------------------------------------------------------- | ||
122 | case "sql": | ||
123 | if len(specs) < 4 { | ||
124 | return nil, ErrStoreInvalidSpec | ||
125 | } | ||
126 | return NewSQLStore(specs[1]) | ||
127 | } | ||
128 | return nil, errors.New("unknown storage mechanism") | ||
129 | } | ||
130 | |||
131 | //------------------------------------------------------------ | ||
132 | // Filesystem-based storage | ||
133 | //------------------------------------------------------------ | ||
134 | |||
135 | // FileStore implements a filesystem-based storage mechanism for | ||
136 | // DHT queries and blocks. | ||
137 | type FileStore struct { | ||
138 | path string // storage path | ||
139 | cached []*crypto.HashCode // list of cached entries (key) | ||
140 | wrPos int // write position in cyclic list | ||
141 | } | ||
142 | |||
143 | // NewFileStore instantiates a new file storage. | ||
144 | func NewFileStore(path string) (DHTStore, error) { | ||
145 | // create file store | ||
146 | return &FileStore{ | ||
147 | path: path, | ||
148 | }, nil | ||
149 | } | ||
150 | |||
151 | // NewFileCache instantiates a new file-based cache. | ||
152 | func NewFileCache(path, param string) (DHTStore, error) { | ||
153 | // remove old cache content | ||
154 | os.RemoveAll(path) | ||
155 | |||
156 | // get number of cache entries | ||
157 | num, err := strconv.ParseUint(param, 10, 32) | ||
158 | if err != nil { | ||
159 | return nil, err | ||
160 | } | ||
161 | // create file store | ||
162 | return &FileStore{ | ||
163 | path: path, | ||
164 | cached: make([]*crypto.HashCode, num), | ||
165 | wrPos: 0, | ||
166 | }, nil | ||
167 | } | ||
168 | |||
169 | // Put block into storage under given key | ||
170 | func (s *FileStore) Put(query blocks.Query, block blocks.Block) (err error) { | ||
171 | // get query parameters for entry | ||
172 | var ( | ||
173 | btype uint16 // block type | ||
174 | expire util.AbsoluteTime // block expiration | ||
175 | ) | ||
176 | query.Get("blkType", &btype) | ||
177 | query.Get("expire", &expire) | ||
178 | |||
179 | // are we in caching mode? | ||
180 | if s.cached != nil { | ||
181 | // release previous block if defined | ||
182 | if key := s.cached[s.wrPos]; key != nil { | ||
183 | // get path and filename from key | ||
184 | path, fname := s.expandPath(key) | ||
185 | if err = os.Remove(path + "/" + fname); err != nil { | ||
186 | return | ||
187 | } | ||
188 | // free slot | ||
189 | s.cached[s.wrPos] = nil | ||
190 | } | ||
191 | } | ||
192 | // get path and filename from key | ||
193 | path, fname := s.expandPath(query.Key()) | ||
194 | // make sure the path exists | ||
195 | if err = os.MkdirAll(path, 0755); err != nil { | ||
196 | return | ||
197 | } | ||
198 | // write to file for storage | ||
199 | var fp *os.File | ||
200 | if fp, err = os.Create(path + "/" + fname); err == nil { | ||
201 | defer fp.Close() | ||
202 | // write block data | ||
203 | if err = binary.Write(fp, binary.BigEndian, btype); err == nil { | ||
204 | if err = binary.Write(fp, binary.BigEndian, expire); err == nil { | ||
205 | _, err = fp.Write(block.Data()) | ||
206 | } | ||
207 | } | ||
208 | } | ||
209 | // update cache list | ||
210 | if s.cached != nil { | ||
211 | s.cached[s.wrPos] = query.Key() | ||
212 | s.wrPos = (s.wrPos + 1) % len(s.cached) | ||
213 | } | ||
214 | return | ||
215 | } | ||
216 | |||
217 | // Get block with given key from storage | ||
218 | func (s *FileStore) Get(query blocks.Query) (block blocks.Block, err error) { | ||
219 | // get requested block type | ||
220 | var ( | ||
221 | btype uint16 = blocks.DHT_BLOCK_ANY | ||
222 | blkt uint16 // actual block type | ||
223 | expire util.AbsoluteTime // expiration date | ||
224 | data []byte // block data | ||
225 | ) | ||
226 | query.Get("blkType", &btype) | ||
227 | |||
228 | // get path and filename from key | ||
229 | path, fname := s.expandPath(query.Key()) | ||
230 | // read file content (block data) | ||
231 | var file *os.File | ||
232 | if file, err = os.Open(path + "/" + fname); err != nil { | ||
233 | return | ||
234 | } | ||
235 | // read block data | ||
236 | if err = binary.Read(file, binary.BigEndian, &blkt); err == nil { | ||
237 | if btype != blocks.DHT_BLOCK_ANY && btype != blkt { | ||
238 | // block types not matching | ||
239 | return | ||
240 | } | ||
241 | if err = binary.Read(file, binary.BigEndian, &expire); err == nil { | ||
242 | if data, err = ioutil.ReadAll(file); err == nil { | ||
243 | block = blocks.NewGenericBlock(data) | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | return | ||
248 | } | ||
249 | |||
250 | // Get a list of all stored block keys (generic query). | ||
251 | func (s *FileStore) List() ([]blocks.Query, error) { | ||
252 | return make([]blocks.Query, 0), nil | ||
253 | } | ||
254 | |||
255 | // expandPath returns the full path to the file for given key. | ||
256 | func (s *FileStore) expandPath(key *crypto.HashCode) (string, string) { | ||
257 | h := hex.EncodeToString(key.Bits) | ||
258 | return fmt.Sprintf("%s/%s/%s", s.path, h[:2], h[2:4]), h[4:] | ||
259 | } | ||
260 | |||
261 | //------------------------------------------------------------ | ||
262 | // Redis: only use for caching purposes on key/value strings | ||
263 | //------------------------------------------------------------ | ||
264 | |||
265 | // RedisStore uses a (local) Redis server for key/value storage | ||
266 | type RedisStore struct { | ||
267 | client *redis.Client // client connection | ||
268 | db int // index to database | ||
269 | } | ||
270 | |||
271 | // NewRedisStore creates a Redis service client instance. | ||
272 | func NewRedisStore(addr, passwd, db string) (s KVStore, err error) { | ||
273 | kvs := new(RedisStore) | ||
274 | if kvs.db, err = strconv.Atoi(db); err != nil { | ||
275 | err = ErrStoreInvalidSpec | ||
276 | return | ||
277 | } | ||
278 | kvs.client = redis.NewClient(&redis.Options{ | ||
279 | Addr: addr, | ||
280 | Password: passwd, | ||
281 | DB: kvs.db, | ||
282 | }) | ||
283 | if kvs.client == nil { | ||
284 | err = ErrStoreNotAvailable | ||
285 | } | ||
286 | s = kvs | ||
287 | return | ||
288 | } | ||
289 | |||
290 | // Put block into storage under given key | ||
291 | func (s *RedisStore) Put(key string, value string) (err error) { | ||
292 | return s.client.Set(context.TODO(), key, value, 0).Err() | ||
293 | } | ||
294 | |||
295 | // Get block with given key from storage | ||
296 | func (s *RedisStore) Get(key string) (value string, err error) { | ||
297 | return s.client.Get(context.TODO(), key).Result() | ||
298 | } | ||
299 | |||
300 | // List all keys in store | ||
301 | func (s *RedisStore) List() (keys []string, err error) { | ||
302 | var ( | ||
303 | crs uint64 | ||
304 | segm []string | ||
305 | ctx = context.TODO() | ||
306 | ) | ||
307 | keys = make([]string, 0) | ||
308 | for { | ||
309 | segm, crs, err = s.client.Scan(ctx, crs, "*", 10).Result() | ||
310 | if err != nil { | ||
311 | return | ||
312 | } | ||
313 | if crs == 0 { | ||
314 | break | ||
315 | } | ||
316 | keys = append(keys, segm...) | ||
317 | } | ||
318 | return | ||
319 | } | ||
320 | |||
321 | //------------------------------------------------------------ | ||
322 | // SQL-based key-value-store | ||
323 | //------------------------------------------------------------ | ||
324 | |||
325 | // SQLStore for generic SQL database handling | ||
326 | type SQLStore struct { | ||
327 | db *util.DbConn | ||
328 | } | ||
329 | |||
330 | // NewSQLStore creates a new SQL-based key/value store. | ||
331 | func NewSQLStore(spec string) (s KVStore, err error) { | ||
332 | kvs := new(SQLStore) | ||
333 | |||
334 | // connect to SQL database | ||
335 | kvs.db, err = util.DbPool.Connect(spec) | ||
336 | if err != nil { | ||
337 | return nil, err | ||
338 | } | ||
339 | // get number of key/value pairs (as a check for existing table) | ||
340 | row := kvs.db.QueryRow("select count(*) from store") | ||
341 | var num int | ||
342 | if row.Scan(&num) != nil { | ||
343 | return nil, ErrStoreNotAvailable | ||
344 | } | ||
345 | return kvs, nil | ||
346 | |||
347 | } | ||
348 | |||
349 | // Put a key/value pair into the store | ||
350 | func (s *SQLStore) Put(key string, value string) error { | ||
351 | _, err := s.db.Exec("insert into store(key,value) values(?,?)", key, value) | ||
352 | return err | ||
353 | } | ||
354 | |||
355 | // Get a value for a given key from store | ||
356 | func (s *SQLStore) Get(key string) (value string, err error) { | ||
357 | row := s.db.QueryRow("select value from store where key=?", key) | ||
358 | err = row.Scan(&value) | ||
359 | return | ||
360 | } | ||
361 | |||
362 | // List all keys in store | ||
363 | func (s *SQLStore) List() (keys []string, err error) { | ||
364 | var ( | ||
365 | rows *sql.Rows | ||
366 | key string | ||
367 | ) | ||
368 | keys = make([]string, 0) | ||
369 | rows, err = s.db.Query("select key from store") | ||
370 | if err == nil { | ||
371 | for rows.Next() { | ||
372 | if err = rows.Scan(&key); err != nil { | ||
373 | break | ||
374 | } | ||
375 | keys = append(keys, key) | ||
376 | } | ||
377 | } | ||
378 | return | ||
379 | } | ||
diff --git a/src/gnunet/test.sh b/src/gnunet/test.sh new file mode 100755 index 0000000..78495a8 --- /dev/null +++ b/src/gnunet/test.sh | |||
@@ -0,0 +1,3 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | go test $* -gcflags "-N -l" ./... | ||
diff --git a/src/gnunet/transport/channel.go b/src/gnunet/transport/channel.go deleted file mode 100644 index 1632aab..0000000 --- a/src/gnunet/transport/channel.go +++ /dev/null | |||
@@ -1,213 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package transport | ||
20 | |||
21 | import ( | ||
22 | "encoding/hex" | ||
23 | "errors" | ||
24 | "fmt" | ||
25 | "path" | ||
26 | "strings" | ||
27 | |||
28 | "gnunet/message" | ||
29 | "gnunet/util" | ||
30 | |||
31 | "github.com/bfix/gospel/concurrent" | ||
32 | "github.com/bfix/gospel/data" | ||
33 | "github.com/bfix/gospel/logger" | ||
34 | ) | ||
35 | |||
36 | // Error codes | ||
37 | var ( | ||
38 | ErrChannelNotImplemented = fmt.Errorf("protocol not implemented") | ||
39 | ErrChannelNotOpened = fmt.Errorf("channel not opened") | ||
40 | ErrChannelInterrupted = fmt.Errorf("channel interrupted") | ||
41 | ) | ||
42 | |||
43 | //////////////////////////////////////////////////////////////////////// | ||
44 | // CHANNEL | ||
45 | |||
46 | // Channel is an abstraction for exchanging arbitrary data over various | ||
47 | // transport protocols and mechanisms. They are created by clients via | ||
48 | // 'NewChannel()' or by services run via 'NewChannelServer()'. | ||
49 | // A string specifies the end-point of the channel: | ||
50 | // "unix+/tmp/test.sock" -- for UDS channels | ||
51 | // "tcp+1.2.3.4:5" -- for TCP channels | ||
52 | // "udp+1.2.3.4:5" -- for UDP channels | ||
53 | type Channel interface { | ||
54 | Open(spec string) error // open channel (for read/write) | ||
55 | Close() error // close open channel | ||
56 | IsOpen() bool // check if channel is open | ||
57 | Read([]byte, *concurrent.Signaller) (int, error) // read from channel | ||
58 | Write([]byte, *concurrent.Signaller) (int, error) // write to channel | ||
59 | } | ||
60 | |||
61 | // ChannelFactory instantiates specific Channel imülementations. | ||
62 | type ChannelFactory func() Channel | ||
63 | |||
64 | // Known channel implementations. | ||
65 | var channelImpl = map[string]ChannelFactory{ | ||
66 | "unix": NewSocketChannel, | ||
67 | "tcp": NewTCPChannel, | ||
68 | "udp": NewUDPChannel, | ||
69 | } | ||
70 | |||
71 | // NewChannel creates a new channel to the specified endpoint. | ||
72 | // Called by a client to connect to a service. | ||
73 | func NewChannel(spec string) (Channel, error) { | ||
74 | parts := strings.Split(spec, "+") | ||
75 | if fac, ok := channelImpl[parts[0]]; ok { | ||
76 | inst := fac() | ||
77 | err := inst.Open(spec) | ||
78 | return inst, err | ||
79 | } | ||
80 | return nil, ErrChannelNotImplemented | ||
81 | } | ||
82 | |||
83 | //////////////////////////////////////////////////////////////////////// | ||
84 | // CHANNEL SERVER | ||
85 | |||
86 | // ChannelServer creates a listener for the specified endpoint. | ||
87 | // The specification string has the same format as for Channel with slightly | ||
88 | // different semantics (for TCP, and ICMP the address specifies is a mask | ||
89 | // for client addresses accepted for a channel request). | ||
90 | type ChannelServer interface { | ||
91 | Open(spec string, hdlr chan<- Channel) error | ||
92 | Close() error | ||
93 | } | ||
94 | |||
95 | // ChannelServerFactory instantiates specific ChannelServer imülementations. | ||
96 | type ChannelServerFactory func() ChannelServer | ||
97 | |||
98 | // Known channel server implementations. | ||
99 | var channelServerImpl = map[string]ChannelServerFactory{ | ||
100 | "unix": NewSocketChannelServer, | ||
101 | "tcp": NewTCPChannelServer, | ||
102 | "udp": NewUDPChannelServer, | ||
103 | } | ||
104 | |||
105 | // NewChannelServer creates a new channel server instance | ||
106 | func NewChannelServer(spec string, hdlr chan<- Channel) (cs ChannelServer, err error) { | ||
107 | parts := strings.Split(spec, "+") | ||
108 | |||
109 | if fac, ok := channelServerImpl[parts[0]]; ok { | ||
110 | // check if the basedir for the spec exists... | ||
111 | if err = util.EnforceDirExists(path.Dir(parts[1])); err != nil { | ||
112 | return | ||
113 | } | ||
114 | // instantiate server implementation | ||
115 | cs = fac() | ||
116 | // create the domain socket and listen to it. | ||
117 | err = cs.Open(spec, hdlr) | ||
118 | return | ||
119 | } | ||
120 | return nil, ErrChannelNotImplemented | ||
121 | } | ||
122 | |||
123 | //////////////////////////////////////////////////////////////////////// | ||
124 | // MESSAGE CHANNEL | ||
125 | |||
126 | // MsgChannel s a wrapper around a generic channel for GNUnet message exchange. | ||
127 | type MsgChannel struct { | ||
128 | ch Channel | ||
129 | buf []byte | ||
130 | } | ||
131 | |||
132 | // NewMsgChannel wraps a plain Channel for GNUnet message exchange. | ||
133 | func NewMsgChannel(ch Channel) *MsgChannel { | ||
134 | return &MsgChannel{ | ||
135 | ch: ch, | ||
136 | buf: make([]byte, 65536), | ||
137 | } | ||
138 | } | ||
139 | |||
140 | // Close a MsgChannel by closing the wrapped plain Channel. | ||
141 | func (c *MsgChannel) Close() error { | ||
142 | return c.ch.Close() | ||
143 | } | ||
144 | |||
145 | // Send a GNUnet message over a channel. | ||
146 | func (c *MsgChannel) Send(msg message.Message, sig *concurrent.Signaller) error { | ||
147 | // convert message to binary data | ||
148 | data, err := data.Marshal(msg) | ||
149 | if err != nil { | ||
150 | return err | ||
151 | } | ||
152 | logger.Printf(logger.DBG, "==> %v\n", msg) | ||
153 | logger.Printf(logger.DBG, " [%s]\n", hex.EncodeToString(data)) | ||
154 | |||
155 | // check message header size and packet size | ||
156 | mh, err := message.GetMsgHeader(data) | ||
157 | if err != nil { | ||
158 | return err | ||
159 | } | ||
160 | if len(data) != int(mh.MsgSize) { | ||
161 | return errors.New("send: message size mismatch") | ||
162 | } | ||
163 | |||
164 | // send packet | ||
165 | n, err := c.ch.Write(data, sig) | ||
166 | if err != nil { | ||
167 | return err | ||
168 | } | ||
169 | if n != len(data) { | ||
170 | return errors.New("incomplete send") | ||
171 | } | ||
172 | return nil | ||
173 | } | ||
174 | |||
175 | // Receive GNUnet messages over a plain Channel. | ||
176 | func (c *MsgChannel) Receive(sig *concurrent.Signaller) (message.Message, error) { | ||
177 | // get bytes from channel | ||
178 | get := func(pos, count int) error { | ||
179 | n, err := c.ch.Read(c.buf[pos:pos+count], sig) | ||
180 | if err != nil { | ||
181 | return err | ||
182 | } | ||
183 | if n != count { | ||
184 | return errors.New("not enough bytes on network") | ||
185 | } | ||
186 | return nil | ||
187 | } | ||
188 | |||
189 | if err := get(0, 4); err != nil { | ||
190 | return nil, err | ||
191 | } | ||
192 | mh, err := message.GetMsgHeader(c.buf[:4]) | ||
193 | if err != nil { | ||
194 | return nil, err | ||
195 | } | ||
196 | |||
197 | if err := get(4, int(mh.MsgSize)-4); err != nil { | ||
198 | return nil, err | ||
199 | } | ||
200 | msg, err := message.NewEmptyMessage(mh.MsgType) | ||
201 | if err != nil { | ||
202 | return nil, err | ||
203 | } | ||
204 | if msg == nil { | ||
205 | return nil, fmt.Errorf("message{%d} is nil", mh.MsgType) | ||
206 | } | ||
207 | if err = data.Unmarshal(msg, c.buf[:mh.MsgSize]); err != nil { | ||
208 | return nil, err | ||
209 | } | ||
210 | logger.Printf(logger.DBG, "<== %v\n", msg) | ||
211 | logger.Printf(logger.DBG, " [%s]\n", hex.EncodeToString(c.buf[:mh.MsgSize])) | ||
212 | return msg, nil | ||
213 | } | ||
diff --git a/src/gnunet/transport/channel_netw.go b/src/gnunet/transport/channel_netw.go deleted file mode 100644 index c0de978..0000000 --- a/src/gnunet/transport/channel_netw.go +++ /dev/null | |||
@@ -1,285 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package transport | ||
20 | |||
21 | import ( | ||
22 | "net" | ||
23 | "os" | ||
24 | "strconv" | ||
25 | "strings" | ||
26 | |||
27 | "github.com/bfix/gospel/concurrent" | ||
28 | "github.com/bfix/gospel/logger" | ||
29 | ) | ||
30 | |||
31 | // ChannelResult for read/write operations on channels. | ||
32 | type ChannelResult struct { | ||
33 | count int // number of bytes read/written | ||
34 | err error // error (or nil) | ||
35 | } | ||
36 | |||
37 | // NewChannelResult instanciates a new object with given attributes. | ||
38 | func NewChannelResult(n int, err error) *ChannelResult { | ||
39 | return &ChannelResult{ | ||
40 | count: n, | ||
41 | err: err, | ||
42 | } | ||
43 | } | ||
44 | |||
45 | // Values returns the attributes of a result instance (for passing up the | ||
46 | // call stack). | ||
47 | func (cr *ChannelResult) Values() (int, error) { | ||
48 | return cr.count, cr.err | ||
49 | } | ||
50 | |||
51 | //////////////////////////////////////////////////////////////////////// | ||
52 | // Generic network-based Channel | ||
53 | |||
54 | // NetworkChannel represents a network-based channel | ||
55 | type NetworkChannel struct { | ||
56 | network string // network protocol identifier ("tcp", "unix", ...) | ||
57 | conn net.Conn // associated connection | ||
58 | } | ||
59 | |||
60 | // NewNetworkChannel creates a new channel for a given network protocol. | ||
61 | // The channel is in pending state and need to be opened before use. | ||
62 | func NewNetworkChannel(netw string) Channel { | ||
63 | return &NetworkChannel{ | ||
64 | network: netw, | ||
65 | conn: nil, | ||
66 | } | ||
67 | } | ||
68 | |||
69 | // Open a network channel based on specification: | ||
70 | // The specification is a string separated into parts by the '+' delimiter | ||
71 | // (e.g. "unix+/tmp/gnunet-service-gns-go.sock+perm=0770"). The network | ||
72 | // identifier (first part) must match the network specification of the | ||
73 | // underlaying NetworkChannel instance. | ||
74 | func (c *NetworkChannel) Open(spec string) (err error) { | ||
75 | parts := strings.Split(spec, "+") | ||
76 | // check for correct protocol | ||
77 | if parts[0] != c.network { | ||
78 | return ErrChannelNotImplemented | ||
79 | } | ||
80 | // open connection | ||
81 | c.conn, err = net.Dial(c.network, parts[1]) | ||
82 | return | ||
83 | } | ||
84 | |||
85 | // Close a network channel | ||
86 | func (c *NetworkChannel) Close() error { | ||
87 | if c.conn != nil { | ||
88 | rc := c.conn.Close() | ||
89 | c.conn = nil | ||
90 | return rc | ||
91 | } | ||
92 | return ErrChannelNotOpened | ||
93 | } | ||
94 | |||
95 | // IsOpen returns true if the channel is opened | ||
96 | func (c *NetworkChannel) IsOpen() bool { | ||
97 | return c.conn != nil | ||
98 | } | ||
99 | |||
100 | // Read bytes from a network channel into buffer: Returns the number of read | ||
101 | // bytes and an error code. Only works on open channels ;) | ||
102 | // The read can be aborted by sending 'true' on the cmd interface; the | ||
103 | // channel is closed after such interruption. | ||
104 | func (c *NetworkChannel) Read(buf []byte, sig *concurrent.Signaller) (int, error) { | ||
105 | // check if the channel is open | ||
106 | if c.conn == nil { | ||
107 | return 0, ErrChannelNotOpened | ||
108 | } | ||
109 | // perform operation in go-routine | ||
110 | result := make(chan *ChannelResult) | ||
111 | go func() { | ||
112 | result <- NewChannelResult(c.conn.Read(buf)) | ||
113 | }() | ||
114 | |||
115 | listener := sig.Listen() | ||
116 | defer sig.Drop(listener) | ||
117 | for { | ||
118 | select { | ||
119 | // handle terminate command | ||
120 | case x := <-listener: | ||
121 | switch val := x.(type) { | ||
122 | case bool: | ||
123 | if val { | ||
124 | return 0, ErrChannelInterrupted | ||
125 | } | ||
126 | } | ||
127 | // handle result of read operation | ||
128 | case res := <-result: | ||
129 | return res.Values() | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | // Write buffer to a network channel: Returns the number of written bytes and | ||
135 | // an error code. The write operation can be aborted by sending 'true' on the | ||
136 | // command channel; the network channel is closed after such interrupt. | ||
137 | func (c *NetworkChannel) Write(buf []byte, sig *concurrent.Signaller) (int, error) { | ||
138 | // check if we have an open channel to write to. | ||
139 | if c.conn == nil { | ||
140 | return 0, ErrChannelNotOpened | ||
141 | } | ||
142 | // perform operation in go-routine | ||
143 | result := make(chan *ChannelResult) | ||
144 | go func() { | ||
145 | result <- NewChannelResult(c.conn.Write(buf)) | ||
146 | }() | ||
147 | |||
148 | listener := sig.Listen() | ||
149 | defer sig.Drop(listener) | ||
150 | for { | ||
151 | select { | ||
152 | // handle terminate command | ||
153 | case x := <-listener: | ||
154 | switch val := x.(type) { | ||
155 | case bool: | ||
156 | if val { | ||
157 | return 0, ErrChannelInterrupted | ||
158 | } | ||
159 | } | ||
160 | // handle result of read operation | ||
161 | case res := <-result: | ||
162 | return res.Values() | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | |||
167 | //////////////////////////////////////////////////////////////////////// | ||
168 | // Generic network-based ChannelServer | ||
169 | |||
170 | // NetworkChannelServer represents a network-based channel server | ||
171 | type NetworkChannelServer struct { | ||
172 | network string // network protocol to listen on | ||
173 | listener net.Listener // reference to listener object | ||
174 | } | ||
175 | |||
176 | // NewNetworkChannelServer creates a new network-based channel server | ||
177 | func NewNetworkChannelServer(netw string) ChannelServer { | ||
178 | return &NetworkChannelServer{ | ||
179 | network: netw, | ||
180 | listener: nil, | ||
181 | } | ||
182 | } | ||
183 | |||
184 | // Open a network channel server (= start running it) based on the given | ||
185 | // specification. For every client connection to the server, the associated | ||
186 | // network channel for the connection is send via the hdlr channel. | ||
187 | func (s *NetworkChannelServer) Open(spec string, hdlr chan<- Channel) (err error) { | ||
188 | parts := strings.Split(spec, "+") | ||
189 | // check for correct protocol | ||
190 | if parts[0] != s.network { | ||
191 | return ErrChannelNotImplemented | ||
192 | } | ||
193 | // create listener | ||
194 | if s.listener, err = net.Listen(s.network, parts[1]); err != nil { | ||
195 | return | ||
196 | } | ||
197 | // handle additional parameters ('key[=value]') | ||
198 | for _, param := range parts[2:] { | ||
199 | frag := strings.Split(param, "=") | ||
200 | switch frag[0] { | ||
201 | case "perm": // set permissions on 'unix' | ||
202 | if s.network == "unix" { | ||
203 | if perm, err := strconv.ParseInt(frag[1], 8, 32); err == nil { | ||
204 | if err := os.Chmod(parts[1], os.FileMode(perm)); err != nil { | ||
205 | logger.Printf( | ||
206 | logger.ERROR, | ||
207 | "NetworkChannelServer: Failed to set permissions: %s\n", | ||
208 | err.Error()) | ||
209 | |||
210 | } | ||
211 | } else { | ||
212 | logger.Printf( | ||
213 | logger.ERROR, | ||
214 | "NetworkChannelServer: Invalid permissions '%s'\n", | ||
215 | frag[1]) | ||
216 | } | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | // run go routine to handle channel requests from clients | ||
221 | go func() { | ||
222 | for { | ||
223 | conn, err := s.listener.Accept() | ||
224 | if err != nil { | ||
225 | // signal failure and terminate | ||
226 | hdlr <- nil | ||
227 | break | ||
228 | } | ||
229 | // send channel to handler | ||
230 | hdlr <- &NetworkChannel{ | ||
231 | network: s.network, | ||
232 | conn: conn, | ||
233 | } | ||
234 | } | ||
235 | if s.listener != nil { | ||
236 | s.listener.Close() | ||
237 | } | ||
238 | }() | ||
239 | |||
240 | return nil | ||
241 | } | ||
242 | |||
243 | // Close a network channel server (= stop the server) | ||
244 | func (s *NetworkChannelServer) Close() error { | ||
245 | if s.listener != nil { | ||
246 | err := s.listener.Close() | ||
247 | s.listener = nil | ||
248 | return err | ||
249 | } | ||
250 | return nil | ||
251 | } | ||
252 | |||
253 | //////////////////////////////////////////////////////////////////////// | ||
254 | // helper functions to instantiate network channels and servers for | ||
255 | // common network protocols | ||
256 | |||
257 | // NewSocketChannel implements a Unix Domain Socket connection | ||
258 | func NewSocketChannel() Channel { | ||
259 | return NewNetworkChannel("unix") | ||
260 | } | ||
261 | |||
262 | // NewTCPChannel implements a: TCP connection | ||
263 | func NewTCPChannel() Channel { | ||
264 | return NewNetworkChannel("tcp") | ||
265 | } | ||
266 | |||
267 | // NewUDPChannel implements an UDP connection | ||
268 | func NewUDPChannel() Channel { | ||
269 | return NewNetworkChannel("udp") | ||
270 | } | ||
271 | |||
272 | // NewSocketChannelServer implements an Unix Domain Socket listener | ||
273 | func NewSocketChannelServer() ChannelServer { | ||
274 | return NewNetworkChannelServer("unix") | ||
275 | } | ||
276 | |||
277 | // NewTCPChannelServer implements a TCP listener | ||
278 | func NewTCPChannelServer() ChannelServer { | ||
279 | return NewNetworkChannelServer("tcp") | ||
280 | } | ||
281 | |||
282 | // NewUDPChannelServer implements an UDP listener | ||
283 | func NewUDPChannelServer() ChannelServer { | ||
284 | return NewNetworkChannelServer("udp") | ||
285 | } | ||
diff --git a/src/gnunet/transport/channel_test.go b/src/gnunet/transport/channel_test.go deleted file mode 100644 index f028171..0000000 --- a/src/gnunet/transport/channel_test.go +++ /dev/null | |||
@@ -1,232 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package transport | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "fmt" | ||
24 | "testing" | ||
25 | "time" | ||
26 | |||
27 | "github.com/bfix/gospel/concurrent" | ||
28 | ) | ||
29 | |||
30 | // TODO: These test cases fail from time to time for no obvious reason. | ||
31 | // This needs to be investigated. | ||
32 | |||
33 | const ( | ||
34 | SockAddr = "/tmp/gnunet-go-test.sock" | ||
35 | TCPAddrClient = "gnunet.org:80" | ||
36 | TCPAddrServer = "127.0.0.1:9876" | ||
37 | ) | ||
38 | |||
39 | type TestChannelServer struct { | ||
40 | hdlr chan Channel | ||
41 | srvc ChannelServer | ||
42 | running bool | ||
43 | } | ||
44 | |||
45 | func NewTestChannelServer() *TestChannelServer { | ||
46 | return &TestChannelServer{ | ||
47 | hdlr: make(chan Channel), | ||
48 | srvc: nil, | ||
49 | running: false, | ||
50 | } | ||
51 | } | ||
52 | |||
53 | func (s *TestChannelServer) handle(ch Channel, sig *concurrent.Signaller) { | ||
54 | buf := make([]byte, 4096) | ||
55 | for { | ||
56 | n, err := ch.Read(buf, sig) | ||
57 | if err != nil { | ||
58 | break | ||
59 | } | ||
60 | _, err = ch.Write(buf[:n], sig) | ||
61 | if err != nil { | ||
62 | break | ||
63 | } | ||
64 | } | ||
65 | ch.Close() | ||
66 | } | ||
67 | |||
68 | func (s *TestChannelServer) Start(spec string) (err error) { | ||
69 | // check if we are already running | ||
70 | if s.running { | ||
71 | return fmt.Errorf("Server already running") | ||
72 | } | ||
73 | |||
74 | // start channel server | ||
75 | if s.srvc, err = NewChannelServer(spec, s.hdlr); err != nil { | ||
76 | return | ||
77 | } | ||
78 | s.running = true | ||
79 | |||
80 | // handle clients | ||
81 | sig := concurrent.NewSignaller() | ||
82 | go func() { | ||
83 | for s.running { | ||
84 | in := <-s.hdlr | ||
85 | if in == nil { | ||
86 | break | ||
87 | } | ||
88 | switch x := in.(type) { | ||
89 | case Channel: | ||
90 | go s.handle(x, sig) | ||
91 | } | ||
92 | } | ||
93 | s.srvc.Close() | ||
94 | s.running = false | ||
95 | }() | ||
96 | return nil | ||
97 | } | ||
98 | |||
99 | func (s *TestChannelServer) Stop() { | ||
100 | s.running = false | ||
101 | } | ||
102 | |||
103 | func TestChannelServerTCPSingle(t *testing.T) { | ||
104 | time.Sleep(time.Second) | ||
105 | s := NewTestChannelServer() | ||
106 | err := s.Start("tcp+" + TCPAddrServer) | ||
107 | defer s.Stop() | ||
108 | if err != nil { | ||
109 | t.Fatal(err) | ||
110 | } | ||
111 | } | ||
112 | |||
113 | func TestChannelServerTCPTwice(t *testing.T) { | ||
114 | time.Sleep(time.Second) | ||
115 | s1 := NewTestChannelServer() | ||
116 | err := s1.Start("tcp+" + TCPAddrServer) | ||
117 | defer s1.Stop() | ||
118 | if err != nil { | ||
119 | t.Fatal(err) | ||
120 | } | ||
121 | time.Sleep(time.Second) | ||
122 | s2 := NewTestChannelServer() | ||
123 | err = s2.Start("tcp+" + TCPAddrServer) | ||
124 | defer s2.Stop() | ||
125 | if err == nil { | ||
126 | t.Fatal("SocketServer started twice!!") | ||
127 | } | ||
128 | } | ||
129 | |||
130 | func TestChannelClientTCP(t *testing.T) { | ||
131 | time.Sleep(time.Second) | ||
132 | ch, err := NewChannel("tcp+" + TCPAddrClient) | ||
133 | if err != nil { | ||
134 | t.Fatal(err) | ||
135 | } | ||
136 | defer ch.Close() | ||
137 | |||
138 | sig := concurrent.NewSignaller() | ||
139 | msg := []byte("GET /\n\n") | ||
140 | n, err := ch.Write(msg, sig) | ||
141 | if err != nil { | ||
142 | t.Fatal(err) | ||
143 | } | ||
144 | if n != len(msg) { | ||
145 | t.Fatal("Send size mismatch") | ||
146 | } | ||
147 | buf := make([]byte, 4096) | ||
148 | n = 0 | ||
149 | start := time.Now().Unix() | ||
150 | for n == 0 && (time.Now().Unix()-start) < 3 { | ||
151 | if n, err = ch.Read(buf, sig); err != nil { | ||
152 | t.Fatal(err) | ||
153 | } | ||
154 | } | ||
155 | t.Logf("'%s' [%d]\n", string(buf[:n]), n) | ||
156 | } | ||
157 | |||
158 | func TestChannelClientServerTCP(t *testing.T) { | ||
159 | time.Sleep(time.Second) | ||
160 | s := NewTestChannelServer() | ||
161 | err := s.Start("tcp+" + TCPAddrServer) | ||
162 | defer s.Stop() | ||
163 | if err != nil { | ||
164 | t.Fatal(err) | ||
165 | } | ||
166 | |||
167 | ch, err := NewChannel("tcp+" + TCPAddrServer) | ||
168 | if err != nil { | ||
169 | t.Fatal(err) | ||
170 | } | ||
171 | sig := concurrent.NewSignaller() | ||
172 | msg := []byte("GET /\n\n") | ||
173 | n, err := ch.Write(msg, sig) | ||
174 | if err != nil { | ||
175 | t.Fatal(err) | ||
176 | } | ||
177 | if n != len(msg) { | ||
178 | t.Fatal("Send size mismatch") | ||
179 | } | ||
180 | buf := make([]byte, 4096) | ||
181 | n = 0 | ||
182 | start := time.Now().Unix() | ||
183 | for n == 0 && (time.Now().Unix()-start) < 3 { | ||
184 | if n, err = ch.Read(buf, sig); err != nil { | ||
185 | t.Fatal(err) | ||
186 | } | ||
187 | } | ||
188 | if err = ch.Close(); err != nil { | ||
189 | t.Fatal(err) | ||
190 | } | ||
191 | if !bytes.Equal(buf[:n], msg) { | ||
192 | t.Fatal("message send/receive mismatch") | ||
193 | } | ||
194 | } | ||
195 | |||
196 | func TestChannelClientServerSock(t *testing.T) { | ||
197 | time.Sleep(time.Second) | ||
198 | s := NewTestChannelServer() | ||
199 | if err := s.Start("unix+" + SockAddr); err != nil { | ||
200 | t.Fatal(err) | ||
201 | } | ||
202 | |||
203 | ch, err := NewChannel("unix+" + SockAddr) | ||
204 | if err != nil { | ||
205 | t.Fatal(err) | ||
206 | } | ||
207 | sig := concurrent.NewSignaller() | ||
208 | msg := []byte("This is just a test -- please ignore...") | ||
209 | n, err := ch.Write(msg, sig) | ||
210 | if err != nil { | ||
211 | t.Fatal(err) | ||
212 | } | ||
213 | if n != len(msg) { | ||
214 | t.Fatal("Send size mismatch") | ||
215 | } | ||
216 | buf := make([]byte, 4096) | ||
217 | n = 0 | ||
218 | start := time.Now().Unix() | ||
219 | for n == 0 && (time.Now().Unix()-start) < 3 { | ||
220 | if n, err = ch.Read(buf, sig); err != nil { | ||
221 | t.Fatal(err) | ||
222 | } | ||
223 | } | ||
224 | if err = ch.Close(); err != nil { | ||
225 | t.Fatal(err) | ||
226 | } | ||
227 | if !bytes.Equal(buf[:n], msg) { | ||
228 | t.Fatal("message send/receive mismatch") | ||
229 | } | ||
230 | |||
231 | s.Stop() | ||
232 | } | ||
diff --git a/src/gnunet/transport/connection.go b/src/gnunet/transport/connection.go index cc2c909..8337260 100644 --- a/src/gnunet/transport/connection.go +++ b/src/gnunet/transport/connection.go | |||
@@ -1,5 +1,5 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | 1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. |
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | 2 | // Copyright (C) 2019-2022 Bernd Fix >Y< |
3 | // | 3 | // |
4 | // gnunet-go is free software: you can redistribute it and/or modify it | 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 | 5 | // under the terms of the GNU Affero General Public License as published |
@@ -19,59 +19,97 @@ | |||
19 | package transport | 19 | package transport |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "gnunet/core" | 22 | "context" |
23 | "errors" | ||
23 | "gnunet/message" | 24 | "gnunet/message" |
25 | "net" | ||
26 | ) | ||
24 | 27 | ||
25 | "github.com/bfix/gospel/concurrent" | 28 | // Error codes |
29 | var ( | ||
30 | ErrConnectionNotOpened = errors.New("connection not opened") | ||
31 | ErrConnectionInterrupted = errors.New("connection interrupted") | ||
26 | ) | 32 | ) |
27 | 33 | ||
28 | // Connection for communicating peers | 34 | //---------------------------------------------------------------------- |
35 | |||
36 | // Connection is a net.Conn for GNUnet message exchange (send/receive) | ||
29 | type Connection struct { | 37 | type Connection struct { |
30 | from, to *core.Peer | 38 | conn net.Conn // associated connection |
31 | ch *MsgChannel | 39 | buf []byte // read/write buffer |
32 | bandwidth uint32 | ||
33 | state int | ||
34 | shared []byte | ||
35 | } | 40 | } |
36 | 41 | ||
37 | // NewConnection instanciates a new connection between peers communicating | 42 | // NewConnection creates a new connection from an existing net.Conn |
38 | // over a message channel (Connections are authenticated and secured). | 43 | // This is usually used by clients to connect to a service. |
39 | func NewConnection(ch *MsgChannel, from, to *core.Peer) *Connection { | 44 | func NewConnection(ctx context.Context, conn net.Conn) *Connection { |
40 | return &Connection{ | 45 | return &Connection{ |
41 | from: from, | 46 | conn: conn, |
42 | to: to, | 47 | buf: make([]byte, 65536), |
43 | state: 1, | ||
44 | ch: ch, | ||
45 | } | 48 | } |
46 | } | 49 | } |
47 | 50 | ||
48 | // SharedSecret computes the shared secret the two endpoints of a connection. | 51 | // Close connection |
49 | func (c *Connection) SharedSecret(secret []byte) { | 52 | func (s *Connection) Close() error { |
50 | c.shared = make([]byte, len(secret)) | 53 | if s.conn != nil { |
51 | copy(c.shared, secret) | 54 | rc := s.conn.Close() |
55 | s.conn = nil | ||
56 | return rc | ||
57 | } | ||
58 | return ErrConnectionNotOpened | ||
52 | } | 59 | } |
53 | 60 | ||
54 | // GetState returns the current state of the connection. | 61 | // Send a GNUnet message over connection |
55 | func (c *Connection) GetState() int { | 62 | func (s *Connection) Send(ctx context.Context, msg message.Message) error { |
56 | return c.state | 63 | return WriteMessage(ctx, s.conn, msg) |
57 | } | 64 | } |
58 | 65 | ||
59 | // SetBandwidth to control transfer rates on the connection | 66 | // Receive GNUnet messages from socket. |
60 | func (c *Connection) SetBandwidth(bw uint32) { | 67 | func (s *Connection) Receive(ctx context.Context) (message.Message, error) { |
61 | c.bandwidth = bw | 68 | return ReadMessage(ctx, s.conn, s.buf) |
62 | } | 69 | } |
63 | 70 | ||
64 | // Close connection between two peers. | 71 | //---------------------------------------------------------------------- |
65 | func (c *Connection) Close() error { | 72 | |
66 | return c.ch.Close() | 73 | // ConnectionManager handles client connections on a net.Listener |
74 | type ConnectionManager struct { | ||
75 | listener net.Listener // reference to listener object | ||
67 | } | 76 | } |
68 | 77 | ||
69 | // Send a message on the connection | 78 | // NewConnectionManager creates a new net.Listener connection manager. |
70 | func (c *Connection) Send(msg message.Message, sig *concurrent.Signaller) error { | 79 | // Incoming connections from clients are dispatched to a handler channel. |
71 | return c.ch.Send(msg, sig) | 80 | func NewConnectionManager(ctx context.Context, listener net.Listener, hdlr chan *Connection) (cs *ConnectionManager, err error) { |
81 | // instantiate connection manager | ||
82 | cs = &ConnectionManager{ | ||
83 | listener: listener, | ||
84 | } | ||
85 | // run watch dog for termination | ||
86 | go func() { | ||
87 | <-ctx.Done() | ||
88 | cs.listener.Close() | ||
89 | }() | ||
90 | // run go routine to handle channel requests from clients | ||
91 | go func() { | ||
92 | for { | ||
93 | conn, err := cs.listener.Accept() | ||
94 | if err != nil { | ||
95 | return | ||
96 | } | ||
97 | // handle connection | ||
98 | c := &Connection{ | ||
99 | conn: conn, | ||
100 | buf: make([]byte, 65536), | ||
101 | } | ||
102 | hdlr <- c | ||
103 | } | ||
104 | }() | ||
105 | return cs, nil | ||
72 | } | 106 | } |
73 | 107 | ||
74 | // Receive a message on the connection | 108 | // Close a connection manager (= stop the server) |
75 | func (c *Connection) Receive(sig *concurrent.Signaller) (message.Message, error) { | 109 | func (s *ConnectionManager) Close() (err error) { |
76 | return c.ch.Receive(sig) | 110 | if s.listener != nil { |
111 | err = s.listener.Close() | ||
112 | s.listener = nil | ||
113 | } | ||
114 | return | ||
77 | } | 115 | } |
diff --git a/src/gnunet/transport/endpoint.go b/src/gnunet/transport/endpoint.go new file mode 100644 index 0000000..b54ee4e --- /dev/null +++ b/src/gnunet/transport/endpoint.go | |||
@@ -0,0 +1,282 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package transport | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "context" | ||
24 | "errors" | ||
25 | "gnunet/message" | ||
26 | "gnunet/util" | ||
27 | "net" | ||
28 | ) | ||
29 | |||
30 | var ( | ||
31 | ErrEndpNotAvailable = errors.New("no endpoint for address available") | ||
32 | ErrEndpProtocolMismatch = errors.New("transport protocol mismatch") | ||
33 | ) | ||
34 | |||
35 | // Endpoint represents a local endpoint that can send and receive messages. | ||
36 | // Implementations need to manage the relations between peer IDs and | ||
37 | // remote endpoints for TCP and UDP traffic. | ||
38 | type Endpoint interface { | ||
39 | // Run the endpoint and send received messages to channel | ||
40 | Run(context.Context, chan *TransportMessage) error | ||
41 | |||
42 | // Send message on endpoint | ||
43 | Send(context.Context, net.Addr, *TransportMessage) error | ||
44 | |||
45 | // Address returns the listening address for the endpoint | ||
46 | Address() net.Addr | ||
47 | |||
48 | // CanSendTo returns true if the endpoint can sent to address | ||
49 | CanSendTo(net.Addr) bool | ||
50 | |||
51 | // Return endpoint identifier | ||
52 | ID() int | ||
53 | } | ||
54 | |||
55 | //---------------------------------------------------------------------- | ||
56 | |||
57 | // NewEndpoint returns a suitable endpoint for the address. | ||
58 | func NewEndpoint(addr net.Addr) (ep Endpoint, err error) { | ||
59 | switch epMode(addr.Network()) { | ||
60 | case "packet": | ||
61 | ep, err = newPacketEndpoint(addr) | ||
62 | case "stream": | ||
63 | ep, err = newStreamEndpoint(addr) | ||
64 | default: | ||
65 | err = ErrEndpNotAvailable | ||
66 | } | ||
67 | return | ||
68 | } | ||
69 | |||
70 | //---------------------------------------------------------------------- | ||
71 | // Packet-oriented endpoint | ||
72 | //---------------------------------------------------------------------- | ||
73 | |||
74 | // PacketEndpoint for packet-oriented network protocols | ||
75 | type PaketEndpoint struct { | ||
76 | id int // endpoint identifier | ||
77 | addr net.Addr // endpoint address | ||
78 | conn net.PacketConn // packet connection | ||
79 | buf []byte // buffer for read/write operations | ||
80 | } | ||
81 | |||
82 | // Run packet endpoint: send incoming messages to the handler. | ||
83 | func (ep *PaketEndpoint) Run(ctx context.Context, hdlr chan *TransportMessage) (err error) { | ||
84 | // create listener | ||
85 | var lc net.ListenConfig | ||
86 | if ep.conn, err = lc.ListenPacket(ctx, ep.addr.Network(), ep.addr.String()); err != nil { | ||
87 | return | ||
88 | } | ||
89 | |||
90 | // run watch dog for termination | ||
91 | go func() { | ||
92 | <-ctx.Done() | ||
93 | ep.conn.Close() | ||
94 | }() | ||
95 | // run go routine to handle messages from clients | ||
96 | go func() { | ||
97 | for { | ||
98 | // read next message from packet | ||
99 | n, _, err := ep.conn.ReadFrom(ep.buf) | ||
100 | if err != nil { | ||
101 | break | ||
102 | } | ||
103 | rdr := bytes.NewBuffer(util.Clone(ep.buf[:n])) | ||
104 | msg, err := ReadMessageDirect(rdr, ep.buf) | ||
105 | if err != nil { | ||
106 | break | ||
107 | } | ||
108 | // check for transport message | ||
109 | if msg.Header().MsgType == message.DUMMY { | ||
110 | // set transient attributes | ||
111 | tm := msg.(*TransportMessage) | ||
112 | tm.endp = ep.id | ||
113 | tm.conn = 0 | ||
114 | // send to handler | ||
115 | go func() { | ||
116 | hdlr <- tm | ||
117 | }() | ||
118 | } | ||
119 | } | ||
120 | // connection ended. | ||
121 | ep.conn.Close() | ||
122 | }() | ||
123 | return | ||
124 | } | ||
125 | |||
126 | // Send message to address from endpoint | ||
127 | func (ep *PaketEndpoint) Send(ctx context.Context, addr net.Addr, msg *TransportMessage) (err error) { | ||
128 | var a *net.UDPAddr | ||
129 | a, err = net.ResolveUDPAddr(addr.Network(), addr.String()) | ||
130 | var buf []byte | ||
131 | if buf, err = msg.Bytes(); err != nil { | ||
132 | return | ||
133 | } | ||
134 | _, err = ep.conn.WriteTo(buf, a) | ||
135 | return | ||
136 | } | ||
137 | |||
138 | // Address returms the | ||
139 | func (ep *PaketEndpoint) Address() net.Addr { | ||
140 | if ep.conn != nil { | ||
141 | return ep.conn.LocalAddr() | ||
142 | } | ||
143 | return ep.addr | ||
144 | } | ||
145 | |||
146 | // CanSendTo returns true if the endpoint can sent to address | ||
147 | func (ep *PaketEndpoint) CanSendTo(addr net.Addr) bool { | ||
148 | return epMode(addr.Network()) == "packet" | ||
149 | } | ||
150 | |||
151 | // ID returns the endpoint identifier | ||
152 | func (ep *PaketEndpoint) ID() int { | ||
153 | return ep.id | ||
154 | } | ||
155 | |||
156 | func newPacketEndpoint(addr net.Addr) (ep *PaketEndpoint, err error) { | ||
157 | // check for matching protocol | ||
158 | if epMode(addr.Network()) != "packet" { | ||
159 | err = ErrEndpProtocolMismatch | ||
160 | return | ||
161 | } | ||
162 | // create endpoint | ||
163 | ep = &PaketEndpoint{ | ||
164 | id: util.NextID(), | ||
165 | addr: addr, | ||
166 | buf: make([]byte, 65536), | ||
167 | } | ||
168 | return | ||
169 | } | ||
170 | |||
171 | //---------------------------------------------------------------------- | ||
172 | // Stream-oriented endpoint | ||
173 | //---------------------------------------------------------------------- | ||
174 | |||
175 | // StreamEndpoint for stream-oriented network protocols | ||
176 | type StreamEndpoint struct { | ||
177 | id int // endpoint identifier | ||
178 | addr net.Addr // listening address | ||
179 | listener net.Listener // listener instance | ||
180 | conns *util.Map[int, net.Conn] // active connections | ||
181 | buf []byte // read/write buffer | ||
182 | } | ||
183 | |||
184 | // Run packet endpoint: send incoming messages to the handler. | ||
185 | func (ep *StreamEndpoint) Run(ctx context.Context, hdlr chan *TransportMessage) (err error) { | ||
186 | // create listener | ||
187 | var lc net.ListenConfig | ||
188 | if ep.listener, err = lc.Listen(ctx, ep.addr.Network(), ep.addr.String()); err != nil { | ||
189 | return | ||
190 | } | ||
191 | // run watch dog for termination | ||
192 | go func() { | ||
193 | <-ctx.Done() | ||
194 | ep.listener.Close() | ||
195 | }() | ||
196 | // run go routine to handle messages from clients | ||
197 | go func() { | ||
198 | for { | ||
199 | // get next client connection | ||
200 | conn, err := ep.listener.Accept() | ||
201 | if err != nil { | ||
202 | return | ||
203 | } | ||
204 | session := util.NextID() | ||
205 | ep.conns.Put(session, conn) | ||
206 | go func() { | ||
207 | for { | ||
208 | // read next message from connection | ||
209 | msg, err := ReadMessage(ctx, conn, ep.buf) | ||
210 | if err != nil { | ||
211 | break | ||
212 | } | ||
213 | // check for transport message | ||
214 | if msg.Header().MsgType == message.DUMMY { | ||
215 | // set transient attributes | ||
216 | tm := msg.(*TransportMessage) | ||
217 | tm.endp = ep.id | ||
218 | tm.conn = session | ||
219 | // send to handler | ||
220 | go func() { | ||
221 | hdlr <- tm | ||
222 | }() | ||
223 | } | ||
224 | } | ||
225 | // connection ended. | ||
226 | conn.Close() | ||
227 | ep.conns.Delete(session) | ||
228 | }() | ||
229 | } | ||
230 | }() | ||
231 | return | ||
232 | } | ||
233 | |||
234 | // Send message to address from endpoint | ||
235 | func (ep *StreamEndpoint) Send(ctx context.Context, addr net.Addr, msg *TransportMessage) error { | ||
236 | return nil | ||
237 | } | ||
238 | |||
239 | // Address returns the actual listening endpoint address | ||
240 | func (ep *StreamEndpoint) Address() net.Addr { | ||
241 | if ep.listener != nil { | ||
242 | return ep.listener.Addr() | ||
243 | } | ||
244 | return ep.addr | ||
245 | } | ||
246 | |||
247 | // CanSendTo returns true if the endpoint can sent to address | ||
248 | func (ep *StreamEndpoint) CanSendTo(addr net.Addr) bool { | ||
249 | return epMode(addr.Network()) == "stream" | ||
250 | } | ||
251 | |||
252 | // ID returns the endpoint identifier | ||
253 | func (ep *StreamEndpoint) ID() int { | ||
254 | return ep.id | ||
255 | } | ||
256 | |||
257 | func newStreamEndpoint(addr net.Addr) (ep *StreamEndpoint, err error) { | ||
258 | // check for matching protocol | ||
259 | if epMode(addr.Network()) != "stream" { | ||
260 | err = ErrEndpProtocolMismatch | ||
261 | return | ||
262 | } | ||
263 | // create endpoint | ||
264 | ep = &StreamEndpoint{ | ||
265 | id: util.NextID(), | ||
266 | addr: addr, | ||
267 | conns: util.NewMap[int, net.Conn](), | ||
268 | buf: make([]byte, 65536), | ||
269 | } | ||
270 | return | ||
271 | } | ||
272 | |||
273 | // epMode returns the endpoint mode (packet or stream) for a given network | ||
274 | func epMode(netw string) string { | ||
275 | switch netw { | ||
276 | case "udp", "udp4", "udp6", "r5n+ip+udp": | ||
277 | return "packet" | ||
278 | case "tcp", "unix": | ||
279 | return "stream" | ||
280 | } | ||
281 | return "" | ||
282 | } | ||
diff --git a/src/gnunet/transport/reader_writer.go b/src/gnunet/transport/reader_writer.go new file mode 100644 index 0000000..2e5f14a --- /dev/null +++ b/src/gnunet/transport/reader_writer.go | |||
@@ -0,0 +1,157 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019-2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package transport | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "errors" | ||
24 | "fmt" | ||
25 | "gnunet/message" | ||
26 | "io" | ||
27 | |||
28 | "github.com/bfix/gospel/data" | ||
29 | ) | ||
30 | |||
31 | // WriteMessageDirect writes directly to io.Writer | ||
32 | func WriteMessageDirect(wrt io.Writer, msg message.Message) error { | ||
33 | dwc := &directWriteCloser{wrt} | ||
34 | return WriteMessage(context.Background(), dwc, msg) | ||
35 | } | ||
36 | |||
37 | // WriteMessage to io.WriteCloser | ||
38 | func WriteMessage(ctx context.Context, wrt io.WriteCloser, msg message.Message) (err error) { | ||
39 | // convert message to binary data | ||
40 | var buf []byte | ||
41 | if buf, err = data.Marshal(msg); err != nil { | ||
42 | return err | ||
43 | } | ||
44 | // check message header size and packet size | ||
45 | mh, err := message.GetMsgHeader(buf) | ||
46 | if err != nil { | ||
47 | return err | ||
48 | } | ||
49 | if len(buf) != int(mh.MsgSize) { | ||
50 | return errors.New("WriteMessage: message size mismatch") | ||
51 | } | ||
52 | // watch dog for write operation | ||
53 | go func() { | ||
54 | for { | ||
55 | select { | ||
56 | case <-ctx.Done(): | ||
57 | wrt.Close() | ||
58 | } | ||
59 | } | ||
60 | }() | ||
61 | // perform write operation | ||
62 | var n int | ||
63 | if n, err = wrt.Write(buf); err != nil { | ||
64 | return | ||
65 | } | ||
66 | if n != len(buf) { | ||
67 | err = fmt.Errorf("WriteMessage incomplete (%d of %d)", n, len(buf)) | ||
68 | } | ||
69 | return | ||
70 | } | ||
71 | |||
72 | //---------------------------------------------------------------------- | ||
73 | |||
74 | // ReadMessageDirect reads directly from io.Reader | ||
75 | func ReadMessageDirect(rdr io.Reader, buf []byte) (msg message.Message, err error) { | ||
76 | drc := &directReadCloser{ | ||
77 | rdr: rdr, | ||
78 | } | ||
79 | return ReadMessage(context.Background(), drc, buf) | ||
80 | } | ||
81 | |||
82 | // ReadMessage from io.ReadCloser | ||
83 | func ReadMessage(ctx context.Context, rdr io.ReadCloser, buf []byte) (msg message.Message, err error) { | ||
84 | // watch dog for write operation | ||
85 | go func() { | ||
86 | for { | ||
87 | select { | ||
88 | case <-ctx.Done(): | ||
89 | rdr.Close() | ||
90 | } | ||
91 | } | ||
92 | }() | ||
93 | // get bytes from reader | ||
94 | if buf == nil { | ||
95 | buf = make([]byte, 65536) | ||
96 | } | ||
97 | get := func(pos, count int) error { | ||
98 | n, err := rdr.Read(buf[pos : pos+count]) | ||
99 | if err == nil && n != count { | ||
100 | err = fmt.Errorf("not enough bytes on reader (%d of %d)", n, count) | ||
101 | } | ||
102 | return err | ||
103 | } | ||
104 | // read header first | ||
105 | if err := get(0, 4); err != nil { | ||
106 | return nil, err | ||
107 | } | ||
108 | var mh *message.Header | ||
109 | if mh, err = message.GetMsgHeader(buf[:4]); err != nil { | ||
110 | return nil, err | ||
111 | } | ||
112 | // get rest of message | ||
113 | if err = get(4, int(mh.MsgSize)-4); err != nil { | ||
114 | return nil, err | ||
115 | } | ||
116 | // handle transport message case | ||
117 | if mh.MsgType == message.DUMMY { | ||
118 | msg = NewTransportMessage(nil, nil) | ||
119 | } else if msg, err = message.NewEmptyMessage(mh.MsgType); err != nil { | ||
120 | return nil, err | ||
121 | } | ||
122 | if msg == nil { | ||
123 | return nil, fmt.Errorf("message{%d} is nil", mh.MsgType) | ||
124 | } | ||
125 | if err = data.Unmarshal(msg, buf[:mh.MsgSize]); err != nil { | ||
126 | return nil, err | ||
127 | } | ||
128 | return msg, nil | ||
129 | } | ||
130 | |||
131 | //---------------------------------------------------------------------- | ||
132 | // helper for wrapped ReadCloser/WriteCloser (close is nop) | ||
133 | //---------------------------------------------------------------------- | ||
134 | |||
135 | type directReadCloser struct { | ||
136 | rdr io.Reader | ||
137 | } | ||
138 | |||
139 | func (drc *directReadCloser) Read(buf []byte) (int, error) { | ||
140 | return drc.rdr.Read(buf) | ||
141 | } | ||
142 | |||
143 | func (drc *directReadCloser) Close() error { | ||
144 | return nil | ||
145 | } | ||
146 | |||
147 | type directWriteCloser struct { | ||
148 | wrt io.Writer | ||
149 | } | ||
150 | |||
151 | func (dwc *directWriteCloser) Write(buf []byte) (int, error) { | ||
152 | return dwc.wrt.Write(buf) | ||
153 | } | ||
154 | |||
155 | func (dwc *directWriteCloser) Close() error { | ||
156 | return nil | ||
157 | } | ||
diff --git a/src/gnunet/transport/transport.go b/src/gnunet/transport/transport.go new file mode 100644 index 0000000..14def98 --- /dev/null +++ b/src/gnunet/transport/transport.go | |||
@@ -0,0 +1,151 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2022 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package transport | ||
20 | |||
21 | import ( | ||
22 | "bytes" | ||
23 | "context" | ||
24 | "errors" | ||
25 | "gnunet/message" | ||
26 | "gnunet/util" | ||
27 | "net" | ||
28 | ) | ||
29 | |||
30 | // Trnsport layer error codes | ||
31 | var ( | ||
32 | ErrTransNoEndpoint = errors.New("no matching endpoint found") | ||
33 | ) | ||
34 | |||
35 | //====================================================================== | ||
36 | // Network-oriented transport implementation | ||
37 | //====================================================================== | ||
38 | |||
39 | // TransportMessage is the unit processed by the transport mechanism. | ||
40 | // Peer refers to the remote endpoint (sender/receiver) and | ||
41 | // Msg is the exchanged GNUnet message. The packet itself satisfies the | ||
42 | // message.Message interface. | ||
43 | type TransportMessage struct { | ||
44 | Hdr *message.Header `` // message header | ||
45 | Peer *util.PeerID `` // remote peer | ||
46 | Payload []byte `size:"*"` // GNUnet message | ||
47 | |||
48 | // package-local attributes (transient) | ||
49 | msg message.Message | ||
50 | endp int // id of endpoint (incoming message) | ||
51 | conn int // id of connection (optional, incoming message) | ||
52 | } | ||
53 | |||
54 | func (msg *TransportMessage) Header() *message.Header { | ||
55 | return msg.Hdr | ||
56 | } | ||
57 | |||
58 | func (msg *TransportMessage) Message() (m message.Message, err error) { | ||
59 | if m = msg.msg; m == nil { | ||
60 | rdr := bytes.NewBuffer(msg.Payload) | ||
61 | m, err = ReadMessageDirect(rdr, nil) | ||
62 | } | ||
63 | return | ||
64 | } | ||
65 | |||
66 | // Bytes returns the binary representation of a transport message | ||
67 | func (msg *TransportMessage) Bytes() ([]byte, error) { | ||
68 | buf := new(bytes.Buffer) | ||
69 | err := WriteMessageDirect(buf, msg) | ||
70 | return buf.Bytes(), err | ||
71 | } | ||
72 | |||
73 | // String returns the message in human-readable form | ||
74 | func (msg *TransportMessage) String() string { | ||
75 | return "TransportMessage{...}" | ||
76 | } | ||
77 | |||
78 | // NewTransportMessage creates a message suitable for transfer | ||
79 | func NewTransportMessage(peer *util.PeerID, payload []byte) (tm *TransportMessage) { | ||
80 | if peer == nil { | ||
81 | peer = util.NewPeerID(nil) | ||
82 | } | ||
83 | msize := 0 | ||
84 | if payload != nil { | ||
85 | msize = len(payload) | ||
86 | } | ||
87 | tm = &TransportMessage{ | ||
88 | Hdr: &message.Header{ | ||
89 | MsgSize: uint16(36 + msize), | ||
90 | MsgType: message.DUMMY, | ||
91 | }, | ||
92 | Peer: peer, | ||
93 | Payload: payload, | ||
94 | } | ||
95 | return | ||
96 | } | ||
97 | |||
98 | //---------------------------------------------------------------------- | ||
99 | |||
100 | // Transport enables network-oriented (like IP, UDP, TCP or UDS) | ||
101 | // message exchange on multiple endpoints. | ||
102 | type Transport struct { | ||
103 | incoming chan *TransportMessage // messages as received from the network | ||
104 | endpoints map[int]Endpoint // list of available endpoints | ||
105 | } | ||
106 | |||
107 | // NewTransport creates and runs a new transport layer implementation. | ||
108 | func NewTransport(ctx context.Context, ch chan *TransportMessage) (t *Transport) { | ||
109 | // create transport instance | ||
110 | return &Transport{ | ||
111 | incoming: ch, | ||
112 | endpoints: make(map[int]Endpoint), | ||
113 | } | ||
114 | } | ||
115 | |||
116 | // Send a message over suitable endpoint | ||
117 | func (t *Transport) Send(ctx context.Context, addr net.Addr, msg *TransportMessage) (err error) { | ||
118 | for _, ep := range t.endpoints { | ||
119 | if ep.CanSendTo(addr) { | ||
120 | err = ep.Send(ctx, addr, msg) | ||
121 | break | ||
122 | } | ||
123 | } | ||
124 | return | ||
125 | } | ||
126 | |||
127 | //---------------------------------------------------------------------- | ||
128 | // Endpoint handling | ||
129 | //---------------------------------------------------------------------- | ||
130 | |||
131 | // AddEndpoint instantiates and run a new endpoint handler for the | ||
132 | // given address (must map to a network interface). | ||
133 | func (t *Transport) AddEndpoint(ctx context.Context, addr net.Addr) (a net.Addr, err error) { | ||
134 | // register endpoint | ||
135 | var ep Endpoint | ||
136 | if ep, err = NewEndpoint(addr); err != nil { | ||
137 | return | ||
138 | } | ||
139 | t.endpoints[ep.ID()] = ep | ||
140 | ep.Run(ctx, t.incoming) | ||
141 | return ep.Address(), nil | ||
142 | } | ||
143 | |||
144 | // Endpoints returns a list of listening addresses managed by transport. | ||
145 | func (t *Transport) Endpoints() (list []net.Addr) { | ||
146 | list = make([]net.Addr, 0) | ||
147 | for _, ep := range t.endpoints { | ||
148 | list = append(list, ep.Address()) | ||
149 | } | ||
150 | return | ||
151 | } | ||
diff --git a/src/gnunet/util/address.go b/src/gnunet/util/address.go index d272742..106e671 100644 --- a/src/gnunet/util/address.go +++ b/src/gnunet/util/address.go | |||
@@ -19,44 +19,81 @@ | |||
19 | package util | 19 | package util |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "encoding/hex" | 22 | "bytes" |
23 | "fmt" | 23 | "fmt" |
24 | "net" | 24 | "net" |
25 | "strings" | ||
25 | ) | 26 | ) |
26 | 27 | ||
27 | // Address specifies how a peer is reachable on the network. | 28 | // Address specifies how a peer is reachable on the network. |
28 | type Address struct { | 29 | type Address struct { |
29 | Transport string // transport protocol | 30 | Netw string `` // network protocol |
30 | Options uint32 `order:"big"` // address options | 31 | Options uint32 `order:"big"` // address options |
31 | Address []byte `size:"*"` // address data (protocol-dependent) | 32 | Expires AbsoluteTime `` // expiration date for address |
33 | Address []byte `size:"*"` // address data (protocol-dependent) | ||
32 | } | 34 | } |
33 | 35 | ||
34 | // NewAddress returns a new Address for the given transport and specs | 36 | // NewAddress returns a new Address for the given transport and specs |
35 | func NewAddress(transport string, addr []byte) *Address { | 37 | func NewAddress(transport string, addr []byte) *Address { |
36 | a := &Address{ | 38 | return &Address{ |
37 | Transport: transport, | 39 | Netw: transport, |
38 | Options: 0, | 40 | Options: 0, |
39 | Address: make([]byte, len(addr)), | 41 | Address: Clone(addr), |
42 | Expires: AbsoluteTimeNever(), | ||
40 | } | 43 | } |
41 | copy(a.Address, addr) | ||
42 | return a | ||
43 | } | 44 | } |
44 | 45 | ||
46 | func NewAddressWrap(addr net.Addr) *Address { | ||
47 | return &Address{ | ||
48 | Netw: addr.Network(), | ||
49 | Options: 0, | ||
50 | Address: []byte(addr.String()), | ||
51 | Expires: AbsoluteTimeNever(), | ||
52 | } | ||
53 | } | ||
54 | |||
55 | // ParseAddress translates a GNUnet address string like | ||
56 | // "r5n+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". | ||
58 | func ParseAddress(s string) (addr *Address, err error) { | ||
59 | p := strings.SplitN(s, ":", 2) | ||
60 | if len(p) != 2 { | ||
61 | err = fmt.Errorf("invalid address format: '%s'", s) | ||
62 | return | ||
63 | } | ||
64 | addr = NewAddress(p[0], []byte(strings.Trim(p[1], "/"))) | ||
65 | return | ||
66 | } | ||
67 | |||
68 | // Equals return true if two addresses match. | ||
69 | func (a *Address) Equals(b *Address) bool { | ||
70 | return a.Netw == b.Netw && | ||
71 | a.Options == b.Options && | ||
72 | bytes.Equal(a.Address, b.Address) | ||
73 | } | ||
74 | |||
75 | // StringAll returns a human-readable representation of an address. | ||
76 | func (a *Address) StringAll() string { | ||
77 | return a.Netw + "://" + string(a.Address) | ||
78 | } | ||
79 | |||
80 | // implement net.Addr interface methods: | ||
81 | |||
45 | // String returns a human-readable representation of an address. | 82 | // String returns a human-readable representation of an address. |
46 | func (a *Address) String() string { | 83 | func (a *Address) String() string { |
47 | return fmt.Sprintf("Address{%s}", AddressString(a.Transport, a.Address)) | 84 | return string(a.Address) |
85 | } | ||
86 | |||
87 | // Network returns the protocol specifier. | ||
88 | func (a *Address) Network() string { | ||
89 | return a.Netw | ||
48 | } | 90 | } |
49 | 91 | ||
50 | //---------------------------------------------------------------------- | 92 | //---------------------------------------------------------------------- |
51 | 93 | ||
52 | // AddressString returns a string representaion of an address. | 94 | // AddressString returns a string representaion of an address. |
53 | func AddressString(transport string, addr []byte) string { | 95 | func AddressString(network string, addr []byte) string { |
54 | if transport == "tcp" || transport == "udp" { | 96 | return network + "://" + string(addr) |
55 | alen := len(addr) | ||
56 | port := uint(addr[alen-2])*256 + uint(addr[alen-1]) | ||
57 | return fmt.Sprintf("%s:%s:%d", transport, net.IP(addr[:alen-2]).String(), port) | ||
58 | } | ||
59 | return fmt.Sprintf("%s:%s", transport, hex.EncodeToString(addr)) | ||
60 | } | 97 | } |
61 | 98 | ||
62 | //---------------------------------------------------------------------- | 99 | //---------------------------------------------------------------------- |
@@ -76,3 +113,71 @@ func NewIPAddress(host []byte, port uint16) *IPAddress { | |||
76 | copy(ip.Host, host) | 113 | copy(ip.Host, host) |
77 | return ip | 114 | return ip |
78 | } | 115 | } |
116 | |||
117 | //---------------------------------------------------------------------- | ||
118 | |||
119 | // PeerAddrList is a list of addresses per peer ID. | ||
120 | type PeerAddrList struct { | ||
121 | list *Map[string, []*Address] | ||
122 | } | ||
123 | |||
124 | // NewPeerAddrList returns a new and empty address list. | ||
125 | func NewPeerAddrList() *PeerAddrList { | ||
126 | return &PeerAddrList{ | ||
127 | list: NewMap[string, []*Address](), | ||
128 | } | ||
129 | } | ||
130 | |||
131 | // Add address for peer. The returned mode is 0=not added, 1=new peer, | ||
132 | // 2=new address | ||
133 | func (a *PeerAddrList) Add(id string, addr *Address) (mode int) { | ||
134 | // check for expired address. | ||
135 | mode = 0 | ||
136 | if !addr.Expires.Expired() { | ||
137 | // run add operation | ||
138 | a.list.Process(func() error { | ||
139 | list, ok := a.list.Get(id) | ||
140 | if !ok { | ||
141 | list = make([]*Address, 0) | ||
142 | mode = 1 | ||
143 | } else { | ||
144 | for _, a := range list { | ||
145 | if a.Equals(addr) { | ||
146 | return nil | ||
147 | } | ||
148 | } | ||
149 | mode = 2 | ||
150 | } | ||
151 | list = append(list, addr) | ||
152 | a.list.Put(id, list) | ||
153 | return nil | ||
154 | }) | ||
155 | } | ||
156 | return | ||
157 | } | ||
158 | |||
159 | // Get address for peer | ||
160 | func (a *PeerAddrList) Get(id string, transport string) *Address { | ||
161 | list, ok := a.list.Get(id) | ||
162 | if ok { | ||
163 | for _, addr := range list { | ||
164 | // check for expired address. | ||
165 | if addr.Expires.Expired() { | ||
166 | // skip expired | ||
167 | continue | ||
168 | } | ||
169 | // check for matching protocol | ||
170 | if len(transport) > 0 && transport != addr.Netw { | ||
171 | // skip other transports | ||
172 | continue | ||
173 | } | ||
174 | return addr | ||
175 | } | ||
176 | } | ||
177 | return nil | ||
178 | } | ||
179 | |||
180 | // Delete a list entry by key. | ||
181 | func (a *PeerAddrList) Delete(id string) { | ||
182 | a.list.Delete(id) | ||
183 | } | ||
diff --git a/src/gnunet/util/array.go b/src/gnunet/util/array.go index 254610b..c6d6371 100644 --- a/src/gnunet/util/array.go +++ b/src/gnunet/util/array.go | |||
@@ -24,44 +24,68 @@ import ( | |||
24 | 24 | ||
25 | // Error variables | 25 | // Error variables |
26 | var ( | 26 | var ( |
27 | ErrUtilArrayTooSmall = fmt.Errorf("Array to small") | 27 | ErrUtilArrayTooSmall = fmt.Errorf("array to small") |
28 | ) | 28 | ) |
29 | 29 | ||
30 | //---------------------------------------------------------------------- | 30 | //---------------------------------------------------------------------- |
31 | // Byte array helpers | 31 | // generic array helpers |
32 | //---------------------------------------------------------------------- | 32 | //---------------------------------------------------------------------- |
33 | 33 | ||
34 | // Clone creates a new array of same content as the argument. | 34 | // Clone creates a new array of same content as the argument. |
35 | func Clone(d []byte) []byte { | 35 | func Clone[T []E, E any](d T) T { |
36 | r := make([]byte, len(d)) | 36 | r := make(T, len(d)) |
37 | copy(r, d) | 37 | copy(r, d) |
38 | return r | 38 | return r |
39 | } | 39 | } |
40 | 40 | ||
41 | // Reverse the content of a byte array | 41 | // Equals returns true if two arrays match. |
42 | func Reverse(b []byte) []byte { | 42 | func Equals[T []E, E comparable](a, b T) bool { |
43 | if len(a) != len(b) { | ||
44 | return false | ||
45 | } | ||
46 | for i, e := range a { | ||
47 | if e != b[i] { | ||
48 | return false | ||
49 | } | ||
50 | } | ||
51 | return true | ||
52 | } | ||
53 | |||
54 | // Reverse the content of an array | ||
55 | func Reverse[T []E, E any](b T) T { | ||
43 | bl := len(b) | 56 | bl := len(b) |
44 | r := make([]byte, bl) | 57 | r := make(T, bl) |
45 | for i := 0; i < bl; i++ { | 58 | for i := 0; i < bl; i++ { |
46 | r[bl-i-1] = b[i] | 59 | r[bl-i-1] = b[i] |
47 | } | 60 | } |
48 | return r | 61 | return r |
49 | } | 62 | } |
50 | 63 | ||
51 | // IsNull returns true if all bytes in an array are set to 0. | 64 | // IsAll returns true if all elements in an array are set to null. |
52 | func IsNull(b []byte) bool { | 65 | func IsAll[T []E, E comparable](b T, null E) bool { |
53 | for _, v := range b { | 66 | for _, v := range b { |
54 | if v != 0 { | 67 | if v != null { |
55 | return false | 68 | return false |
56 | } | 69 | } |
57 | } | 70 | } |
58 | return true | 71 | return true |
59 | } | 72 | } |
60 | 73 | ||
61 | // CopyBlock copies 'in' to 'out' so that 'out' is filled completely. | 74 | // Fill an array with a value |
75 | func Fill[T []E, E any](b T, val E) { | ||
76 | for i := range b { | ||
77 | b[i] = val | ||
78 | } | ||
79 | } | ||
80 | |||
81 | //---------------------------------------------------------------------- | ||
82 | // byte array helpers | ||
83 | //---------------------------------------------------------------------- | ||
84 | |||
85 | // CopyAlignedBlock copies 'in' to 'out' so that 'out' is filled completely. | ||
62 | // - If 'in' is larger than 'out', it is left-truncated before copy | 86 | // - If 'in' is larger than 'out', it is left-truncated before copy |
63 | // - If 'in' is smaller than 'out', it is left-padded with 0 before copy | 87 | // - If 'in' is smaller than 'out', it is left-padded with 0 before copy |
64 | func CopyBlock(out, in []byte) { | 88 | func CopyAlignedBlock(out, in []byte) { |
65 | count := len(in) | 89 | count := len(in) |
66 | size := len(out) | 90 | size := len(out) |
67 | from, to := 0, 0 | 91 | from, to := 0, 0 |
@@ -76,27 +100,10 @@ func CopyBlock(out, in []byte) { | |||
76 | copy(out[to:], in[from:]) | 100 | copy(out[to:], in[from:]) |
77 | } | 101 | } |
78 | 102 | ||
79 | // Fill an array with a value | ||
80 | func Fill(b []byte, val byte) { | ||
81 | for i := 0; i < len(b); i++ { | ||
82 | b[i] = val | ||
83 | } | ||
84 | } | ||
85 | |||
86 | //---------------------------------------------------------------------- | 103 | //---------------------------------------------------------------------- |
87 | // String list helpers | 104 | // String list helpers |
88 | //---------------------------------------------------------------------- | 105 | //---------------------------------------------------------------------- |
89 | 106 | ||
90 | // ReverseStringList reverse an array of strings | ||
91 | func ReverseStringList(s []string) []string { | ||
92 | sl := len(s) | ||
93 | r := make([]string, sl) | ||
94 | for i := 0; i < sl; i++ { | ||
95 | r[sl-i-1] = s[i] | ||
96 | } | ||
97 | return r | ||
98 | } | ||
99 | |||
100 | // StringList converts a binary representation of a string list. Each string | 107 | // StringList converts a binary representation of a string list. Each string |
101 | // is '\0'-terminated. The whole byte array is parsed; if the final string is | 108 | // is '\0'-terminated. The whole byte array is parsed; if the final string is |
102 | // not terminated, it is skipped. | 109 | // not terminated, it is skipped. |
diff --git a/src/gnunet/util/database.go b/src/gnunet/util/database.go index 5805a8f..a1198fd 100644 --- a/src/gnunet/util/database.go +++ b/src/gnunet/util/database.go | |||
@@ -19,6 +19,7 @@ | |||
19 | package util | 19 | package util |
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "context" | ||
22 | "database/sql" | 23 | "database/sql" |
23 | "fmt" | 24 | "fmt" |
24 | "os" | 25 | "os" |
@@ -34,7 +35,96 @@ var ( | |||
34 | ErrSQLNoDatabase = fmt.Errorf("Database not found") | 35 | ErrSQLNoDatabase = fmt.Errorf("Database not found") |
35 | ) | 36 | ) |
36 | 37 | ||
37 | // ConnectSQLDatabase connects to an SQL database (various types and flavors): | 38 | //---------------------------------------------------------------------- |
39 | // Connection to a database instance. There can be multiple connections | ||
40 | // on the same instance, managed by the database pool. | ||
41 | //---------------------------------------------------------------------- | ||
42 | |||
43 | // DbConn is a database connection suitable for executing SQL commands. | ||
44 | type DbConn struct { | ||
45 | conn *sql.Conn // connection to database instance | ||
46 | pool *dbPool // reference to managng pool | ||
47 | key string // database identifier (connect string) | ||
48 | } | ||
49 | |||
50 | // Close database connection. | ||
51 | func (db *DbConn) Close() (err error) { | ||
52 | if err = db.conn.Close(); err != nil { | ||
53 | return | ||
54 | } | ||
55 | err = db.pool.remove(db.key) | ||
56 | return | ||
57 | } | ||
58 | |||
59 | // QueryRow returns a single record for a query | ||
60 | func (db *DbConn) QueryRow(query string, args ...any) *sql.Row { | ||
61 | return db.conn.QueryRowContext(db.pool.ctx, query, args...) | ||
62 | } | ||
63 | |||
64 | // Query returns all matching records for a query | ||
65 | func (db *DbConn) Query(query string, args ...any) (*sql.Rows, error) { | ||
66 | return db.conn.QueryContext(db.pool.ctx, query, args...) | ||
67 | } | ||
68 | |||
69 | // Exec a SQL statement | ||
70 | func (db *DbConn) Exec(query string, args ...any) (sql.Result, error) { | ||
71 | return db.conn.ExecContext(db.pool.ctx, query, args...) | ||
72 | } | ||
73 | |||
74 | // TODO: add more SQL methods | ||
75 | |||
76 | //---------------------------------------------------------------------- | ||
77 | // DbPool holds all database instances used: Connecting with the same | ||
78 | // connect string returns the same instance. | ||
79 | //---------------------------------------------------------------------- | ||
80 | |||
81 | // global instance for the database pool (singleton) | ||
82 | var ( | ||
83 | DbPool *dbPool | ||
84 | ) | ||
85 | |||
86 | // DbPoolEntry holds information about a database instance. | ||
87 | type DbPoolEntry struct { | ||
88 | db *sql.DB // reference to the database engine | ||
89 | refs int // number of open connections (reference count) | ||
90 | connect string // SQL connect string | ||
91 | } | ||
92 | |||
93 | // package initialization | ||
94 | func init() { | ||
95 | // construct database pool | ||
96 | DbPool = new(dbPool) | ||
97 | DbPool.insts = NewMap[string, *DbPoolEntry]() | ||
98 | DbPool.ctx, DbPool.cancel = context.WithCancel(context.Background()) | ||
99 | } | ||
100 | |||
101 | // dbPool keeps a mapping between connect string and database instance | ||
102 | type dbPool struct { | ||
103 | ctx context.Context // connection context | ||
104 | cancel context.CancelFunc // cancel function | ||
105 | insts *Map[string, *DbPoolEntry] // map of database instances | ||
106 | } | ||
107 | |||
108 | // remove a database instance from the pool based on its connect string. | ||
109 | func (p *dbPool) remove(key string) error { | ||
110 | return p.insts.Process(func() (err error) { | ||
111 | // get pool entry | ||
112 | pe, ok := p.insts.Get(key) | ||
113 | if !ok { | ||
114 | return nil | ||
115 | } | ||
116 | // decrement ref count | ||
117 | pe.refs-- | ||
118 | if pe.refs == 0 { | ||
119 | // no more refs: close database | ||
120 | err = pe.db.Close() | ||
121 | p.insts.Delete(key) | ||
122 | } | ||
123 | return | ||
124 | }) | ||
125 | } | ||
126 | |||
127 | // Connect to a SQL database (various types and flavors): | ||
38 | // The 'spec' option defines the arguments required to connect to a database; | 128 | // The 'spec' option defines the arguments required to connect to a database; |
39 | // the meaning and format of the arguments depends on the specific SQL database. | 129 | // the meaning and format of the arguments depends on the specific SQL database. |
40 | // The arguments are seperated by the '+' character; the first (and mandatory) | 130 | // The arguments are seperated by the '+' character; the first (and mandatory) |
@@ -46,27 +136,50 @@ var ( | |||
46 | // * 'mysql': A MySQL-compatible database; the second argument specifies the | 136 | // * 'mysql': A MySQL-compatible database; the second argument specifies the |
47 | // information required to log into the database (e.g. | 137 | // information required to log into the database (e.g. |
48 | // "[user[:passwd]@][proto[(addr)]]/dbname[?param1=value1&...]"). | 138 | // "[user[:passwd]@][proto[(addr)]]/dbname[?param1=value1&...]"). |
49 | func ConnectSQLDatabase(spec string) (db *sql.DB, err error) { | 139 | func (p *dbPool) Connect(spec string) (db *DbConn, err error) { |
50 | // split spec string into segments | 140 | err = p.insts.Process(func() error { |
51 | specs := strings.Split(spec, ":") | 141 | // check if we have a connection to this database. |
52 | if len(specs) < 2 { | 142 | inst, ok := p.insts.Get(spec) |
53 | return nil, ErrSQLInvalidDatabaseSpec | 143 | if !ok { |
54 | } | 144 | inst = new(DbPoolEntry) |
55 | switch specs[0] { | 145 | inst.refs = 0 |
56 | case "sqlite3": | 146 | inst.connect = spec |
57 | // check if the database file exists | 147 | |
58 | var fi os.FileInfo | 148 | // No: create new database instance. |
59 | if fi, err = os.Stat(specs[1]); err != nil { | 149 | // split spec string into segments |
60 | return nil, ErrSQLNoDatabase | 150 | specs := strings.Split(spec, ":") |
61 | } | 151 | if len(specs) < 2 { |
62 | if fi.IsDir() { | 152 | return ErrSQLInvalidDatabaseSpec |
63 | return nil, ErrSQLNoDatabase | 153 | } |
154 | // create database object | ||
155 | switch specs[0] { | ||
156 | case "sqlite3": | ||
157 | // check if the database file exists | ||
158 | var fi os.FileInfo | ||
159 | if fi, err = os.Stat(specs[1]); err != nil { | ||
160 | return ErrSQLNoDatabase | ||
161 | } | ||
162 | if fi.IsDir() { | ||
163 | return ErrSQLNoDatabase | ||
164 | } | ||
165 | // open the database file | ||
166 | inst.db, err = sql.Open("sqlite3", specs[1]) | ||
167 | case "mysql": | ||
168 | // just connect to the database | ||
169 | inst.db, err = sql.Open("mysql", specs[1]) | ||
170 | default: | ||
171 | return ErrSQLInvalidDatabaseSpec | ||
172 | } | ||
173 | // save database in pool | ||
174 | p.insts.Put(spec, inst) | ||
175 | ok = true | ||
64 | } | 176 | } |
65 | // open the database file | 177 | // increment reference count |
66 | return sql.Open("sqlite3", specs[1]) | 178 | inst.refs++ |
67 | case "mysql": | 179 | // return a new connection to the database. |
68 | // just connect to the database | 180 | db = new(DbConn) |
69 | return sql.Open("mysql", specs[1]) | 181 | db.conn, err = inst.db.Conn(p.ctx) |
70 | } | 182 | return err |
71 | return nil, ErrSQLInvalidDatabaseSpec | 183 | }) |
184 | return | ||
72 | } | 185 | } |
diff --git a/src/gnunet/util/fs.go b/src/gnunet/util/fs.go index 009ef62..b2a464e 100644 --- a/src/gnunet/util/fs.go +++ b/src/gnunet/util/fs.go | |||
@@ -25,7 +25,7 @@ import ( | |||
25 | "github.com/bfix/gospel/logger" | 25 | "github.com/bfix/gospel/logger" |
26 | ) | 26 | ) |
27 | 27 | ||
28 | // EnforceDirExists make sure that the path | 28 | // EnforceDirExists make sure that the path is created |
29 | func EnforceDirExists(path string) error { | 29 | func EnforceDirExists(path string) error { |
30 | logger.Printf(logger.DBG, "[util] Checking directory '%s'...\n", path) | 30 | logger.Printf(logger.DBG, "[util] Checking directory '%s'...\n", path) |
31 | fi, err := os.Lstat(path) | 31 | fi, err := os.Lstat(path) |
diff --git a/src/gnunet/util/key_value_store.go b/src/gnunet/util/key_value_store.go deleted file mode 100644 index 0658218..0000000 --- a/src/gnunet/util/key_value_store.go +++ /dev/null | |||
@@ -1,188 +0,0 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | ||
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | ||
3 | // | ||
4 | // gnunet-go is free software: you can redistribute it and/or modify it | ||
5 | // under the terms of the GNU Affero General Public License as published | ||
6 | // by the Free Software Foundation, either version 3 of the License, | ||
7 | // or (at your option) any later version. | ||
8 | // | ||
9 | // gnunet-go is distributed in the hope that it will be useful, but | ||
10 | // WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | // Affero General Public License for more details. | ||
13 | // | ||
14 | // You should have received a copy of the GNU Affero General Public License | ||
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | // | ||
17 | // SPDX-License-Identifier: AGPL3.0-or-later | ||
18 | |||
19 | package util | ||
20 | |||
21 | import ( | ||
22 | "context" | ||
23 | "database/sql" | ||
24 | "fmt" | ||
25 | "strconv" | ||
26 | "strings" | ||
27 | |||
28 | redis "github.com/go-redis/redis/v8" | ||
29 | ) | ||
30 | |||
31 | // Error messages related to the key/value-store implementations | ||
32 | var ( | ||
33 | ErrKVSInvalidSpec = fmt.Errorf("Invalid KVStore specification") | ||
34 | ErrKVSNotAvailable = fmt.Errorf("KVStore not available") | ||
35 | ) | ||
36 | |||
37 | // KeyValueStore interface for implementations that store and retrieve | ||
38 | // key/value pairs. Keys and values are strings. | ||
39 | type KeyValueStore interface { | ||
40 | Put(key string, value string) error // put a key/value pair into store | ||
41 | Get(key string) (string, error) // retrieve a value for a key from store | ||
42 | List() ([]string, error) // get all keys from the store | ||
43 | } | ||
44 | |||
45 | // OpenKVStore opens a key/value store for further put/get operations. | ||
46 | // The 'spec' option specifies the arguments required to connect to a specific | ||
47 | // persistence mechanism. The arguments in the 'spec' string are separated by | ||
48 | // the '+' character. | ||
49 | // The first argument specifies the type of key/value store to be used; the | ||
50 | // meaning and format of the following arguments depend on this type. | ||
51 | // | ||
52 | // Key/Value Store types defined: | ||
53 | // * 'redis': Use a Redis server for persistance; the specification is | ||
54 | // "redis+addr+[passwd]+db". 'db' must be an integer value. | ||
55 | // * 'mysql': MySQL-compatible database (see 'database.go' for details) | ||
56 | // * 'sqlite3': SQLite3-compatible database (see 'database.go' for details) | ||
57 | func OpenKVStore(spec string) (KeyValueStore, error) { | ||
58 | // check specification string | ||
59 | specs := strings.Split(spec, "+") | ||
60 | if len(specs) < 2 { | ||
61 | return nil, ErrKVSInvalidSpec | ||
62 | } | ||
63 | switch specs[0] { | ||
64 | case "redis": | ||
65 | //-------------------------------------------------------------- | ||
66 | // NoSQL-based persistance | ||
67 | //-------------------------------------------------------------- | ||
68 | if len(specs) < 4 { | ||
69 | return nil, ErrKVSInvalidSpec | ||
70 | } | ||
71 | db, err := strconv.Atoi(specs[3]) | ||
72 | if err != nil { | ||
73 | return nil, ErrKVSInvalidSpec | ||
74 | } | ||
75 | kvs := new(KvsRedis) | ||
76 | kvs.db = db | ||
77 | kvs.client = redis.NewClient(&redis.Options{ | ||
78 | Addr: specs[1], | ||
79 | Password: specs[2], | ||
80 | DB: db, | ||
81 | }) | ||
82 | if kvs.client == nil { | ||
83 | err = ErrKVSNotAvailable | ||
84 | } | ||
85 | return kvs, err | ||
86 | |||
87 | case "sqlite3", "mysql": | ||
88 | //-------------------------------------------------------------- | ||
89 | // SQL-based persistance | ||
90 | //-------------------------------------------------------------- | ||
91 | kvs := new(KvsSQL) | ||
92 | var err error | ||
93 | |||
94 | // connect to SQL database | ||
95 | kvs.db, err = ConnectSQLDatabase(spec) | ||
96 | if err != nil { | ||
97 | return nil, err | ||
98 | } | ||
99 | // get number of key/value pairs (as a check for existing table) | ||
100 | row := kvs.db.QueryRow("select count(*) from store") | ||
101 | var num int | ||
102 | if row.Scan(&num) != nil { | ||
103 | return nil, ErrKVSNotAvailable | ||
104 | } | ||
105 | return kvs, nil | ||
106 | } | ||
107 | return nil, ErrKVSInvalidSpec | ||
108 | } | ||
109 | |||
110 | //====================================================================== | ||
111 | // NoSQL-based key-value-stores | ||
112 | //====================================================================== | ||
113 | |||
114 | // KvsRedis represents a redis-based key/value store | ||
115 | type KvsRedis struct { | ||
116 | client *redis.Client // client connection | ||
117 | db int // index to database | ||
118 | } | ||
119 | |||
120 | // Put a key/value pair into the store | ||
121 | func (kvs *KvsRedis) Put(key string, value string) error { | ||
122 | return kvs.client.Set(context.TODO(), key, value, 0).Err() | ||
123 | } | ||
124 | |||
125 | // Get a value for a given key from store | ||
126 | func (kvs *KvsRedis) Get(key string) (value string, err error) { | ||
127 | return kvs.client.Get(context.TODO(), key).Result() | ||
128 | } | ||
129 | |||
130 | // List all keys in store | ||
131 | func (kvs *KvsRedis) List() (keys []string, err error) { | ||
132 | var ( | ||
133 | crs uint64 | ||
134 | segm []string | ||
135 | ctx = context.TODO() | ||
136 | ) | ||
137 | for { | ||
138 | segm, crs, err = kvs.client.Scan(ctx, crs, "*", 10).Result() | ||
139 | if err != nil { | ||
140 | return nil, err | ||
141 | } | ||
142 | if crs == 0 { | ||
143 | break | ||
144 | } | ||
145 | keys = append(keys, segm...) | ||
146 | } | ||
147 | return | ||
148 | } | ||
149 | |||
150 | //====================================================================== | ||
151 | // SQL-based key-value-store | ||
152 | //====================================================================== | ||
153 | |||
154 | // KvsSQL represents a SQL-based key/value store | ||
155 | type KvsSQL struct { | ||
156 | db *sql.DB | ||
157 | } | ||
158 | |||
159 | // Put a key/value pair into the store | ||
160 | func (kvs *KvsSQL) Put(key string, value string) error { | ||
161 | _, err := kvs.db.Exec("insert into store(key,value) values(?,?)", key, value) | ||
162 | return err | ||
163 | } | ||
164 | |||
165 | // Get a value for a given key from store | ||
166 | func (kvs *KvsSQL) Get(key string) (value string, err error) { | ||
167 | row := kvs.db.QueryRow("select value from store where key=?", key) | ||
168 | err = row.Scan(&value) | ||
169 | return | ||
170 | } | ||
171 | |||
172 | // List all keys in store | ||
173 | func (kvs *KvsSQL) List() (keys []string, err error) { | ||
174 | var ( | ||
175 | rows *sql.Rows | ||
176 | key string | ||
177 | ) | ||
178 | rows, err = kvs.db.Query("select key from store") | ||
179 | if err == nil { | ||
180 | for rows.Next() { | ||
181 | if err = rows.Scan(&key); err != nil { | ||
182 | break | ||
183 | } | ||
184 | keys = append(keys, key) | ||
185 | } | ||
186 | } | ||
187 | return | ||
188 | } | ||
diff --git a/src/gnunet/util/misc.go b/src/gnunet/util/misc.go index 66744bd..7240757 100644 --- a/src/gnunet/util/misc.go +++ b/src/gnunet/util/misc.go | |||
@@ -20,13 +20,18 @@ package util | |||
20 | 20 | ||
21 | import ( | 21 | import ( |
22 | "strings" | 22 | "strings" |
23 | "sync" | ||
23 | ) | 24 | ) |
24 | 25 | ||
25 | // CounterMap is a metric with single key | 26 | //---------------------------------------------------------------------- |
26 | type CounterMap map[interface{}]int | 27 | // Count occurence of multiple instance at the same time. |
28 | //---------------------------------------------------------------------- | ||
29 | |||
30 | // Counter is a metric with single key | ||
31 | type Counter[T comparable] map[T]int | ||
27 | 32 | ||
28 | // Add one to themetric for a given key and return current value | 33 | // Add one to themetric for a given key and return current value |
29 | func (cm CounterMap) Add(i interface{}) int { | 34 | func (cm Counter[T]) Add(i T) int { |
30 | count, ok := cm[i] | 35 | count, ok := cm[i] |
31 | if !ok { | 36 | if !ok { |
32 | count = 1 | 37 | count = 1 |
@@ -38,7 +43,7 @@ func (cm CounterMap) Add(i interface{}) int { | |||
38 | } | 43 | } |
39 | 44 | ||
40 | // Num returns the metric for a given key | 45 | // Num returns the metric for a given key |
41 | func (cm CounterMap) Num(i interface{}) int { | 46 | func (cm Counter[T]) Num(i T) int { |
42 | count, ok := cm[i] | 47 | count, ok := cm[i] |
43 | if !ok { | 48 | if !ok { |
44 | count = 0 | 49 | count = 0 |
@@ -46,6 +51,68 @@ func (cm CounterMap) Num(i interface{}) int { | |||
46 | return count | 51 | return count |
47 | } | 52 | } |
48 | 53 | ||
54 | //---------------------------------------------------------------------- | ||
55 | // Thread-safe map implementation | ||
56 | //---------------------------------------------------------------------- | ||
57 | |||
58 | // Map keys to values | ||
59 | type Map[K comparable, V any] struct { | ||
60 | list map[K]V | ||
61 | mtx sync.RWMutex | ||
62 | inProcess bool | ||
63 | } | ||
64 | |||
65 | // NewMap allocates a new mapping. | ||
66 | func NewMap[K comparable, V any]() *Map[K, V] { | ||
67 | return &Map[K, V]{ | ||
68 | list: make(map[K]V), | ||
69 | inProcess: false, | ||
70 | } | ||
71 | } | ||
72 | |||
73 | // Process a function in the locked map context. Calls | ||
74 | // to other map functions in 'f' will use additional locks. | ||
75 | func (m *Map[K, V]) Process(f func() error) error { | ||
76 | m.mtx.Lock() | ||
77 | defer m.mtx.Unlock() | ||
78 | m.inProcess = true | ||
79 | err := f() | ||
80 | m.inProcess = false | ||
81 | return err | ||
82 | } | ||
83 | |||
84 | // Put value into map under given key. | ||
85 | func (m *Map[K, V]) Put(key K, value V) { | ||
86 | if !m.inProcess { | ||
87 | m.mtx.Lock() | ||
88 | defer m.mtx.Unlock() | ||
89 | } | ||
90 | m.list[key] = value | ||
91 | } | ||
92 | |||
93 | // Get value with iven key from map. | ||
94 | func (m *Map[K, V]) Get(key K) (value V, ok bool) { | ||
95 | if !m.inProcess { | ||
96 | m.mtx.RLock() | ||
97 | defer m.mtx.RUnlock() | ||
98 | } | ||
99 | value, ok = m.list[key] | ||
100 | return | ||
101 | } | ||
102 | |||
103 | // Delete key/value pair from map. | ||
104 | func (m *Map[K, V]) Delete(key K) { | ||
105 | if !m.inProcess { | ||
106 | m.mtx.Lock() | ||
107 | defer m.mtx.Unlock() | ||
108 | } | ||
109 | delete(m.list, key) | ||
110 | } | ||
111 | |||
112 | //---------------------------------------------------------------------- | ||
113 | // additional helpers | ||
114 | //---------------------------------------------------------------------- | ||
115 | |||
49 | // StripPathRight returns a dot-separated path without | 116 | // StripPathRight returns a dot-separated path without |
50 | // its last (right-most) element. | 117 | // its last (right-most) element. |
51 | func StripPathRight(s string) string { | 118 | func StripPathRight(s string) string { |
diff --git a/src/gnunet/util/peer_id.go b/src/gnunet/util/peer_id.go index f4c14bd..a8202a1 100644 --- a/src/gnunet/util/peer_id.go +++ b/src/gnunet/util/peer_id.go | |||
@@ -1,5 +1,5 @@ | |||
1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. | 1 | // This file is part of gnunet-go, a GNUnet-implementation in Golang. |
2 | // Copyright (C) 2019, 2020 Bernd Fix >Y< | 2 | // Copyright (C) 2019-2022 Bernd Fix >Y< |
3 | // | 3 | // |
4 | // gnunet-go is free software: you can redistribute it and/or modify it | 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 | 5 | // under the terms of the GNU Affero General Public License as published |
@@ -18,6 +18,8 @@ | |||
18 | 18 | ||
19 | package util | 19 | package util |
20 | 20 | ||
21 | import "bytes" | ||
22 | |||
21 | // PeerID is the 32-byte binary representation od a Ed25519 key | 23 | // PeerID is the 32-byte binary representation od a Ed25519 key |
22 | type PeerID struct { | 24 | type PeerID struct { |
23 | Key []byte `size:"32"` | 25 | Key []byte `size:"32"` |
@@ -33,7 +35,7 @@ func NewPeerID(data []byte) *PeerID { | |||
33 | data = data[:32] | 35 | data = data[:32] |
34 | } else if size < 32 { | 36 | } else if size < 32 { |
35 | buf := make([]byte, 32) | 37 | buf := make([]byte, 32) |
36 | CopyBlock(buf, data) | 38 | CopyAlignedBlock(buf, data) |
37 | data = buf | 39 | data = buf |
38 | } | 40 | } |
39 | } | 41 | } |
@@ -42,6 +44,11 @@ func NewPeerID(data []byte) *PeerID { | |||
42 | } | 44 | } |
43 | } | 45 | } |
44 | 46 | ||
47 | // Equals returns true if two peer IDs match. | ||
48 | func (p *PeerID) Equals(q *PeerID) bool { | ||
49 | return bytes.Equal(p.Key, q.Key) | ||
50 | } | ||
51 | |||
45 | // String returns a human-readable representation of a peer id. | 52 | // String returns a human-readable representation of a peer id. |
46 | func (p *PeerID) String() string { | 53 | func (p *PeerID) String() string { |
47 | return EncodeBinaryToString(p.Key) | 54 | return EncodeBinaryToString(p.Key) |
diff --git a/src/gnunet/util/time.go b/src/gnunet/util/time.go index e70635c..70b91e1 100644 --- a/src/gnunet/util/time.go +++ b/src/gnunet/util/time.go | |||
@@ -43,6 +43,13 @@ func NewAbsoluteTime(t time.Time) AbsoluteTime { | |||
43 | } | 43 | } |
44 | } | 44 | } |
45 | 45 | ||
46 | // NewAbsoluteTimeEpoch set the point in time to the given time value | ||
47 | func NewAbsoluteTimeEpoch(secs uint64) AbsoluteTime { | ||
48 | return AbsoluteTime{ | ||
49 | Val: uint64(secs * 1000000), | ||
50 | } | ||
51 | } | ||
52 | |||
46 | // AbsoluteTimeNow returns the current point in time. | 53 | // AbsoluteTimeNow returns the current point in time. |
47 | func AbsoluteTimeNow() AbsoluteTime { | 54 | func AbsoluteTimeNow() AbsoluteTime { |
48 | return NewAbsoluteTime(time.Now()) | 55 | return NewAbsoluteTime(time.Now()) |
@@ -53,6 +60,11 @@ func AbsoluteTimeNever() AbsoluteTime { | |||
53 | return AbsoluteTime{math.MaxUint64} | 60 | return AbsoluteTime{math.MaxUint64} |
54 | } | 61 | } |
55 | 62 | ||
63 | // Epoch returns the seconds since Unix epoch. | ||
64 | func (t AbsoluteTime) Epoch() uint64 { | ||
65 | return t.Val / 1000000 | ||
66 | } | ||
67 | |||
56 | // String returns a human-readable notation of an absolute time. | 68 | // String returns a human-readable notation of an absolute time. |
57 | func (t AbsoluteTime) String() string { | 69 | func (t AbsoluteTime) String() string { |
58 | if t.Val == math.MaxUint64 { | 70 | if t.Val == math.MaxUint64 { |
@@ -133,3 +145,8 @@ func (t RelativeTime) String() string { | |||
133 | } | 145 | } |
134 | return time.Duration(t.Val * 1000).String() | 146 | return time.Duration(t.Val * 1000).String() |
135 | } | 147 | } |
148 | |||
149 | // Add two durations | ||
150 | func (t RelativeTime) Add(t2 RelativeTime) { | ||
151 | t.Val += t2.Val | ||
152 | } | ||