]> Cypherpunks.ru repositories - nncp.git/blob - src/cypherpunks.ru/nncp/cfg.go
Ability to configure online deadline timeout
[nncp.git] / src / cypherpunks.ru / nncp / cfg.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package nncp
20
21 import (
22         "errors"
23         "os"
24         "path"
25
26         "golang.org/x/crypto/ed25519"
27         "gopkg.in/yaml.v2"
28 )
29
30 const (
31         CfgPathEnv = "NNCPCFG"
32 )
33
34 var (
35         DefaultCfgPath      string = "/usr/local/etc/nncp.yaml"
36         DefaultSendmailPath string = "/usr/sbin/sendmail"
37 )
38
39 type NodeYAML struct {
40         Id       string
41         ExchPub  string
42         SignPub  string
43         NoisePub *string `noisepub,omitempty`
44         Sendmail []string
45         Incoming *string  `incoming,omitempty`
46         Freq     *string  `freq,omitempty`
47         Via      []string `via,omitempty`
48
49         Addrs map[string]string `addrs,omitempty`
50
51         OnlineDeadline *int `onlinedeadline,omitempty`
52 }
53
54 type NodeOurYAML struct {
55         Id       string
56         ExchPub  string
57         ExchPrv  string
58         SignPub  string
59         SignPrv  string
60         NoisePrv string
61         NoisePub string
62 }
63
64 type FromToYAML struct {
65         From string
66         To   string
67 }
68
69 type NotifyYAML struct {
70         File *FromToYAML `file,omitempty`
71         Freq *FromToYAML `freq,omitempty`
72 }
73
74 type CfgYAML struct {
75         Self  NodeOurYAML
76         Neigh map[string]NodeYAML
77
78         Spool  string
79         Log    string
80         Notify *NotifyYAML `notify,omitempty`
81 }
82
83 func NewNode(name string, yml NodeYAML) (*Node, error) {
84         nodeId, err := NodeIdFromString(yml.Id)
85         if err != nil {
86                 return nil, err
87         }
88
89         exchPub, err := FromBase32(yml.ExchPub)
90         if err != nil {
91                 return nil, err
92         }
93         if len(exchPub) != 32 {
94                 return nil, errors.New("Invalid exchPub size")
95         }
96
97         signPub, err := FromBase32(yml.SignPub)
98         if err != nil {
99                 return nil, err
100         }
101         if len(signPub) != ed25519.PublicKeySize {
102                 return nil, errors.New("Invalid signPub size")
103         }
104
105         var noisePub []byte
106         if yml.NoisePub != nil {
107                 noisePub, err = FromBase32(*yml.NoisePub)
108                 if err != nil {
109                         return nil, err
110                 }
111                 if len(noisePub) != 32 {
112                         return nil, errors.New("Invalid noisePub size")
113                 }
114         }
115
116         var incoming *string
117         if yml.Incoming != nil {
118                 inc := path.Clean(*yml.Incoming)
119                 if !path.IsAbs(inc) {
120                         return nil, errors.New("Incoming path must be absolute")
121                 }
122                 incoming = &inc
123         }
124
125         var freq *string
126         if yml.Freq != nil {
127                 fr := path.Clean(*yml.Freq)
128                 if !path.IsAbs(fr) {
129                         return nil, errors.New("Freq path must be absolute")
130                 }
131                 freq = &fr
132         }
133
134         node := Node{
135                 Name:           name,
136                 Id:             nodeId,
137                 ExchPub:        new([32]byte),
138                 SignPub:        ed25519.PublicKey(signPub),
139                 Sendmail:       yml.Sendmail,
140                 Incoming:       incoming,
141                 Freq:           freq,
142                 Addrs:          yml.Addrs,
143                 OnlineDeadline: DefaultDeadline,
144         }
145         copy(node.ExchPub[:], exchPub)
146         if len(noisePub) > 0 {
147                 node.NoisePub = new([32]byte)
148                 copy(node.NoisePub[:], noisePub)
149         }
150         if yml.OnlineDeadline != nil {
151                 if *yml.OnlineDeadline <= 0 {
152                         return nil, errors.New("OnlineDeadline must be at least 1 second")
153                 }
154                 node.OnlineDeadline = *yml.OnlineDeadline
155         }
156         return &node, nil
157 }
158
159 func NewNodeOur(yml NodeOurYAML) (*NodeOur, error) {
160         id, err := NodeIdFromString(yml.Id)
161         if err != nil {
162                 return nil, err
163         }
164
165         exchPub, err := FromBase32(yml.ExchPub)
166         if err != nil {
167                 return nil, err
168         }
169         if len(exchPub) != 32 {
170                 return nil, errors.New("Invalid exchPub size")
171         }
172
173         exchPrv, err := FromBase32(yml.ExchPrv)
174         if err != nil {
175                 return nil, err
176         }
177         if len(exchPrv) != 32 {
178                 return nil, errors.New("Invalid exchPrv size")
179         }
180
181         signPub, err := FromBase32(yml.SignPub)
182         if err != nil {
183                 return nil, err
184         }
185         if len(signPub) != ed25519.PublicKeySize {
186                 return nil, errors.New("Invalid signPub size")
187         }
188
189         signPrv, err := FromBase32(yml.SignPrv)
190         if err != nil {
191                 return nil, err
192         }
193         if len(signPrv) != ed25519.PrivateKeySize {
194                 return nil, errors.New("Invalid signPrv size")
195         }
196
197         noisePub, err := FromBase32(yml.NoisePub)
198         if err != nil {
199                 return nil, err
200         }
201         if len(noisePub) != 32 {
202                 return nil, errors.New("Invalid noisePub size")
203         }
204
205         noisePrv, err := FromBase32(yml.NoisePrv)
206         if err != nil {
207                 return nil, err
208         }
209         if len(noisePrv) != 32 {
210                 return nil, errors.New("Invalid noisePrv size")
211         }
212
213         node := NodeOur{
214                 Id:       id,
215                 ExchPub:  new([32]byte),
216                 ExchPrv:  new([32]byte),
217                 SignPub:  ed25519.PublicKey(signPub),
218                 SignPrv:  ed25519.PrivateKey(signPrv),
219                 NoisePub: new([32]byte),
220                 NoisePrv: new([32]byte),
221         }
222         copy(node.ExchPub[:], exchPub)
223         copy(node.ExchPrv[:], exchPrv)
224         copy(node.NoisePub[:], noisePub)
225         copy(node.NoisePrv[:], noisePrv)
226         return &node, nil
227 }
228
229 func (nodeOur *NodeOur) ToYAML() string {
230         yml := NodeOurYAML{
231                 Id:       nodeOur.Id.String(),
232                 ExchPub:  ToBase32(nodeOur.ExchPub[:]),
233                 ExchPrv:  ToBase32(nodeOur.ExchPrv[:]),
234                 SignPub:  ToBase32(nodeOur.SignPub[:]),
235                 SignPrv:  ToBase32(nodeOur.SignPrv[:]),
236                 NoisePub: ToBase32(nodeOur.NoisePub[:]),
237                 NoisePrv: ToBase32(nodeOur.NoisePrv[:]),
238         }
239         raw, err := yaml.Marshal(&yml)
240         if err != nil {
241                 panic(err)
242         }
243         return string(raw)
244 }
245
246 func CfgParse(data []byte) (*Ctx, error) {
247         var cfgYAML CfgYAML
248         err := yaml.Unmarshal(data, &cfgYAML)
249         if err != nil {
250                 return nil, err
251         }
252         self, err := NewNodeOur(cfgYAML.Self)
253         if err != nil {
254                 return nil, err
255         }
256         spoolPath := path.Clean(cfgYAML.Spool)
257         if !path.IsAbs(spoolPath) {
258                 return nil, errors.New("Spool path must be absolute")
259         }
260         logPath := path.Clean(cfgYAML.Log)
261         if !path.IsAbs(logPath) {
262                 return nil, errors.New("Log path must be absolute")
263         }
264         ctx := Ctx{
265                 Spool:   spoolPath,
266                 LogPath: logPath,
267                 Self:    self,
268                 Neigh:   make(map[NodeId]*Node, len(cfgYAML.Neigh)),
269                 Alias:   make(map[string]*NodeId),
270         }
271         if cfgYAML.Notify != nil {
272                 if cfgYAML.Notify.File != nil {
273                         ctx.NotifyFile = cfgYAML.Notify.File
274                 }
275                 if cfgYAML.Notify.Freq != nil {
276                         ctx.NotifyFreq = cfgYAML.Notify.Freq
277                 }
278         }
279         vias := make(map[NodeId][]string)
280         for name, neighYAML := range cfgYAML.Neigh {
281                 neigh, err := NewNode(name, neighYAML)
282                 if err != nil {
283                         return nil, err
284                 }
285                 ctx.Neigh[*neigh.Id] = neigh
286                 if _, already := ctx.Alias[name]; already {
287                         return nil, errors.New("Node names conflict")
288                 }
289                 ctx.Alias[name] = neigh.Id
290                 vias[*neigh.Id] = neighYAML.Via
291         }
292         for neighId, viasRaw := range vias {
293                 for _, viaRaw := range viasRaw {
294                         foundNodeId, err := ctx.FindNode(viaRaw)
295                         if err != nil {
296                                 return nil, err
297                         }
298                         ctx.Neigh[neighId].Via = append(
299                                 ctx.Neigh[neighId].Via,
300                                 foundNodeId.Id,
301                         )
302                 }
303         }
304         return &ctx, nil
305 }
306
307 func CfgPathFromEnv(cmdlineFlag *string) (p string) {
308         p = os.Getenv(CfgPathEnv)
309         if p == "" {
310                 p = *cmdlineFlag
311         }
312         return
313 }