]> Cypherpunks.ru repositories - nncp.git/blob - src/cypherpunks.ru/nncp/cfg.go
Configure sendmail command per node
[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         "path"
24
25         "golang.org/x/crypto/ed25519"
26         "gopkg.in/yaml.v2"
27 )
28
29 var (
30         DefaultCfgPath      string = "/usr/local/etc/nncp.yaml"
31         DefaultSendmailPath string = "/usr/sbin/sendmail"
32 )
33
34 type NodeYAML struct {
35         Id       string
36         ExchPub  string
37         SignPub  string
38         NoisePub string
39         Sendmail []string
40         Incoming *string  `incoming,omitempty`
41         Freq     *string  `freq,omitempty`
42         Via      []string `via,omitempty`
43
44         Addrs map[string]string `addrs,omitempty`
45 }
46
47 type NodeOurYAML struct {
48         Id       string
49         ExchPub  string
50         ExchPrv  string
51         SignPub  string
52         SignPrv  string
53         NoisePrv string
54         NoisePub string
55 }
56
57 type FromToYAML struct {
58         From string
59         To   string
60 }
61
62 type NotifyYAML struct {
63         File *FromToYAML `file,omitempty`
64         Freq *FromToYAML `freq,omitempty`
65 }
66
67 type CfgYAML struct {
68         Self  NodeOurYAML
69         Neigh map[string]NodeYAML
70
71         Spool  string
72         Log    string
73         Notify *NotifyYAML `notify,omitempty`
74 }
75
76 func NewNode(name string, yml NodeYAML) (*Node, error) {
77         nodeId, err := NodeIdFromString(yml.Id)
78         if err != nil {
79                 return nil, err
80         }
81
82         exchPub, err := FromBase32(yml.ExchPub)
83         if err != nil {
84                 return nil, err
85         }
86         if len(exchPub) != 32 {
87                 return nil, errors.New("Invalid exchPub size")
88         }
89
90         signPub, err := FromBase32(yml.SignPub)
91         if err != nil {
92                 return nil, err
93         }
94         if len(signPub) != ed25519.PublicKeySize {
95                 return nil, errors.New("Invalid signPub size")
96         }
97
98         noisePub, err := FromBase32(yml.NoisePub)
99         if err != nil {
100                 return nil, err
101         }
102         if len(noisePub) != 32 {
103                 return nil, errors.New("Invalid noisePub size")
104         }
105
106         var incoming *string
107         if yml.Incoming != nil {
108                 inc := path.Clean(*yml.Incoming)
109                 if !path.IsAbs(inc) {
110                         return nil, errors.New("Incoming path must be absolute")
111                 }
112                 incoming = &inc
113         }
114
115         var freq *string
116         if yml.Freq != nil {
117                 fr := path.Clean(*yml.Freq)
118                 if !path.IsAbs(fr) {
119                         return nil, errors.New("Freq path must be absolute")
120                 }
121                 freq = &fr
122         }
123
124         node := Node{
125                 Name:     name,
126                 Id:       nodeId,
127                 ExchPub:  new([32]byte),
128                 SignPub:  ed25519.PublicKey(signPub),
129                 NoisePub: new([32]byte),
130                 Sendmail: yml.Sendmail,
131                 Incoming: incoming,
132                 Freq:     freq,
133                 Addrs:    yml.Addrs,
134         }
135         copy(node.ExchPub[:], exchPub)
136         copy(node.NoisePub[:], noisePub)
137         return &node, nil
138 }
139
140 func NewNodeOur(yml NodeOurYAML) (*NodeOur, error) {
141         id, err := NodeIdFromString(yml.Id)
142         if err != nil {
143                 return nil, err
144         }
145
146         exchPub, err := FromBase32(yml.ExchPub)
147         if err != nil {
148                 return nil, err
149         }
150         if len(exchPub) != 32 {
151                 return nil, errors.New("Invalid exchPub size")
152         }
153
154         exchPrv, err := FromBase32(yml.ExchPrv)
155         if err != nil {
156                 return nil, err
157         }
158         if len(exchPrv) != 32 {
159                 return nil, errors.New("Invalid exchPrv size")
160         }
161
162         signPub, err := FromBase32(yml.SignPub)
163         if err != nil {
164                 return nil, err
165         }
166         if len(signPub) != ed25519.PublicKeySize {
167                 return nil, errors.New("Invalid signPub size")
168         }
169
170         signPrv, err := FromBase32(yml.SignPrv)
171         if err != nil {
172                 return nil, err
173         }
174         if len(signPrv) != ed25519.PrivateKeySize {
175                 return nil, errors.New("Invalid signPrv size")
176         }
177
178         noisePub, err := FromBase32(yml.NoisePub)
179         if err != nil {
180                 return nil, err
181         }
182         if len(noisePub) != 32 {
183                 return nil, errors.New("Invalid noisePub size")
184         }
185
186         noisePrv, err := FromBase32(yml.NoisePrv)
187         if err != nil {
188                 return nil, err
189         }
190         if len(noisePrv) != 32 {
191                 return nil, errors.New("Invalid noisePrv size")
192         }
193
194         node := NodeOur{
195                 Id:       id,
196                 ExchPub:  new([32]byte),
197                 ExchPrv:  new([32]byte),
198                 SignPub:  ed25519.PublicKey(signPub),
199                 SignPrv:  ed25519.PrivateKey(signPrv),
200                 NoisePub: new([32]byte),
201                 NoisePrv: new([32]byte),
202         }
203         copy(node.ExchPub[:], exchPub)
204         copy(node.ExchPrv[:], exchPrv)
205         copy(node.NoisePub[:], noisePub)
206         copy(node.NoisePrv[:], noisePrv)
207         return &node, nil
208 }
209
210 func (nodeOur *NodeOur) ToYAML() string {
211         yml := NodeOurYAML{
212                 Id:       nodeOur.Id.String(),
213                 ExchPub:  ToBase32(nodeOur.ExchPub[:]),
214                 ExchPrv:  ToBase32(nodeOur.ExchPrv[:]),
215                 SignPub:  ToBase32(nodeOur.SignPub[:]),
216                 SignPrv:  ToBase32(nodeOur.SignPrv[:]),
217                 NoisePub: ToBase32(nodeOur.NoisePub[:]),
218                 NoisePrv: ToBase32(nodeOur.NoisePrv[:]),
219         }
220         raw, err := yaml.Marshal(&yml)
221         if err != nil {
222                 panic(err)
223         }
224         return string(raw)
225 }
226
227 func CfgParse(data []byte) (*Ctx, error) {
228         var cfgYAML CfgYAML
229         err := yaml.Unmarshal(data, &cfgYAML)
230         if err != nil {
231                 return nil, err
232         }
233         self, err := NewNodeOur(cfgYAML.Self)
234         if err != nil {
235                 return nil, err
236         }
237         spoolPath := path.Clean(cfgYAML.Spool)
238         if !path.IsAbs(spoolPath) {
239                 return nil, errors.New("Spool path must be absolute")
240         }
241         logPath := path.Clean(cfgYAML.Log)
242         if !path.IsAbs(logPath) {
243                 return nil, errors.New("Log path must be absolute")
244         }
245         ctx := Ctx{
246                 Spool:   spoolPath,
247                 LogPath: logPath,
248                 Self:    self,
249                 Neigh:   make(map[NodeId]*Node, len(cfgYAML.Neigh)),
250                 Alias:   make(map[string]*NodeId),
251         }
252         if cfgYAML.Notify != nil {
253                 if cfgYAML.Notify.File != nil {
254                         ctx.NotifyFile = cfgYAML.Notify.File
255                 }
256                 if cfgYAML.Notify.Freq != nil {
257                         ctx.NotifyFreq = cfgYAML.Notify.Freq
258                 }
259         }
260         vias := make(map[NodeId][]string)
261         for name, neighYAML := range cfgYAML.Neigh {
262                 neigh, err := NewNode(name, neighYAML)
263                 if err != nil {
264                         return nil, err
265                 }
266                 ctx.Neigh[*neigh.Id] = neigh
267                 if _, already := ctx.Alias[name]; already {
268                         return nil, errors.New("Node names conflict")
269                 }
270                 ctx.Alias[name] = neigh.Id
271                 vias[*neigh.Id] = neighYAML.Via
272         }
273         for neighId, viasRaw := range vias {
274                 for _, viaRaw := range viasRaw {
275                         foundNodeId, err := ctx.FindNode(viaRaw)
276                         if err != nil {
277                                 return nil, err
278                         }
279                         ctx.Neigh[neighId].Via = append(
280                                 ctx.Neigh[neighId].Via,
281                                 foundNodeId.Id,
282                         )
283                 }
284         }
285         return &ctx, nil
286 }