1 //go:build !noyggdrasil
5 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
6 Copyright (C) 2016-2022 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/>.
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"
42 const DefaultPort = 5400
44 // Copy-pasted from yggdrasil-go/src/ipv6rwc/ipv6rwc.go,
45 // because they are non-exportable.
55 stacks map[string]*TCPIPEndpoint
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)
67 func ycoreStart(cfg *ycfg.NodeConfig, port int, mcasts []string) (*ycore.Core, error) {
69 for _, mcast := range mcasts {
70 cols := strings.SplitN(mcast, ":", 2)
74 mport, err = strconv.Atoi(cols[1])
79 cfg.MulticastInterfaces = append(
80 cfg.MulticastInterfaces, ycfg.MulticastInterfaceConfig{
89 if err := core.Start(cfg, glog); err != nil {
93 mc := &ymcast.Multicast{}
94 if err := mc.Init(core, cfg, glog, nil); err != nil {
98 if err := mc.Start(); err != nil {
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())
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)
115 if u.Scheme != "yggdrasilc" {
116 return nil, errors.New("expected yggdrasilc:// scheme")
119 pubHex := u.Hostname()
120 if v, ok := aliases[pubHex]; ok {
123 pubRaw, err := hex.DecodeString(pubHex)
129 if p := u.Port(); p != "" {
130 port, err = strconv.Atoi(p)
137 if len(values["prv"]) == 0 {
138 return nil, errors.New("yggdrasilc:// misses prv field")
140 prvHex := values["prv"][0]
141 if v, ok := aliases[prvHex]; ok {
144 prvRaw, err := hex.DecodeString(prvHex)
148 if len(prvRaw) != ed25519.PrivateKeySize {
149 return nil, errors.New("invalid private key size")
152 for _, peer := range values["peer"] {
153 if v, ok := aliases[peer]; ok {
156 peers = append(peers, peer)
159 for _, mcast := range values["mcast"] {
160 if v, ok := aliases[mcast]; ok {
163 mcasts = append(mcasts, mcast)
166 addrOur := yaddr.AddrForKey(ed25519.PublicKey(
167 prvRaw[len(prvRaw)-ed25519.PublicKeySize:],
169 ipOur := net.IP(addrOur[:])
170 addrTheir := yaddr.AddrForKey(ed25519.PublicKey(pubRaw))
171 ipTheir := net.IP(addrTheir[:])
173 copy(ip[:], addrTheir[:])
176 defer stacksM.Unlock()
177 e, ok := stacks[prvHex]
179 e.ipToAddr[ip] = iwt.Addr(pubRaw)
180 return e.DialTCP(&net.TCPAddr{IP: ipTheir, Port: port})
182 cfg := ycfg.NodeConfig{
185 NodeInfo: map[string]interface{}{"name": "NNCP"},
186 NodeInfoPrivacy: true,
188 core, err := ycoreStart(&cfg, port, mcasts)
192 e, err = NewTCPIPEndpoint(core, ipOur, uint32(core.MTU()))
196 e.ipToAddr[ip] = iwt.Addr(pubRaw)
198 return e.DialTCP(&net.TCPAddr{IP: ipTheir, Port: port})
201 type OOBState struct {
206 func (state *OOBState) Handler(fromKey, toKey ed25519.PublicKey, data []byte) {
207 if len(data) != 1+ed25519.SignatureSize {
210 if data[0] == typeKeyLookup {
211 snet := *yaddr.SubnetForKey(toKey)
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[:])...,
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)
228 if u.Scheme != "yggdrasils" {
229 return nil, errors.New("expected yggdrasils:// scheme")
232 prvHex := u.Hostname()
233 if v, ok := aliases[prvHex]; ok {
236 prvRaw, err := hex.DecodeString(prvHex)
240 if len(prvRaw) != ed25519.PrivateKeySize {
241 return nil, errors.New("invalid private key size")
245 if p := u.Port(); p != "" {
246 port, err = strconv.Atoi(p)
254 for _, bind := range values["bind"] {
255 if v, ok := aliases[bind]; ok {
258 binds = append(binds, bind)
261 for _, pub := range values["pub"] {
262 if v, ok := aliases[pub]; ok {
265 pubs = append(pubs, pub)
268 for _, peer := range values["peer"] {
269 if v, ok := aliases[peer]; ok {
272 peers = append(peers, peer)
275 for _, mcast := range values["mcast"] {
276 if v, ok := aliases[mcast]; ok {
279 mcasts = append(mcasts, mcast)
282 addrOur := yaddr.AddrForKey(ed25519.PublicKey(
283 prvRaw[len(prvRaw)-ed25519.PublicKeySize:],
285 ipOur := net.IP(addrOur[:])
288 defer stacksM.Unlock()
289 e, ok := stacks[prvHex]
291 return e.ListenTCP(&net.TCPAddr{IP: ipOur, Port: port})
293 cfg := ycfg.NodeConfig{
296 AllowedPublicKeys: pubs,
298 NodeInfo: map[string]interface{}{"name": "NNCP"},
299 NodeInfoPrivacy: true,
301 core, err := ycoreStart(&cfg, port, mcasts)
305 oobState := OOBState{core, *yaddr.SubnetForKey(core.PublicKey())}
306 if err := core.SetOutOfBandHandler(oobState.Handler); err != nil {
310 e, err = NewTCPIPEndpoint(core, ipOur, uint32(core.MTU()))
315 ln, err := e.ListenTCP(&net.TCPAddr{IP: ipOur, Port: port})