X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=src%2Fcfg.go;h=d46bf0e08ce8dbbed9dfddef04f6fc722100b02e;hb=55b235efb2f86748c0466d50cdfecf685b72ab71;hp=deebd1981d8d5c3cb80417e5ab1ebcbf40d68032;hpb=0139e8deda4112d2c3dcd52e0ad72162e54caa03;p=nncp.git diff --git a/src/cfg.go b/src/cfg.go index deebd19..d46bf0e 100644 --- a/src/cfg.go +++ b/src/cfg.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2019 Sergey Matveev +Copyright (C) 2016-2021 Sergey Matveev 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 @@ -21,14 +21,17 @@ import ( "bytes" "encoding/json" "errors" + "fmt" "log" "os" "path" + "strconv" + "time" "github.com/gorhill/cronexpr" "github.com/hjson/hjson-go" "golang.org/x/crypto/ed25519" - "golang.org/x/crypto/ssh/terminal" + "golang.org/x/term" ) const ( @@ -45,17 +48,15 @@ var ( ) type NodeJSON struct { - Id string `json:"id"` - ExchPub string `json:"exchpub"` - SignPub string `json:"signpub"` - NoisePub *string `json:"noisepub,omitempty"` - Exec map[string][]string `json:"exec,omitempty"` - Incoming *string `json:"incoming,omitempty"` - Freq *string `json:"freq,omitempty"` - FreqChunked *uint64 `json:"freqchunked,omitempty"` - FreqMinSize *uint64 `json:"freqminsize,omitempty"` - Via []string `json:"via,omitempty"` - Calls []CallJSON `json:"calls,omitempty"` + Id string `json:"id"` + ExchPub string `json:"exchpub"` + SignPub string `json:"signpub"` + NoisePub *string `json:"noisepub,omitempty"` + Incoming *string `json:"incoming,omitempty"` + Exec map[string][]string `json:"exec,omitempty"` + Freq *NodeFreqJSON `json:"freq,omitempty"` + Via []string `json:"via,omitempty"` + Calls []CallJSON `json:"calls,omitempty"` Addrs map[string]string `json:"addrs,omitempty"` @@ -65,8 +66,15 @@ type NodeJSON struct { MaxOnlineTime *uint `json:"maxonlinetime,omitempty"` } +type NodeFreqJSON struct { + Path *string `json:"path,omitempty"` + Chunked *uint64 `json:"chunked,omitempty"` + MinSize *uint64 `json:"minsize,omitempty"` + MaxSize *uint64 `json:"maxsize,omitempty"` +} + type CallJSON struct { - Cron string + Cron string `json:"cron"` Nice *string `json:"nice,omitempty"` Xx *string `json:"xx,omitempty"` RxRate *int `json:"rxrate,omitempty"` @@ -74,6 +82,17 @@ type CallJSON struct { Addr *string `json:"addr,omitempty"` OnlineDeadline *uint `json:"onlinedeadline,omitempty"` MaxOnlineTime *uint `json:"maxonlinetime,omitempty"` + WhenTxExists bool `json:"when-tx-exists,omitempty"` + NoCK bool `json:"nock,omitempty"` + MCDIgnore bool `json:"mcd-ignore,omitempty"` + + AutoToss bool `json:"autotoss,omitempty"` + AutoTossDoSeen bool `json:"autotoss-doseen,omitempty"` + AutoTossNoFile bool `json:"autotoss-nofile,omitempty"` + AutoTossNoFreq bool `json:"autotoss-nofreq,omitempty"` + AutoTossNoExec bool `json:"autotoss-noexec,omitempty"` + AutoTossNoTrns bool `json:"autotoss-notrns,omitempty"` + AutoTossNoArea bool `json:"autotoss-noarea,omitempty"` } type NodeOurJSON struct { @@ -82,36 +101,60 @@ type NodeOurJSON struct { ExchPrv string `json:"exchprv"` SignPub string `json:"signpub"` SignPrv string `json:"signprv"` - NoisePrv string `json:"noiseprv"` NoisePub string `json:"noisepub"` + NoisePrv string `json:"noiseprv"` } type FromToJSON struct { - From string - To string + From string `json:"from"` + To string `json:"to"` } type NotifyJSON struct { - File *FromToJSON `json:"file,omitempty"` - Freq *FromToJSON `json:"freq,omitempty"` + File *FromToJSON `json:"file,omitempty"` + Freq *FromToJSON `json:"freq,omitempty"` + Exec map[string]*FromToJSON `json:"exec,omitempty"` +} + +type AreaJSON struct { + Id string `json:"id"` + Pub *string `json:"pub,omitempty"` + Prv *string `json:"prv,omitempty"` + + Subs []string `json:"subs"` + + Incoming *string `json:"incoming,omitempty"` + Exec map[string][]string `json:"exec,omitempty"` + + AllowUnknown bool `json:"allow-unknown,omitempty"` } type CfgJSON struct { - Spool string `json:"spool"` - Log string `json:"log"` + Spool string `json:"spool"` + Log string `json:"log"` + Umask *string `json:"umask,omitempty"` + + OmitPrgrs bool `json:"noprogress,omitempty"` + NoHdr bool `json:"nohdr,omitempty"` + + MCDRxIfis []string `json:"mcd-listen,omitempty"` + MCDTxIfis map[string]int `json:"mcd-send,omitempty"` + Notify *NotifyJSON `json:"notify,omitempty"` Self *NodeOurJSON `json:"self"` Neigh map[string]NodeJSON `json:"neigh"` + + Areas map[string]AreaJSON `json:"areas,omitempty"` } -func NewNode(name string, yml NodeJSON) (*Node, error) { - nodeId, err := NodeIdFromString(yml.Id) +func NewNode(name string, cfg NodeJSON) (*Node, error) { + nodeId, err := NodeIdFromString(cfg.Id) if err != nil { return nil, err } - exchPub, err := FromBase32(yml.ExchPub) + exchPub, err := Base32Codec.DecodeString(cfg.ExchPub) if err != nil { return nil, err } @@ -119,7 +162,7 @@ func NewNode(name string, yml NodeJSON) (*Node, error) { return nil, errors.New("Invalid exchPub size") } - signPub, err := FromBase32(yml.SignPub) + signPub, err := Base32Codec.DecodeString(cfg.SignPub) if err != nil { return nil, err } @@ -128,8 +171,8 @@ func NewNode(name string, yml NodeJSON) (*Node, error) { } var noisePub []byte - if yml.NoisePub != nil { - noisePub, err = FromBase32(*yml.NoisePub) + if cfg.NoisePub != nil { + noisePub, err = Base32Codec.DecodeString(*cfg.NoisePub) if err != nil { return nil, err } @@ -139,73 +182,80 @@ func NewNode(name string, yml NodeJSON) (*Node, error) { } var incoming *string - if yml.Incoming != nil { - inc := path.Clean(*yml.Incoming) + if cfg.Incoming != nil { + inc := path.Clean(*cfg.Incoming) if !path.IsAbs(inc) { return nil, errors.New("Incoming path must be absolute") } incoming = &inc } - var freq *string - if yml.Freq != nil { - fr := path.Clean(*yml.Freq) - if !path.IsAbs(fr) { - return nil, errors.New("Freq path must be absolute") - } - freq = &fr - } + var freqPath *string 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 + freqMaxSize := int64(MaxFileSize) + if cfg.Freq != nil { + f := cfg.Freq + if f.Path != nil { + fPath := path.Clean(*f.Path) + if !path.IsAbs(fPath) { + return nil, errors.New("freq.path path must be absolute") + } + freqPath = &fPath + } + if f.Chunked != nil { + if *f.Chunked == 0 { + return nil, errors.New("freq.chunked value must be greater than zero") + } + freqChunked = int64(*f.Chunked) * 1024 + } + if f.MinSize != nil { + freqMinSize = int64(*f.MinSize) * 1024 + } + if f.MaxSize != nil { + freqMaxSize = int64(*f.MaxSize) * 1024 + } } defRxRate := 0 - if yml.RxRate != nil && *yml.RxRate > 0 { - defRxRate = *yml.RxRate + if cfg.RxRate != nil && *cfg.RxRate > 0 { + defRxRate = *cfg.RxRate } defTxRate := 0 - if yml.TxRate != nil && *yml.TxRate > 0 { - defTxRate = *yml.TxRate + if cfg.TxRate != nil && *cfg.TxRate > 0 { + defTxRate = *cfg.TxRate } - defOnlineDeadline := uint(DefaultDeadline) - if yml.OnlineDeadline != nil { - if *yml.OnlineDeadline <= 0 { + defOnlineDeadline := DefaultDeadline + if cfg.OnlineDeadline != nil { + if *cfg.OnlineDeadline <= 0 { return nil, errors.New("OnlineDeadline must be at least 1 second") } - defOnlineDeadline = *yml.OnlineDeadline + defOnlineDeadline = time.Duration(*cfg.OnlineDeadline) * time.Second } - var defMaxOnlineTime uint - if yml.MaxOnlineTime != nil { - defMaxOnlineTime = *yml.MaxOnlineTime + var defMaxOnlineTime time.Duration + if cfg.MaxOnlineTime != nil { + defMaxOnlineTime = time.Duration(*cfg.MaxOnlineTime) * time.Second } var calls []*Call - for _, callYml := range yml.Calls { - expr, err := cronexpr.Parse(callYml.Cron) + for _, callCfg := range cfg.Calls { + expr, err := cronexpr.Parse(callCfg.Cron) if err != nil { return nil, err } nice := uint8(255) - if callYml.Nice != nil { - nice, err = NicenessParse(*callYml.Nice) + if callCfg.Nice != nil { + nice, err = NicenessParse(*callCfg.Nice) if err != nil { return nil, err } } var xx TRxTx - if callYml.Xx != nil { - switch *callYml.Xx { + if callCfg.Xx != nil { + switch *callCfg.Xx { case "rx": xx = TRx case "tx": @@ -216,37 +266,32 @@ func NewNode(name string, yml NodeJSON) (*Node, error) { } rxRate := defRxRate - if callYml.RxRate != nil { - rxRate = *callYml.RxRate + if callCfg.RxRate != nil { + rxRate = *callCfg.RxRate } txRate := defTxRate - if callYml.TxRate != nil { - txRate = *callYml.TxRate + if callCfg.TxRate != nil { + txRate = *callCfg.TxRate } var addr *string - if callYml.Addr != nil { - if a, exists := yml.Addrs[*callYml.Addr]; exists { + if callCfg.Addr != nil { + if a, exists := cfg.Addrs[*callCfg.Addr]; exists { addr = &a } else { - addr = callYml.Addr + addr = callCfg.Addr } } onlineDeadline := defOnlineDeadline - if callYml.OnlineDeadline != nil { - if *callYml.OnlineDeadline == 0 { + if callCfg.OnlineDeadline != nil { + if *callCfg.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 + onlineDeadline = time.Duration(*callCfg.OnlineDeadline) * time.Second } - calls = append(calls, &Call{ + call := Call{ Cron: expr, Nice: nice, Xx: xx, @@ -254,8 +299,23 @@ func NewNode(name string, yml NodeJSON) (*Node, error) { TxRate: txRate, Addr: addr, OnlineDeadline: onlineDeadline, - MaxOnlineTime: maxOnlineTime, - }) + } + + if callCfg.MaxOnlineTime != nil { + call.MaxOnlineTime = time.Duration(*callCfg.MaxOnlineTime) * time.Second + } + call.WhenTxExists = callCfg.WhenTxExists + call.NoCK = callCfg.NoCK + call.MCDIgnore = callCfg.MCDIgnore + call.AutoToss = callCfg.AutoToss + call.AutoTossDoSeen = callCfg.AutoTossDoSeen + call.AutoTossNoFile = callCfg.AutoTossNoFile + call.AutoTossNoFreq = callCfg.AutoTossNoFreq + call.AutoTossNoExec = callCfg.AutoTossNoExec + call.AutoTossNoTrns = callCfg.AutoTossNoTrns + call.AutoTossNoArea = callCfg.AutoTossNoArea + + calls = append(calls, &call) } node := Node{ @@ -263,13 +323,14 @@ func NewNode(name string, yml NodeJSON) (*Node, error) { Id: nodeId, ExchPub: new([32]byte), SignPub: ed25519.PublicKey(signPub), - Exec: yml.Exec, + Exec: cfg.Exec, Incoming: incoming, - Freq: freq, + FreqPath: freqPath, FreqChunked: freqChunked, FreqMinSize: freqMinSize, + FreqMaxSize: freqMaxSize, Calls: calls, - Addrs: yml.Addrs, + Addrs: cfg.Addrs, RxRate: defRxRate, TxRate: defTxRate, OnlineDeadline: defOnlineDeadline, @@ -283,13 +344,13 @@ func NewNode(name string, yml NodeJSON) (*Node, error) { return &node, nil } -func NewNodeOur(yml *NodeOurJSON) (*NodeOur, error) { - id, err := NodeIdFromString(yml.Id) +func NewNodeOur(cfg *NodeOurJSON) (*NodeOur, error) { + id, err := NodeIdFromString(cfg.Id) if err != nil { return nil, err } - exchPub, err := FromBase32(yml.ExchPub) + exchPub, err := Base32Codec.DecodeString(cfg.ExchPub) if err != nil { return nil, err } @@ -297,7 +358,7 @@ func NewNodeOur(yml *NodeOurJSON) (*NodeOur, error) { return nil, errors.New("Invalid exchPub size") } - exchPrv, err := FromBase32(yml.ExchPrv) + exchPrv, err := Base32Codec.DecodeString(cfg.ExchPrv) if err != nil { return nil, err } @@ -305,7 +366,7 @@ func NewNodeOur(yml *NodeOurJSON) (*NodeOur, error) { return nil, errors.New("Invalid exchPrv size") } - signPub, err := FromBase32(yml.SignPub) + signPub, err := Base32Codec.DecodeString(cfg.SignPub) if err != nil { return nil, err } @@ -313,7 +374,7 @@ func NewNodeOur(yml *NodeOurJSON) (*NodeOur, error) { return nil, errors.New("Invalid signPub size") } - signPrv, err := FromBase32(yml.SignPrv) + signPrv, err := Base32Codec.DecodeString(cfg.SignPrv) if err != nil { return nil, err } @@ -321,7 +382,7 @@ func NewNodeOur(yml *NodeOurJSON) (*NodeOur, error) { return nil, errors.New("Invalid signPrv size") } - noisePub, err := FromBase32(yml.NoisePub) + noisePub, err := Base32Codec.DecodeString(cfg.NoisePub) if err != nil { return nil, err } @@ -329,7 +390,7 @@ func NewNodeOur(yml *NodeOurJSON) (*NodeOur, error) { return nil, errors.New("Invalid noisePub size") } - noisePrv, err := FromBase32(yml.NoisePrv) + noisePrv, err := Base32Codec.DecodeString(cfg.NoisePrv) if err != nil { return nil, err } @@ -353,11 +414,60 @@ func NewNodeOur(yml *NodeOurJSON) (*NodeOur, error) { return &node, nil } -func CfgParse(data []byte) (*Ctx, error) { +func NewArea(ctx *Ctx, name string, cfg *AreaJSON) (*Area, error) { + areaId, err := AreaIdFromString(cfg.Id) + if err != nil { + return nil, err + } + subs := make([]*NodeId, 0, len(cfg.Subs)) + for _, s := range cfg.Subs { + node, err := ctx.FindNode(s) + if err != nil { + return nil, err + } + subs = append(subs, node.Id) + } + area := Area{ + Name: name, + Id: areaId, + Subs: subs, + Exec: cfg.Exec, + Incoming: cfg.Incoming, + } + if cfg.Pub != nil { + pub, err := Base32Codec.DecodeString(*cfg.Pub) + if err != nil { + return nil, err + } + if len(pub) != 32 { + return nil, errors.New("Invalid pub size") + } + area.Pub = new([32]byte) + copy(area.Pub[:], pub) + } + if cfg.Prv != nil { + if area.Pub == nil { + return nil, fmt.Errorf("area %s: prv requires pub presence", name) + } + prv, err := Base32Codec.DecodeString(*cfg.Prv) + if err != nil { + return nil, err + } + if len(prv) != 32 { + return nil, errors.New("Invalid prv size") + } + area.Prv = new([32]byte) + copy(area.Prv[:], prv) + } + area.AllowUnknown = cfg.AllowUnknown + return &area, nil +} + +func CfgParse(data []byte) (*CfgJSON, error) { var err error - if bytes.Compare(data[:8], MagicNNCPBv3[:]) == 0 { + if bytes.Compare(data[:8], MagicNNCPBv3.B[:]) == 0 { os.Stderr.WriteString("Passphrase:") - password, err := terminal.ReadPassword(0) + password, err := term.ReadPassword(0) if err != nil { log.Fatalln(err) } @@ -366,6 +476,10 @@ func CfgParse(data []byte) (*Ctx, error) { if err != nil { return nil, err } + } else if bytes.Compare(data[:8], MagicNNCPBv2.B[:]) == 0 { + log.Fatalln(MagicNNCPBv2.TooOld()) + } else if bytes.Compare(data[:8], MagicNNCPBv1.B[:]) == 0 { + log.Fatalln(MagicNNCPBv1.TooOld()) } var cfgGeneral map[string]interface{} if err = hjson.Unmarshal(data, &cfgGeneral); err != nil { @@ -376,14 +490,17 @@ func CfgParse(data []byte) (*Ctx, error) { return nil, err } var cfgJSON CfgJSON - if err = json.Unmarshal(marshaled, &cfgJSON); err != nil { - return nil, err - } + err = json.Unmarshal(marshaled, &cfgJSON) + return &cfgJSON, err +} + +func Cfg2Ctx(cfgJSON *CfgJSON) (*Ctx, error) { if _, exists := cfgJSON.Neigh["self"]; !exists { return nil, errors.New("self neighbour missing") } var self *NodeOur if cfgJSON.Self != nil { + var err error self, err = NewNodeOur(cfgJSON.Self) if err != nil { return nil, err @@ -397,12 +514,34 @@ func CfgParse(data []byte) (*Ctx, error) { if !path.IsAbs(logPath) { return nil, errors.New("Log path must be absolute") } + var umaskForce *int + if cfgJSON.Umask != nil { + r, err := strconv.ParseUint(*cfgJSON.Umask, 8, 16) + if err != nil { + return nil, err + } + rInt := int(r) + umaskForce = &rInt + } + showPrgrs := true + if cfgJSON.OmitPrgrs { + showPrgrs = false + } + hdrUsage := true + if cfgJSON.NoHdr { + hdrUsage = false + } ctx := Ctx{ - Spool: spoolPath, - LogPath: logPath, - Self: self, - Neigh: make(map[NodeId]*Node, len(cfgJSON.Neigh)), - Alias: make(map[string]*NodeId), + Spool: spoolPath, + LogPath: logPath, + UmaskForce: umaskForce, + ShowPrgrs: showPrgrs, + HdrUsage: hdrUsage, + Self: self, + Neigh: make(map[NodeId]*Node, len(cfgJSON.Neigh)), + Alias: make(map[string]*NodeId), + MCDRxIfis: cfgJSON.MCDRxIfis, + MCDTxIfis: cfgJSON.MCDTxIfis, } if cfgJSON.Notify != nil { if cfgJSON.Notify.File != nil { @@ -411,6 +550,9 @@ func CfgParse(data []byte) (*Ctx, error) { if cfgJSON.Notify.Freq != nil { ctx.NotifyFreq = cfgJSON.Notify.Freq } + if cfgJSON.Notify.Exec != nil { + ctx.NotifyExec = cfgJSON.Notify.Exec + } } vias := make(map[NodeId][]string) for name, neighJSON := range cfgJSON.Neigh { @@ -438,5 +580,15 @@ func CfgParse(data []byte) (*Ctx, error) { ) } } + ctx.AreaId2Area = make(map[AreaId]*Area, len(cfgJSON.Areas)) + ctx.AreaName2Id = make(map[string]*AreaId, len(cfgJSON.Areas)) + for name, areaJSON := range cfgJSON.Areas { + area, err := NewArea(&ctx, name, &areaJSON) + if err != nil { + return nil, err + } + ctx.AreaId2Area[*area.Id] = area + ctx.AreaName2Id[name] = area.Id + } return &ctx, nil }