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