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
48 stacks map[string]*TCPIPEndpoint
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)
60 func ycoreStart(cfg *ycfg.NodeConfig, port int, mcasts []string) (*ycore.Core, error) {
62 for _, mcast := range mcasts {
63 cols := strings.SplitN(mcast, ":", 2)
67 mport, err = strconv.Atoi(cols[1])
72 cfg.MulticastInterfaces = append(
73 cfg.MulticastInterfaces, ycfg.MulticastInterfaceConfig{
82 options := []ycore.SetupOption{
83 ycore.NodeInfo(cfg.NodeInfo),
84 ycore.NodeInfoPrivacy(cfg.NodeInfoPrivacy),
86 for _, addr := range cfg.Listen {
87 options = append(options, ycore.ListenAddress(addr))
89 for _, peer := range cfg.Peers {
90 options = append(options, ycore.Peer{URI: peer})
92 for intf, peers := range cfg.InterfacePeers {
93 for _, peer := range peers {
94 options = append(options, ycore.Peer{URI: peer, SourceInterface: intf})
97 for _, allowed := range cfg.AllowedPublicKeys {
98 k, err := hex.DecodeString(allowed)
102 options = append(options, ycore.AllowedPublicKey(k[:]))
105 err = cfg.GenerateSelfSignedCertificate()
109 core, err := ycore.New(cfg.Certificate, glog, options...)
115 options := []ymcast.SetupOption{}
116 for _, intf := range cfg.MulticastInterfaces {
117 options = append(options, ymcast.MulticastInterface{
118 Regex: regexp.MustCompile(intf.Regex),
122 Priority: uint8(intf.Priority),
125 if _, err = ymcast.New(core, glog, options...); err != nil {
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())
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)
142 if u.Scheme != "yggdrasilc" {
143 return nil, errors.New("expected yggdrasilc:// scheme")
146 pubHex := u.Hostname()
147 if v, ok := aliases[pubHex]; ok {
150 pubRaw, err := hex.DecodeString(pubHex)
156 if p := u.Port(); p != "" {
157 port, err = strconv.Atoi(p)
164 if len(values["prv"]) == 0 {
165 return nil, errors.New("yggdrasilc:// misses prv field")
167 prvHex := values["prv"][0]
168 if v, ok := aliases[prvHex]; ok {
171 prvRaw, err := hex.DecodeString(prvHex)
175 if len(prvRaw) != ed25519.PrivateKeySize {
176 return nil, errors.New("invalid private key size")
179 for _, peer := range values["peer"] {
180 if v, ok := aliases[peer]; ok {
183 peers = append(peers, peer)
186 for _, mcast := range values["mcast"] {
187 if v, ok := aliases[mcast]; ok {
190 mcasts = append(mcasts, mcast)
193 addrOur := yaddr.AddrForKey(ed25519.PublicKey(
194 prvRaw[len(prvRaw)-ed25519.PublicKeySize:],
196 ipOur := net.IP(addrOur[:])
197 addrTheir := yaddr.AddrForKey(ed25519.PublicKey(pubRaw))
198 ipTheir := net.IP(addrTheir[:])
200 copy(ip[:], addrTheir[:])
203 defer stacksM.Unlock()
204 e, ok := stacks[prvHex]
206 e.ipToAddr[ip] = iwt.Addr(pubRaw)
207 return e.DialTCP(&net.TCPAddr{IP: ipTheir, Port: port})
209 cfg := ycfg.NodeConfig{
212 NodeInfo: map[string]interface{}{"name": "NNCP"},
213 NodeInfoPrivacy: true,
215 core, err := ycoreStart(&cfg, port, mcasts)
219 e, err = NewTCPIPEndpoint(core, ipOur, uint32(core.MTU()))
223 e.ipToAddr[ip] = iwt.Addr(pubRaw)
225 return e.DialTCP(&net.TCPAddr{IP: ipTheir, Port: port})
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)
234 if u.Scheme != "yggdrasils" {
235 return nil, errors.New("expected yggdrasils:// scheme")
238 prvHex := u.Hostname()
239 if v, ok := aliases[prvHex]; ok {
242 prvRaw, err := hex.DecodeString(prvHex)
246 if len(prvRaw) != ed25519.PrivateKeySize {
247 return nil, errors.New("invalid private key size")
251 if p := u.Port(); p != "" {
252 port, err = strconv.Atoi(p)
260 for _, bind := range values["bind"] {
261 if v, ok := aliases[bind]; ok {
264 binds = append(binds, bind)
267 for _, pub := range values["pub"] {
268 if v, ok := aliases[pub]; ok {
271 pubs = append(pubs, pub)
274 for _, peer := range values["peer"] {
275 if v, ok := aliases[peer]; ok {
278 peers = append(peers, peer)
281 for _, mcast := range values["mcast"] {
282 if v, ok := aliases[mcast]; ok {
285 mcasts = append(mcasts, mcast)
288 addrOur := yaddr.AddrForKey(ed25519.PublicKey(
289 prvRaw[len(prvRaw)-ed25519.PublicKeySize:],
291 ipOur := net.IP(addrOur[:])
294 defer stacksM.Unlock()
295 e, ok := stacks[prvHex]
297 return e.ListenTCP(&net.TCPAddr{IP: ipOur, Port: port})
299 cfg := ycfg.NodeConfig{
300 PrivateKey: ycfg.KeyBytes(prvRaw),
302 AllowedPublicKeys: pubs,
304 NodeInfo: map[string]interface{}{"name": "NNCP"},
305 NodeInfoPrivacy: true,
307 core, err := ycoreStart(&cfg, port, mcasts)
311 e, err = NewTCPIPEndpoint(core, ipOur, uint32(core.MTU()))
316 ln, err := e.ListenTCP(&net.TCPAddr{IP: ipOur, Port: port})