]> Cypherpunks.ru repositories - nncp.git/blob - src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go
ffd1c0c9ae930ede4cc6d8bcd2dcac81fdd5e861
[nncp.git] / src / cypherpunks.ru / nncp / cmd / nncp-xfer / main.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 // Copy NNCP inbound and outbounds packets
20 package main
21
22 import (
23         "bufio"
24         "flag"
25         "fmt"
26         "io"
27         "io/ioutil"
28         "log"
29         "os"
30         "path/filepath"
31         "strconv"
32
33         "cypherpunks.ru/nncp"
34         "github.com/davecgh/go-xdr/xdr2"
35 )
36
37 func usage() {
38         fmt.Fprintf(os.Stderr, nncp.UsageHeader())
39         fmt.Fprintln(os.Stderr, "nncp-xfer -- copy inbound and outbounds packets\n")
40         fmt.Fprintf(os.Stderr, "Usage: %s [options] DIR\nOptions:\n", os.Args[0])
41         flag.PrintDefaults()
42 }
43
44 func main() {
45         var (
46                 cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
47                 nodeRaw   = flag.String("node", "", "Process only that node")
48                 niceRaw   = flag.Int("nice", 255, "Minimal required niceness")
49                 rxOnly    = flag.Bool("rx", false, "Only receive packets")
50                 txOnly    = flag.Bool("tx", false, "Only transfer packets")
51                 mkdir     = flag.Bool("mkdir", false, "Create necessary outbound directories")
52                 keep      = flag.Bool("keep", false, "Do not delete transferred packets")
53                 spoolPath = flag.String("spool", "", "Override path to spool")
54                 logPath   = flag.String("log", "", "Override path to logfile")
55                 quiet     = flag.Bool("quiet", false, "Print only errors")
56                 debug     = flag.Bool("debug", false, "Print debug messages")
57                 version   = flag.Bool("version", false, "Print version information")
58                 warranty  = flag.Bool("warranty", false, "Print warranty information")
59         )
60         flag.Usage = usage
61         flag.Parse()
62         if *warranty {
63                 fmt.Println(nncp.Warranty)
64                 return
65         }
66         if *version {
67                 fmt.Println(nncp.VersionGet())
68                 return
69         }
70         if flag.NArg() != 1 {
71                 usage()
72                 os.Exit(1)
73         }
74         if *niceRaw < 1 || *niceRaw > 255 {
75                 log.Fatalln("-nice must be between 1 and 255")
76         }
77         nice := uint8(*niceRaw)
78         if *rxOnly && *txOnly {
79                 log.Fatalln("-rx and -tx can not be set simultaneously")
80         }
81
82         ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
83         if err != nil {
84                 log.Fatalln("Error during initialization:", err)
85         }
86
87         var nodeOnly *nncp.Node
88         if *nodeRaw != "" {
89                 nodeOnly, err = ctx.FindNode(*nodeRaw)
90                 if err != nil {
91                         log.Fatalln("Invalid -node specified:", err)
92                 }
93         }
94
95         selfPath := filepath.Join(flag.Arg(0), ctx.SelfId.String())
96         isBad := false
97         var dir *os.File
98         var fis []os.FileInfo
99         sds := nncp.SDS{}
100         if *txOnly {
101                 goto Tx
102         }
103         sds["xx"] = string(nncp.TRx)
104         sds["dir"] = selfPath
105         ctx.LogD("nncp-xfer", sds, "self")
106         if _, err = os.Stat(selfPath); err != nil {
107                 if os.IsNotExist(err) {
108                         ctx.LogD("nncp-xfer", sds, "no dir")
109                         goto Tx
110                 }
111                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "stat")
112                 isBad = true
113                 goto Tx
114         }
115         dir, err = os.Open(selfPath)
116         if err != nil {
117                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "open")
118                 isBad = true
119                 goto Tx
120         }
121         fis, err = dir.Readdir(0)
122         dir.Close()
123         if err != nil {
124                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "read")
125                 isBad = true
126                 goto Tx
127         }
128         for _, fi := range fis {
129                 if !fi.IsDir() {
130                         continue
131                 }
132                 nodeId, err := nncp.NodeIdFromString(fi.Name())
133                 sds["node"] = fi.Name()
134                 if err != nil {
135                         ctx.LogD("nncp-xfer", sds, "is not NodeId")
136                         continue
137                 }
138                 if nodeOnly != nil && *nodeId != *nodeOnly.Id {
139                         ctx.LogD("nncp-xfer", sds, "skip")
140                         continue
141                 }
142                 if _, known := ctx.Neigh[*nodeId]; !known {
143                         ctx.LogD("nncp-xfer", sds, "unknown")
144                         continue
145                 }
146                 dir, err = os.Open(filepath.Join(selfPath, fi.Name()))
147                 if err != nil {
148                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "open")
149                         isBad = true
150                         continue
151                 }
152                 fisInt, err := dir.Readdir(0)
153                 dir.Close()
154                 if err != nil {
155                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "read")
156                         isBad = true
157                         continue
158                 }
159                 for _, fiInt := range fisInt {
160                         if !fi.IsDir() {
161                                 continue
162                         }
163                         filename := filepath.Join(dir.Name(), fiInt.Name())
164                         sds["file"] = filename
165                         fd, err := os.Open(filename)
166                         if err != nil {
167                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "open")
168                                 isBad = true
169                                 continue
170                         }
171                         var pktEnc nncp.PktEnc
172                         _, err = xdr.Unmarshal(fd, &pktEnc)
173                         if err != nil || pktEnc.Magic != nncp.MagicNNCPEv2 {
174                                 ctx.LogD("nncp-xfer", sds, "is not a packet")
175                                 fd.Close()
176                                 continue
177                         }
178                         if pktEnc.Nice > nice {
179                                 ctx.LogD("nncp-xfer", sds, "too nice")
180                                 fd.Close()
181                                 continue
182                         }
183                         fd.Seek(0, 0)
184                         tmp, err := ctx.NewTmpFileWHash()
185                         if err != nil {
186                                 log.Fatalln(err)
187                         }
188                         copied, err := io.Copy(tmp.W, bufio.NewReader(fd))
189                         if err != nil {
190                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "copy")
191                                 isBad = true
192                                 fd.Close()
193                                 tmp.Cancel()
194                                 continue
195                         }
196                         fd.Close()
197                         if err = tmp.Commit(filepath.Join(
198                                 ctx.Spool,
199                                 nodeId.String(),
200                                 string(nncp.TRx),
201                         )); err != nil {
202                                 log.Fatalln(err)
203                         }
204                         ctx.LogI("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{
205                                 "size": strconv.FormatInt(copied, 10),
206                         }), "")
207                         if !*keep {
208                                 if err = os.Remove(filename); err != nil {
209                                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "remove")
210                                         isBad = true
211                                 }
212                         }
213                 }
214         }
215
216 Tx:
217         if *rxOnly {
218                 if isBad {
219                         os.Exit(1)
220                 }
221                 return
222         }
223         sds["xx"] = string(nncp.TTx)
224         for nodeId, _ := range ctx.Neigh {
225                 sds["node"] = nodeId
226                 if nodeOnly != nil && nodeId != *nodeOnly.Id {
227                         ctx.LogD("nncp-xfer", sds, "skip")
228                         continue
229                 }
230                 dirLock, err := ctx.LockDir(&nodeId, nncp.TTx)
231                 if err != nil {
232                         continue
233                 }
234                 nodePath := filepath.Join(flag.Arg(0), nodeId.String())
235                 sds["dir"] = nodePath
236                 _, err = os.Stat(nodePath)
237                 if err != nil {
238                         if os.IsNotExist(err) {
239                                 ctx.LogD("nncp-xfer", sds, "does not exist")
240                                 if !*mkdir {
241                                         ctx.UnlockDir(dirLock)
242                                         continue
243                                 }
244                                 if err = os.Mkdir(nodePath, os.FileMode(0700)); err != nil {
245                                         ctx.UnlockDir(dirLock)
246                                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "mkdir")
247                                         isBad = true
248                                         continue
249                                 }
250                         } else {
251                                 ctx.UnlockDir(dirLock)
252                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "stat")
253                                 isBad = true
254                                 continue
255                         }
256                 }
257                 dstPath := filepath.Join(nodePath, ctx.SelfId.String())
258                 sds["dir"] = dstPath
259                 _, err = os.Stat(dstPath)
260                 if err != nil {
261                         if os.IsNotExist(err) {
262                                 if err = os.Mkdir(dstPath, os.FileMode(0700)); err != nil {
263                                         ctx.UnlockDir(dirLock)
264                                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "mkdir")
265                                         isBad = true
266                                         continue
267                                 }
268                         } else {
269                                 ctx.UnlockDir(dirLock)
270                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "stat")
271                                 isBad = true
272                                 continue
273                         }
274                 }
275                 delete(sds, "dir")
276                 for job := range ctx.Jobs(&nodeId, nncp.TTx) {
277                         pktName := filepath.Base(job.Fd.Name())
278                         sds["pkt"] = pktName
279                         if job.PktEnc.Nice > nice {
280                                 ctx.LogD("nncp-xfer", sds, "too nice")
281                                 job.Fd.Close()
282                                 continue
283                         }
284                         if _, err = os.Stat(filepath.Join(dstPath, pktName)); err == nil || !os.IsNotExist(err) {
285                                 ctx.LogD("nncp-xfer", sds, "already exists")
286                                 job.Fd.Close()
287                                 continue
288                         }
289                         if _, err = os.Stat(filepath.Join(dstPath, pktName+nncp.SeenSuffix)); err == nil || !os.IsNotExist(err) {
290                                 ctx.LogD("nncp-xfer", sds, "already exists")
291                                 job.Fd.Close()
292                                 continue
293                         }
294                         tmp, err := ioutil.TempFile(dstPath, "nncp-xfer")
295                         if err != nil {
296                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "mktemp")
297                                 job.Fd.Close()
298                                 isBad = true
299                                 break
300                         }
301                         sds["tmp"] = tmp.Name()
302                         ctx.LogD("nncp-xfer", sds, "created")
303                         bufW := bufio.NewWriter(tmp)
304                         copied, err := io.Copy(bufW, bufio.NewReader(job.Fd))
305                         job.Fd.Close()
306                         if err != nil {
307                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "copy")
308                                 tmp.Close()
309                                 isBad = true
310                                 continue
311                         }
312                         err = bufW.Flush()
313                         tmp.Sync()
314                         tmp.Close()
315                         if err != nil {
316                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "copy")
317                                 isBad = true
318                                 continue
319                         }
320                         if err = os.Rename(tmp.Name(), filepath.Join(dstPath, pktName)); err != nil {
321                                 ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "rename")
322                                 isBad = true
323                                 continue
324                         }
325                         os.Remove(filepath.Join(dstPath, pktName+".part"))
326                         delete(sds, "tmp")
327                         ctx.LogI("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{
328                                 "size": strconv.FormatInt(copied, 10),
329                         }), "")
330                         if !*keep {
331                                 if err = os.Remove(job.Fd.Name()); err != nil {
332                                         ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "remove")
333                                         isBad = true
334                                 }
335                         }
336                 }
337                 ctx.UnlockDir(dirLock)
338         }
339         if isBad {
340                 os.Exit(1)
341         }
342 }