1 //go:build !noyggdrasil
5 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
6 Copyright (C) 2016-2023 Sergey Matveev <stargrave@stargrave.org>
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.
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.
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/>.
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"
43 const DefaultPort = 5400
45 // Copy-pasted from yggdrasil-go/src/ipv6rwc/ipv6rwc.go,
46 // because they are non-exportable.
56 stacks map[string]*TCPIPEndpoint
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)
68 func ycoreStart(cfg *ycfg.NodeConfig, port int, mcasts []string) (*ycore.Core, error) {
70 for _, mcast := range mcasts {
71 cols := strings.SplitN(mcast, ":", 2)
75 mport, err = strconv.Atoi(cols[1])
80 cfg.MulticastInterfaces = append(
81 cfg.MulticastInterfaces, ycfg.MulticastInterfaceConfig{
90 sk, err := hex.DecodeString(cfg.PrivateKey)
94 options := []ycore.SetupOption{
95 ycore.NodeInfo(cfg.NodeInfo),
96 ycore.NodeInfoPrivacy(cfg.NodeInfoPrivacy),
98 for _, addr := range cfg.Listen {
99 options = append(options, ycore.ListenAddress(addr))
101 for _, peer := range cfg.Peers {
102 options = append(options, ycore.Peer{URI: peer})
104 for intf, peers := range cfg.InterfacePeers {
105 for _, peer := range peers {
106 options = append(options, ycore.Peer{URI: peer, SourceInterface: intf})
109 for _, allowed := range cfg.AllowedPublicKeys {
110 k, err := hex.DecodeString(allowed)
114 options = append(options, ycore.AllowedPublicKey(k[:]))
117 core, err := ycore.New(sk[:], glog, options...)
123 options := []ymcast.SetupOption{}
124 for _, intf := range cfg.MulticastInterfaces {
125 options = append(options, ymcast.MulticastInterface{
126 Regex: regexp.MustCompile(intf.Regex),
130 Priority: uint8(intf.Priority),
133 if _, err = ymcast.New(core, glog, options...); err != nil {
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())
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)
150 if u.Scheme != "yggdrasilc" {
151 return nil, errors.New("expected yggdrasilc:// scheme")
154 pubHex := u.Hostname()
155 if v, ok := aliases[pubHex]; ok {
158 pubRaw, err := hex.DecodeString(pubHex)
164 if p := u.Port(); p != "" {
165 port, err = strconv.Atoi(p)
172 if len(values["prv"]) == 0 {
173 return nil, errors.New("yggdrasilc:// misses prv field")
175 prvHex := values["prv"][0]
176 if v, ok := aliases[prvHex]; ok {
179 prvRaw, err := hex.DecodeString(prvHex)
183 if len(prvRaw) != ed25519.PrivateKeySize {
184 return nil, errors.New("invalid private key size")
187 for _, peer := range values["peer"] {
188 if v, ok := aliases[peer]; ok {
191 peers = append(peers, peer)
194 for _, mcast := range values["mcast"] {
195 if v, ok := aliases[mcast]; ok {
198 mcasts = append(mcasts, mcast)
201 addrOur := yaddr.AddrForKey(ed25519.PublicKey(
202 prvRaw[len(prvRaw)-ed25519.PublicKeySize:],
204 ipOur := net.IP(addrOur[:])
205 addrTheir := yaddr.AddrForKey(ed25519.PublicKey(pubRaw))
206 ipTheir := net.IP(addrTheir[:])
208 copy(ip[:], addrTheir[:])
211 defer stacksM.Unlock()
212 e, ok := stacks[prvHex]
214 e.ipToAddr[ip] = iwt.Addr(pubRaw)
215 return e.DialTCP(&net.TCPAddr{IP: ipTheir, Port: port})
217 cfg := ycfg.NodeConfig{
220 NodeInfo: map[string]interface{}{"name": "NNCP"},
221 NodeInfoPrivacy: true,
223 core, err := ycoreStart(&cfg, port, mcasts)
227 e, err = NewTCPIPEndpoint(core, ipOur, uint32(core.MTU()))
231 e.ipToAddr[ip] = iwt.Addr(pubRaw)
233 return e.DialTCP(&net.TCPAddr{IP: ipTheir, Port: port})
236 type OOBState struct {
241 func (state *OOBState) Handler(fromKey, toKey ed25519.PublicKey, data []byte) {
242 if len(data) != 1+ed25519.SignatureSize {
245 if data[0] == typeKeyLookup {
246 snet := *yaddr.SubnetForKey(toKey)
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[:])...,
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)
263 if u.Scheme != "yggdrasils" {
264 return nil, errors.New("expected yggdrasils:// scheme")
267 prvHex := u.Hostname()
268 if v, ok := aliases[prvHex]; ok {
271 prvRaw, err := hex.DecodeString(prvHex)
275 if len(prvRaw) != ed25519.PrivateKeySize {
276 return nil, errors.New("invalid private key size")
280 if p := u.Port(); p != "" {
281 port, err = strconv.Atoi(p)
289 for _, bind := range values["bind"] {
290 if v, ok := aliases[bind]; ok {
293 binds = append(binds, bind)
296 for _, pub := range values["pub"] {
297 if v, ok := aliases[pub]; ok {
300 pubs = append(pubs, pub)
303 for _, peer := range values["peer"] {
304 if v, ok := aliases[peer]; ok {
307 peers = append(peers, peer)
310 for _, mcast := range values["mcast"] {
311 if v, ok := aliases[mcast]; ok {
314 mcasts = append(mcasts, mcast)
317 addrOur := yaddr.AddrForKey(ed25519.PublicKey(
318 prvRaw[len(prvRaw)-ed25519.PublicKeySize:],
320 ipOur := net.IP(addrOur[:])
323 defer stacksM.Unlock()
324 e, ok := stacks[prvHex]
326 return e.ListenTCP(&net.TCPAddr{IP: ipOur, Port: port})
328 cfg := ycfg.NodeConfig{
331 AllowedPublicKeys: pubs,
333 NodeInfo: map[string]interface{}{"name": "NNCP"},
334 NodeInfoPrivacy: true,
336 core, err := ycoreStart(&cfg, port, mcasts)
340 oobState := OOBState{core, *yaddr.SubnetForKey(core.PublicKey())}
341 if err := core.SetOutOfBandHandler(oobState.Handler); err != nil {
345 e, err = NewTCPIPEndpoint(core, ipOur, uint32(core.MTU()))
350 ln, err := e.ListenTCP(&net.TCPAddr{IP: ipOur, Port: port})