/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
package nncp
import (
+ "bytes"
"errors"
+ "log"
+ "os"
"path"
+ "github.com/gorhill/cronexpr"
"golang.org/x/crypto/ed25519"
+ "golang.org/x/crypto/ssh/terminal"
"gopkg.in/yaml.v2"
)
+const (
+ CfgPathEnv = "NNCPCFG"
+ CfgSpoolEnv = "NNCPSPOOL"
+ CfgLogEnv = "NNCPLOG"
+)
+
var (
DefaultCfgPath string = "/usr/local/etc/nncp.yaml"
DefaultSendmailPath string = "/usr/sbin/sendmail"
+ DefaultSpoolPath string = "/var/spool/nncp"
+ DefaultLogPath string = "/var/spool/nncp/log"
)
type NodeYAML struct {
- Id string
- ExchPub string
- SignPub string
- NoisePub string
- Incoming *string `incoming,omitempty`
- Freq *string `freq,omitempty`
- Via []string `via,omitempty`
+ Id string
+ ExchPub string
+ SignPub string
+ NoisePub *string `yaml:"noisepub,omitempty"`
+ Exec map[string][]string `yaml:"exec,omitempty"`
+ Incoming *string `yaml:"incoming,omitempty"`
+ Freq *string `yaml:"freq,omitempty"`
+ FreqChunked *uint64 `yaml:"freqchunked,omitempty"`
+ FreqMinSize *uint64 `yaml:"freqminsize,omitempty"`
+ Via []string `yaml:"via,omitempty"`
+ Calls []CallYAML `yaml:"calls,omitempty"`
+
+ Addrs map[string]string `yaml:"addrs,omitempty"`
+
+ RxRate *int `yaml:"rxrate,omitempty"`
+ TxRate *int `yaml:"txrate,omitempty"`
+ OnlineDeadline *uint `yaml:"onlinedeadline,omitempty"`
+ MaxOnlineTime *uint `yaml:"maxonlinetime,omitempty"`
+}
- Addrs map[string]string `addrs,omitempty`
+type CallYAML struct {
+ Cron string
+ Nice *string `yaml:"nice,omitempty"`
+ Xx string `yaml:"xx,omitempty"`
+ RxRate *int `yaml:"rxrate,omitempty"`
+ TxRate *int `yaml:"txrate,omitempty"`
+ Addr *string `yaml:"addr,omitempty"`
+ OnlineDeadline *uint `yaml:"onlinedeadline,omitempty"`
+ MaxOnlineTime *uint `yaml:"maxonlinetime,omitempty"`
}
type NodeOurYAML struct {
}
type NotifyYAML struct {
- File *FromToYAML `file,omitempty`
- Freq *FromToYAML `freq,omitempty`
+ File *FromToYAML `yaml:"file,omitempty"`
+ Freq *FromToYAML `yaml:"freq,omitempty"`
}
type CfgYAML struct {
- Self NodeOurYAML
+ Self *NodeOurYAML `yaml:"self,omitempty"`
Neigh map[string]NodeYAML
- Spool string
- Log string
- Sendmail []string
- Notify *NotifyYAML `notify,omitempty`
+ Spool string
+ Log string
+ Notify *NotifyYAML `yaml:"notify,omitempty"`
}
func NewNode(name string, yml NodeYAML) (*Node, error) {
return nil, errors.New("Invalid signPub size")
}
- noisePub, err := FromBase32(yml.NoisePub)
- if err != nil {
- return nil, err
- }
- if len(noisePub) != 32 {
- return nil, errors.New("Invalid noisePub size")
+ var noisePub []byte
+ if yml.NoisePub != nil {
+ noisePub, err = FromBase32(*yml.NoisePub)
+ if err != nil {
+ return nil, err
+ }
+ if len(noisePub) != 32 {
+ return nil, errors.New("Invalid noisePub size")
+ }
}
var incoming *string
}
freq = &fr
}
+ var freqChunked int64
+ if yml.FreqChunked != nil {
+ if *yml.FreqChunked == 0 {
+ return nil, errors.New("freqchunked value must be greater than zero")
+ }
+ freqChunked = int64(*yml.FreqChunked) * 1024
+ }
+ var freqMinSize int64
+ if yml.FreqMinSize != nil {
+ freqMinSize = int64(*yml.FreqMinSize) * 1024
+ }
+
+ defRxRate := 0
+ if yml.RxRate != nil && *yml.RxRate > 0 {
+ defRxRate = *yml.RxRate
+ }
+ defTxRate := 0
+ if yml.TxRate != nil && *yml.TxRate > 0 {
+ defTxRate = *yml.TxRate
+ }
+
+ defOnlineDeadline := uint(DefaultDeadline)
+ if yml.OnlineDeadline != nil {
+ if *yml.OnlineDeadline <= 0 {
+ return nil, errors.New("OnlineDeadline must be at least 1 second")
+ }
+ defOnlineDeadline = *yml.OnlineDeadline
+ }
+ var defMaxOnlineTime uint
+ if yml.MaxOnlineTime != nil {
+ defMaxOnlineTime = *yml.MaxOnlineTime
+ }
+
+ var calls []*Call
+ for _, callYml := range yml.Calls {
+ expr, err := cronexpr.Parse(callYml.Cron)
+ if err != nil {
+ return nil, err
+ }
+
+ nice := uint8(255)
+ if callYml.Nice != nil {
+ nice, err = NicenessParse(*callYml.Nice)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ var xx TRxTx
+ switch callYml.Xx {
+ case "rx":
+ xx = TRx
+ case "tx":
+ xx = TTx
+ case "":
+ default:
+ return nil, errors.New("xx field must be either \"rx\" or \"tx\"")
+ }
+
+ rxRate := defRxRate
+ if callYml.RxRate != nil {
+ rxRate = *callYml.RxRate
+ }
+ txRate := defTxRate
+ if callYml.TxRate != nil {
+ txRate = *callYml.TxRate
+ }
+
+ var addr *string
+ if callYml.Addr != nil {
+ if a, exists := yml.Addrs[*callYml.Addr]; exists {
+ addr = &a
+ } else {
+ addr = callYml.Addr
+ }
+ }
+
+ onlineDeadline := defOnlineDeadline
+ if callYml.OnlineDeadline != nil {
+ if *callYml.OnlineDeadline == 0 {
+ return nil, errors.New("OnlineDeadline must be at least 1 second")
+ }
+ onlineDeadline = *callYml.OnlineDeadline
+ }
+
+ var maxOnlineTime uint
+ if callYml.MaxOnlineTime != nil {
+ maxOnlineTime = *callYml.MaxOnlineTime
+ }
+
+ calls = append(calls, &Call{
+ Cron: expr,
+ Nice: nice,
+ Xx: xx,
+ RxRate: rxRate,
+ TxRate: txRate,
+ Addr: addr,
+ OnlineDeadline: onlineDeadline,
+ MaxOnlineTime: maxOnlineTime,
+ })
+ }
node := Node{
- Name: name,
- Id: nodeId,
- ExchPub: new([32]byte),
- SignPub: ed25519.PublicKey(signPub),
- NoisePub: new([32]byte),
- Incoming: incoming,
- Freq: freq,
- Addrs: yml.Addrs,
+ Name: name,
+ Id: nodeId,
+ ExchPub: new([32]byte),
+ SignPub: ed25519.PublicKey(signPub),
+ Exec: yml.Exec,
+ Incoming: incoming,
+ Freq: freq,
+ FreqChunked: freqChunked,
+ FreqMinSize: freqMinSize,
+ Calls: calls,
+ Addrs: yml.Addrs,
+ RxRate: defRxRate,
+ TxRate: defTxRate,
+ OnlineDeadline: defOnlineDeadline,
+ MaxOnlineTime: defMaxOnlineTime,
}
copy(node.ExchPub[:], exchPub)
- copy(node.NoisePub[:], noisePub)
+ if len(noisePub) > 0 {
+ node.NoisePub = new([32]byte)
+ copy(node.NoisePub[:], noisePub)
+ }
return &node, nil
}
-func NewNodeOur(yml NodeOurYAML) (*NodeOur, error) {
+func NewNodeOur(yml *NodeOurYAML) (*NodeOur, error) {
id, err := NodeIdFromString(yml.Id)
if err != nil {
return nil, err
}
func CfgParse(data []byte) (*Ctx, error) {
+ var err error
+ if bytes.Compare(data[:8], MagicNNCPBv3[:]) == 0 {
+ os.Stderr.WriteString("Passphrase:")
+ password, err := terminal.ReadPassword(0)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ os.Stderr.WriteString("\n")
+ data, err = DeEBlob(data, password)
+ if err != nil {
+ return nil, err
+ }
+ }
var cfgYAML CfgYAML
- err := yaml.Unmarshal(data, &cfgYAML)
- if err != nil {
+ if err = yaml.Unmarshal(data, &cfgYAML); err != nil {
return nil, err
}
- self, err := NewNodeOur(cfgYAML.Self)
- if err != nil {
- return nil, err
+ if _, exists := cfgYAML.Neigh["self"]; !exists {
+ return nil, errors.New("self neighbour missing")
+ }
+ var self *NodeOur
+ if cfgYAML.Self != nil {
+ self, err = NewNodeOur(cfgYAML.Self)
+ if err != nil {
+ return nil, err
+ }
}
spoolPath := path.Clean(cfgYAML.Spool)
if !path.IsAbs(spoolPath) {
return nil, errors.New("Log path must be absolute")
}
ctx := Ctx{
- Spool: spoolPath,
- LogPath: logPath,
- Self: self,
- Neigh: make(map[NodeId]*Node, len(cfgYAML.Neigh)),
- Alias: make(map[string]*NodeId),
- Sendmail: cfgYAML.Sendmail,
+ Spool: spoolPath,
+ LogPath: logPath,
+ Self: self,
+ Neigh: make(map[NodeId]*Node, len(cfgYAML.Neigh)),
+ Alias: make(map[string]*NodeId),
}
if cfgYAML.Notify != nil {
if cfgYAML.Notify.File != nil {
ctx.Alias[name] = neigh.Id
vias[*neigh.Id] = neighYAML.Via
}
+ ctx.SelfId = ctx.Alias["self"]
for neighId, viasRaw := range vias {
for _, viaRaw := range viasRaw {
foundNodeId, err := ctx.FindNode(viaRaw)