]> Cypherpunks.ru repositories - nncp.git/blobdiff - src/cypherpunks.ru/nncp/cfg.go
Forbid any later GNU GPL versions autousage
[nncp.git] / src / cypherpunks.ru / nncp / cfg.go
index 9092be08c83fd48fbe6ec55277f2ff9fa121c6ca..203752bf8d8d0e2111e94a474500e8c8c530ab09 100644 (file)
@@ -1,11 +1,10 @@
 /*
 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
@@ -19,28 +18,61 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 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 {
@@ -59,18 +91,17 @@ type FromToYAML 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) {
@@ -95,12 +126,15 @@ 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
@@ -120,23 +154,134 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                }
                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
@@ -224,14 +369,32 @@ func (nodeOur *NodeOur) ToYAML() string {
 }
 
 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) {
@@ -242,12 +405,11 @@ func CfgParse(data []byte) (*Ctx, error) {
                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 {
@@ -270,6 +432,7 @@ func CfgParse(data []byte) (*Ctx, error) {
                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)