]> Cypherpunks.ru repositories - nncp.git/blob - src/yggdrasil/yggdrasil.go
Merge branch 'develop'
[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 var (
46         glog *gologme.Logger
47
48         stacks  map[string]*TCPIPEndpoint
49         stacksM sync.Mutex
50 )
51
52 func init() {
53         glog = gologme.New(log.Writer(), "yggdrasil: ", gologme.Lmsgprefix)
54         glog.EnableLevel("warn")
55         glog.EnableLevel("error")
56         glog.EnableLevel("info")
57         stacks = make(map[string]*TCPIPEndpoint)
58 }
59
60 func ycoreStart(cfg *ycfg.NodeConfig, port int, mcasts []string) (*ycore.Core, error) {
61         var err error
62         for _, mcast := range mcasts {
63                 cols := strings.SplitN(mcast, ":", 2)
64                 mport := 0
65                 if len(cols) == 2 {
66                         mcast = cols[0]
67                         mport, err = strconv.Atoi(cols[1])
68                         if err != nil {
69                                 return nil, err
70                         }
71                 }
72                 cfg.MulticastInterfaces = append(
73                         cfg.MulticastInterfaces, ycfg.MulticastInterfaceConfig{
74                                 Regex:  mcast,
75                                 Beacon: true,
76                                 Listen: true,
77                                 Port:   uint16(mport),
78                         },
79                 )
80         }
81
82         options := []ycore.SetupOption{
83                 ycore.NodeInfo(cfg.NodeInfo),
84                 ycore.NodeInfoPrivacy(cfg.NodeInfoPrivacy),
85         }
86         for _, addr := range cfg.Listen {
87                 options = append(options, ycore.ListenAddress(addr))
88         }
89         for _, peer := range cfg.Peers {
90                 options = append(options, ycore.Peer{URI: peer})
91         }
92         for intf, peers := range cfg.InterfacePeers {
93                 for _, peer := range peers {
94                         options = append(options, ycore.Peer{URI: peer, SourceInterface: intf})
95                 }
96         }
97         for _, allowed := range cfg.AllowedPublicKeys {
98                 k, err := hex.DecodeString(allowed)
99                 if err != nil {
100                         panic(err)
101                 }
102                 options = append(options, ycore.AllowedPublicKey(k[:]))
103         }
104
105         err = cfg.GenerateSelfSignedCertificate()
106         if err != nil {
107                 return nil, err
108         }
109         core, err := ycore.New(cfg.Certificate, glog, options...)
110         if err != nil {
111                 return nil, err
112         }
113         if len(mcasts) > 0 {
114
115                 options := []ymcast.SetupOption{}
116                 for _, intf := range cfg.MulticastInterfaces {
117                         options = append(options, ymcast.MulticastInterface{
118                                 Regex:    regexp.MustCompile(intf.Regex),
119                                 Beacon:   intf.Beacon,
120                                 Listen:   intf.Listen,
121                                 Port:     intf.Port,
122                                 Priority: uint8(intf.Priority),
123                         })
124                 }
125                 if _, err = ymcast.New(core, glog, options...); err != nil {
126                         core.Stop()
127                         return nil, err
128                 }
129         }
130         glog.Infoln("Public key:", hex.EncodeToString(core.GetSelf().Key))
131         glog.Infof("NNCP TCP: [%s]:%d", core.Address().String(), port)
132         glog.Infoln("MTU:", core.MTU())
133         return core, nil
134 }
135
136 func NewConn(aliases map[string]string, in string) (net.Conn, error) {
137         // yggdrasilc://PUB[:PORT]?prv=PRV[&peer=PEER][&mcast=REGEX[:PORT]]
138         u, err := url.Parse(in)
139         if err != nil {
140                 return nil, err
141         }
142         if u.Scheme != "yggdrasilc" {
143                 return nil, errors.New("expected yggdrasilc:// scheme")
144         }
145
146         pubHex := u.Hostname()
147         if v, ok := aliases[pubHex]; ok {
148                 pubHex = v
149         }
150         pubRaw, err := hex.DecodeString(pubHex)
151         if err != nil {
152                 return nil, err
153         }
154
155         port := DefaultPort
156         if p := u.Port(); p != "" {
157                 port, err = strconv.Atoi(p)
158                 if err != nil {
159                         return nil, err
160                 }
161         }
162
163         values := u.Query()
164         if len(values["prv"]) == 0 {
165                 return nil, errors.New("yggdrasilc:// misses prv field")
166         }
167         prvHex := values["prv"][0]
168         if v, ok := aliases[prvHex]; ok {
169                 prvHex = v
170         }
171         prvRaw, err := hex.DecodeString(prvHex)
172         if err != nil {
173                 return nil, err
174         }
175         if len(prvRaw) != ed25519.PrivateKeySize {
176                 return nil, errors.New("invalid private key size")
177         }
178         var peers []string
179         for _, peer := range values["peer"] {
180                 if v, ok := aliases[peer]; ok {
181                         peer = v
182                 }
183                 peers = append(peers, peer)
184         }
185         var mcasts []string
186         for _, mcast := range values["mcast"] {
187                 if v, ok := aliases[mcast]; ok {
188                         mcast = v
189                 }
190                 mcasts = append(mcasts, mcast)
191         }
192
193         addrOur := yaddr.AddrForKey(ed25519.PublicKey(
194                 prvRaw[len(prvRaw)-ed25519.PublicKeySize:],
195         ))
196         ipOur := net.IP(addrOur[:])
197         addrTheir := yaddr.AddrForKey(ed25519.PublicKey(pubRaw))
198         ipTheir := net.IP(addrTheir[:])
199         var ip yaddr.Address
200         copy(ip[:], addrTheir[:])
201
202         stacksM.Lock()
203         defer stacksM.Unlock()
204         e, ok := stacks[prvHex]
205         if ok {
206                 e.ipToAddr[ip] = iwt.Addr(pubRaw)
207                 return e.DialTCP(&net.TCPAddr{IP: ipTheir, Port: port})
208         }
209         cfg := ycfg.NodeConfig{
210                 PrivateKey:      prvRaw,
211                 Peers:           peers,
212                 NodeInfo:        map[string]interface{}{"name": "NNCP"},
213                 NodeInfoPrivacy: true,
214         }
215         core, err := ycoreStart(&cfg, port, mcasts)
216         if err != nil {
217                 return nil, err
218         }
219         e, err = NewTCPIPEndpoint(core, ipOur, uint32(core.MTU()))
220         if err != nil {
221                 return nil, err
222         }
223         e.ipToAddr[ip] = iwt.Addr(pubRaw)
224         stacks[prvHex] = e
225         return e.DialTCP(&net.TCPAddr{IP: ipTheir, Port: port})
226 }
227
228 func NewListener(aliases map[string]string, in string) (net.Listener, error) {
229         // yggdrasils://PRV[:PORT]?[bind=BIND][&pub=PUB][&peer=PEER][&mcast=REGEX[:PORT]]
230         u, err := url.Parse(in)
231         if err != nil {
232                 return nil, err
233         }
234         if u.Scheme != "yggdrasils" {
235                 return nil, errors.New("expected yggdrasils:// scheme")
236         }
237
238         prvHex := u.Hostname()
239         if v, ok := aliases[prvHex]; ok {
240                 prvHex = v
241         }
242         prvRaw, err := hex.DecodeString(prvHex)
243         if err != nil {
244                 return nil, err
245         }
246         if len(prvRaw) != ed25519.PrivateKeySize {
247                 return nil, errors.New("invalid private key size")
248         }
249
250         port := DefaultPort
251         if p := u.Port(); p != "" {
252                 port, err = strconv.Atoi(p)
253                 if err != nil {
254                         return nil, err
255                 }
256         }
257
258         values := u.Query()
259         var binds []string
260         for _, bind := range values["bind"] {
261                 if v, ok := aliases[bind]; ok {
262                         bind = v
263                 }
264                 binds = append(binds, bind)
265         }
266         var pubs []string
267         for _, pub := range values["pub"] {
268                 if v, ok := aliases[pub]; ok {
269                         pub = v
270                 }
271                 pubs = append(pubs, pub)
272         }
273         var peers []string
274         for _, peer := range values["peer"] {
275                 if v, ok := aliases[peer]; ok {
276                         peer = v
277                 }
278                 peers = append(peers, peer)
279         }
280         var mcasts []string
281         for _, mcast := range values["mcast"] {
282                 if v, ok := aliases[mcast]; ok {
283                         mcast = v
284                 }
285                 mcasts = append(mcasts, mcast)
286         }
287
288         addrOur := yaddr.AddrForKey(ed25519.PublicKey(
289                 prvRaw[len(prvRaw)-ed25519.PublicKeySize:],
290         ))
291         ipOur := net.IP(addrOur[:])
292
293         stacksM.Lock()
294         defer stacksM.Unlock()
295         e, ok := stacks[prvHex]
296         if ok {
297                 return e.ListenTCP(&net.TCPAddr{IP: ipOur, Port: port})
298         }
299         cfg := ycfg.NodeConfig{
300                 PrivateKey:        ycfg.KeyBytes(prvRaw),
301                 Listen:            binds,
302                 AllowedPublicKeys: pubs,
303                 Peers:             peers,
304                 NodeInfo:          map[string]interface{}{"name": "NNCP"},
305                 NodeInfoPrivacy:   true,
306         }
307         core, err := ycoreStart(&cfg, port, mcasts)
308         if err != nil {
309                 return nil, err
310         }
311         e, err = NewTCPIPEndpoint(core, ipOur, uint32(core.MTU()))
312         if err != nil {
313                 core.Stop()
314                 return nil, err
315         }
316         ln, err := e.ListenTCP(&net.TCPAddr{IP: ipOur, Port: port})
317         if err != nil {
318                 e.Close()
319                 core.Stop()
320                 return nil, err
321         }
322         stacks[prvHex] = e
323         return ln, nil
324 }