]> Cypherpunks.ru repositories - nncp.git/blob - src/yggdrasil/yggdrasil.go
Raise copyright years
[nncp.git] / src / yggdrasil / yggdrasil.go
1 //go:build !noyggdrasil
2 // +build !noyggdrasil
3
4 /*
5 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
6 Copyright (C) 2016-2023 Sergey Matveev <stargrave@stargrave.org>
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, version 3 of the License.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 package yggdrasil
22
23 import (
24         "encoding/hex"
25         "errors"
26         "log"
27         "net"
28         "net/url"
29         "regexp"
30         "strconv"
31         "strings"
32         "sync"
33
34         iwt "github.com/Arceliar/ironwood/types"
35         gologme "github.com/gologme/log"
36         yaddr "github.com/yggdrasil-network/yggdrasil-go/src/address"
37         ycfg "github.com/yggdrasil-network/yggdrasil-go/src/config"
38         ycore "github.com/yggdrasil-network/yggdrasil-go/src/core"
39         ymcast "github.com/yggdrasil-network/yggdrasil-go/src/multicast"
40         "golang.org/x/crypto/ed25519"
41 )
42
43 const DefaultPort = 5400
44
45 // Copy-pasted from yggdrasil-go/src/ipv6rwc/ipv6rwc.go,
46 // because they are non-exportable.
47 const (
48         typeKeyDummy = iota
49         typeKeyLookup
50         typeKeyResponse
51 )
52
53 var (
54         glog *gologme.Logger
55
56         stacks  map[string]*TCPIPEndpoint
57         stacksM sync.Mutex
58 )
59
60 func init() {
61         glog = gologme.New(log.Writer(), "yggdrasil: ", gologme.Lmsgprefix)
62         glog.EnableLevel("warn")
63         glog.EnableLevel("error")
64         glog.EnableLevel("info")
65         stacks = make(map[string]*TCPIPEndpoint)
66 }
67
68 func ycoreStart(cfg *ycfg.NodeConfig, port int, mcasts []string) (*ycore.Core, error) {
69         var err error
70         for _, mcast := range mcasts {
71                 cols := strings.SplitN(mcast, ":", 2)
72                 mport := 0
73                 if len(cols) == 2 {
74                         mcast = cols[0]
75                         mport, err = strconv.Atoi(cols[1])
76                         if err != nil {
77                                 return nil, err
78                         }
79                 }
80                 cfg.MulticastInterfaces = append(
81                         cfg.MulticastInterfaces, ycfg.MulticastInterfaceConfig{
82                                 Regex:  mcast,
83                                 Beacon: true,
84                                 Listen: true,
85                                 Port:   uint16(mport),
86                         },
87                 )
88         }
89
90         sk, err := hex.DecodeString(cfg.PrivateKey)
91         if err != nil {
92                 panic(err)
93         }
94         options := []ycore.SetupOption{
95                 ycore.NodeInfo(cfg.NodeInfo),
96                 ycore.NodeInfoPrivacy(cfg.NodeInfoPrivacy),
97         }
98         for _, addr := range cfg.Listen {
99                 options = append(options, ycore.ListenAddress(addr))
100         }
101         for _, peer := range cfg.Peers {
102                 options = append(options, ycore.Peer{URI: peer})
103         }
104         for intf, peers := range cfg.InterfacePeers {
105                 for _, peer := range peers {
106                         options = append(options, ycore.Peer{URI: peer, SourceInterface: intf})
107                 }
108         }
109         for _, allowed := range cfg.AllowedPublicKeys {
110                 k, err := hex.DecodeString(allowed)
111                 if err != nil {
112                         panic(err)
113                 }
114                 options = append(options, ycore.AllowedPublicKey(k[:]))
115         }
116
117         core, err := ycore.New(sk[:], glog, options...)
118         if err != nil {
119                 return nil, err
120         }
121         if len(mcasts) > 0 {
122
123                 options := []ymcast.SetupOption{}
124                 for _, intf := range cfg.MulticastInterfaces {
125                         options = append(options, ymcast.MulticastInterface{
126                                 Regex:    regexp.MustCompile(intf.Regex),
127                                 Beacon:   intf.Beacon,
128                                 Listen:   intf.Listen,
129                                 Port:     intf.Port,
130                                 Priority: uint8(intf.Priority),
131                         })
132                 }
133                 if _, err = ymcast.New(core, glog, options...); err != nil {
134                         core.Stop()
135                         return nil, err
136                 }
137         }
138         glog.Infoln("Public key:", hex.EncodeToString(core.GetSelf().Key))
139         glog.Infof("NNCP TCP: [%s]:%d", core.Address().String(), port)
140         glog.Infoln("MTU:", core.MTU())
141         return core, nil
142 }
143
144 func NewConn(aliases map[string]string, in string) (net.Conn, error) {
145         // yggdrasilc://PUB[:PORT]?prv=PRV[&peer=PEER][&mcast=REGEX[:PORT]]
146         u, err := url.Parse(in)
147         if err != nil {
148                 return nil, err
149         }
150         if u.Scheme != "yggdrasilc" {
151                 return nil, errors.New("expected yggdrasilc:// scheme")
152         }
153
154         pubHex := u.Hostname()
155         if v, ok := aliases[pubHex]; ok {
156                 pubHex = v
157         }
158         pubRaw, err := hex.DecodeString(pubHex)
159         if err != nil {
160                 return nil, err
161         }
162
163         port := DefaultPort
164         if p := u.Port(); p != "" {
165                 port, err = strconv.Atoi(p)
166                 if err != nil {
167                         return nil, err
168                 }
169         }
170
171         values := u.Query()
172         if len(values["prv"]) == 0 {
173                 return nil, errors.New("yggdrasilc:// misses prv field")
174         }
175         prvHex := values["prv"][0]
176         if v, ok := aliases[prvHex]; ok {
177                 prvHex = v
178         }
179         prvRaw, err := hex.DecodeString(prvHex)
180         if err != nil {
181                 return nil, err
182         }
183         if len(prvRaw) != ed25519.PrivateKeySize {
184                 return nil, errors.New("invalid private key size")
185         }
186         var peers []string
187         for _, peer := range values["peer"] {
188                 if v, ok := aliases[peer]; ok {
189                         peer = v
190                 }
191                 peers = append(peers, peer)
192         }
193         var mcasts []string
194         for _, mcast := range values["mcast"] {
195                 if v, ok := aliases[mcast]; ok {
196                         mcast = v
197                 }
198                 mcasts = append(mcasts, mcast)
199         }
200
201         addrOur := yaddr.AddrForKey(ed25519.PublicKey(
202                 prvRaw[len(prvRaw)-ed25519.PublicKeySize:],
203         ))
204         ipOur := net.IP(addrOur[:])
205         addrTheir := yaddr.AddrForKey(ed25519.PublicKey(pubRaw))
206         ipTheir := net.IP(addrTheir[:])
207         var ip yaddr.Address
208         copy(ip[:], addrTheir[:])
209
210         stacksM.Lock()
211         defer stacksM.Unlock()
212         e, ok := stacks[prvHex]
213         if ok {
214                 e.ipToAddr[ip] = iwt.Addr(pubRaw)
215                 return e.DialTCP(&net.TCPAddr{IP: ipTheir, Port: port})
216         }
217         cfg := ycfg.NodeConfig{
218                 PrivateKey:      prvHex,
219                 Peers:           peers,
220                 NodeInfo:        map[string]interface{}{"name": "NNCP"},
221                 NodeInfoPrivacy: true,
222         }
223         core, err := ycoreStart(&cfg, port, mcasts)
224         if err != nil {
225                 return nil, err
226         }
227         e, err = NewTCPIPEndpoint(core, ipOur, uint32(core.MTU()))
228         if err != nil {
229                 return nil, err
230         }
231         e.ipToAddr[ip] = iwt.Addr(pubRaw)
232         stacks[prvHex] = e
233         return e.DialTCP(&net.TCPAddr{IP: ipTheir, Port: port})
234 }
235
236 type OOBState struct {
237         c      *ycore.Core
238         subnet yaddr.Subnet
239 }
240
241 func (state *OOBState) Handler(fromKey, toKey ed25519.PublicKey, data []byte) {
242         if len(data) != 1+ed25519.SignatureSize {
243                 return
244         }
245         if data[0] == typeKeyLookup {
246                 snet := *yaddr.SubnetForKey(toKey)
247                 sig := data[1:]
248                 if snet == state.subnet && ed25519.Verify(fromKey, toKey[:], sig) {
249                         state.c.SendOutOfBand(fromKey, append(
250                                 []byte{typeKeyResponse},
251                                 ed25519.Sign(state.c.PrivateKey(), fromKey[:])...,
252                         ))
253                 }
254         }
255 }
256
257 func NewListener(aliases map[string]string, in string) (net.Listener, error) {
258         // yggdrasils://PRV[:PORT]?[bind=BIND][&pub=PUB][&peer=PEER][&mcast=REGEX[:PORT]]
259         u, err := url.Parse(in)
260         if err != nil {
261                 return nil, err
262         }
263         if u.Scheme != "yggdrasils" {
264                 return nil, errors.New("expected yggdrasils:// scheme")
265         }
266
267         prvHex := u.Hostname()
268         if v, ok := aliases[prvHex]; ok {
269                 prvHex = v
270         }
271         prvRaw, err := hex.DecodeString(prvHex)
272         if err != nil {
273                 return nil, err
274         }
275         if len(prvRaw) != ed25519.PrivateKeySize {
276                 return nil, errors.New("invalid private key size")
277         }
278
279         port := DefaultPort
280         if p := u.Port(); p != "" {
281                 port, err = strconv.Atoi(p)
282                 if err != nil {
283                         return nil, err
284                 }
285         }
286
287         values := u.Query()
288         var binds []string
289         for _, bind := range values["bind"] {
290                 if v, ok := aliases[bind]; ok {
291                         bind = v
292                 }
293                 binds = append(binds, bind)
294         }
295         var pubs []string
296         for _, pub := range values["pub"] {
297                 if v, ok := aliases[pub]; ok {
298                         pub = v
299                 }
300                 pubs = append(pubs, pub)
301         }
302         var peers []string
303         for _, peer := range values["peer"] {
304                 if v, ok := aliases[peer]; ok {
305                         peer = v
306                 }
307                 peers = append(peers, peer)
308         }
309         var mcasts []string
310         for _, mcast := range values["mcast"] {
311                 if v, ok := aliases[mcast]; ok {
312                         mcast = v
313                 }
314                 mcasts = append(mcasts, mcast)
315         }
316
317         addrOur := yaddr.AddrForKey(ed25519.PublicKey(
318                 prvRaw[len(prvRaw)-ed25519.PublicKeySize:],
319         ))
320         ipOur := net.IP(addrOur[:])
321
322         stacksM.Lock()
323         defer stacksM.Unlock()
324         e, ok := stacks[prvHex]
325         if ok {
326                 return e.ListenTCP(&net.TCPAddr{IP: ipOur, Port: port})
327         }
328         cfg := ycfg.NodeConfig{
329                 PrivateKey:        prvHex,
330                 Listen:            binds,
331                 AllowedPublicKeys: pubs,
332                 Peers:             peers,
333                 NodeInfo:          map[string]interface{}{"name": "NNCP"},
334                 NodeInfoPrivacy:   true,
335         }
336         core, err := ycoreStart(&cfg, port, mcasts)
337         if err != nil {
338                 return nil, err
339         }
340         oobState := OOBState{core, *yaddr.SubnetForKey(core.PublicKey())}
341         if err := core.SetOutOfBandHandler(oobState.Handler); err != nil {
342                 core.Stop()
343                 return nil, err
344         }
345         e, err = NewTCPIPEndpoint(core, ipOur, uint32(core.MTU()))
346         if err != nil {
347                 core.Stop()
348                 return nil, err
349         }
350         ln, err := e.ListenTCP(&net.TCPAddr{IP: ipOur, Port: port})
351         if err != nil {
352                 e.Close()
353                 core.Stop()
354                 return nil, err
355         }
356         stacks[prvHex] = e
357         return ln, nil
358 }