]> Cypherpunks.ru repositories - nncp.git/blob - src/toss.go
Merge branch 'develop'
[nncp.git] / src / toss.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2021 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, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package nncp
19
20 import (
21         "bufio"
22         "bytes"
23         "encoding/base64"
24         "errors"
25         "fmt"
26         "io"
27         "io/ioutil"
28         "log"
29         "mime"
30         "os"
31         "os/exec"
32         "path"
33         "path/filepath"
34         "strconv"
35         "strings"
36         "time"
37
38         xdr "github.com/davecgh/go-xdr/xdr2"
39         "github.com/dustin/go-humanize"
40         "github.com/klauspost/compress/zstd"
41         "golang.org/x/crypto/poly1305"
42 )
43
44 const (
45         SeenSuffix = ".seen"
46 )
47
48 func newNotification(fromTo *FromToJSON, subject string, body []byte) io.Reader {
49         lines := []string{
50                 "From: " + fromTo.From,
51                 "To: " + fromTo.To,
52                 "Subject: " + mime.BEncoding.Encode("UTF-8", subject),
53         }
54         if len(body) > 0 {
55                 lines = append(
56                         lines,
57                         "MIME-Version: 1.0",
58                         "Content-Type: text/plain; charset=utf-8",
59                         "Content-Transfer-Encoding: base64",
60                         "",
61                         base64.StdEncoding.EncodeToString(body),
62                 )
63         }
64         return strings.NewReader(strings.Join(lines, "\n"))
65 }
66
67 func (ctx *Ctx) Toss(
68         nodeId *NodeId,
69         nice uint8,
70         dryRun, doSeen, noFile, noFreq, noExec, noTrns bool,
71 ) bool {
72         dirLock, err := ctx.LockDir(nodeId, "toss")
73         if err != nil {
74                 return false
75         }
76         defer ctx.UnlockDir(dirLock)
77         isBad := false
78         sendmail := ctx.Neigh[*ctx.SelfId].Exec["sendmail"]
79         decompressor, err := zstd.NewReader(nil)
80         if err != nil {
81                 panic(err)
82         }
83         defer decompressor.Close()
84         for job := range ctx.Jobs(nodeId, TRx) {
85                 pktName := filepath.Base(job.Path)
86                 les := LEs{
87                         {"Node", job.PktEnc.Sender},
88                         {"Pkt", pktName},
89                         {"Nice", int(job.PktEnc.Nice)},
90                 }
91                 if job.PktEnc.Nice > nice {
92                         ctx.LogD("rx-too-nice", les, func(les LEs) string {
93                                 return fmt.Sprintf(
94                                         "Tossing %s/%s: too nice: %s",
95                                         ctx.NodeName(job.PktEnc.Sender), pktName,
96                                         NicenessFmt(job.PktEnc.Nice),
97                                 )
98                         })
99                         continue
100                 }
101                 fd, err := os.Open(job.Path)
102                 if err != nil {
103                         ctx.LogE("rx-open", les, err, func(les LEs) string {
104                                 return fmt.Sprintf(
105                                         "Tossing %s/%s: opening %s",
106                                         ctx.NodeName(job.PktEnc.Sender), pktName, job.Path,
107                                 )
108                         })
109                         isBad = true
110                         continue
111                 }
112
113                 pipeR, pipeW := io.Pipe()
114                 go func(job Job) error {
115                         pipeWB := bufio.NewWriter(pipeW)
116                         _, _, err := PktEncRead(ctx.Self, ctx.Neigh, bufio.NewReader(fd), pipeWB)
117                         fd.Close() // #nosec G104
118                         if err != nil {
119                                 return pipeW.CloseWithError(err)
120                         }
121                         if err = pipeWB.Flush(); err != nil {
122                                 return pipeW.CloseWithError(err)
123                         }
124                         return pipeW.Close()
125                 }(job)
126                 var pkt Pkt
127                 var pktSize int64
128                 var pktSizeBlocks int64
129                 if _, err = xdr.Unmarshal(pipeR, &pkt); err != nil {
130                         ctx.LogE("rx-unmarshal", les, err, func(les LEs) string {
131                                 return fmt.Sprintf(
132                                         "Tossing %s/%s: unmarshal",
133                                         ctx.NodeName(job.PktEnc.Sender), pktName,
134                                 )
135                         })
136                         isBad = true
137                         goto Closing
138                 }
139                 pktSize = job.Size - PktEncOverhead - PktOverhead - PktSizeOverhead
140                 pktSizeBlocks = pktSize / (EncBlkSize + poly1305.TagSize)
141                 if pktSize%(EncBlkSize+poly1305.TagSize) != 0 {
142                         pktSize -= poly1305.TagSize
143                 }
144                 pktSize -= pktSizeBlocks * poly1305.TagSize
145                 les = append(les, LE{"Size", pktSize})
146                 ctx.LogD("rx", les, func(les LEs) string {
147                         return fmt.Sprintf(
148                                 "Tossing %s/%s (%s)",
149                                 ctx.NodeName(job.PktEnc.Sender), pktName,
150                                 humanize.IBytes(uint64(pktSize)),
151                         )
152                 })
153
154                 switch pkt.Type {
155                 case PktTypeExec, PktTypeExecFat:
156                         if noExec {
157                                 goto Closing
158                         }
159                         path := bytes.Split(pkt.Path[:int(pkt.PathLen)], []byte{0})
160                         handle := string(path[0])
161                         args := make([]string, 0, len(path)-1)
162                         for _, p := range path[1:] {
163                                 args = append(args, string(p))
164                         }
165                         argsStr := strings.Join(append([]string{handle}, args...), " ")
166                         les = append(les, LE{"Type", "exec"}, LE{"Dst", argsStr})
167                         sender := ctx.Neigh[*job.PktEnc.Sender]
168                         cmdline, exists := sender.Exec[handle]
169                         if !exists || len(cmdline) == 0 {
170                                 ctx.LogE(
171                                         "rx-no-handle", les, errors.New("No handle found"),
172                                         func(les LEs) string {
173                                                 return fmt.Sprintf(
174                                                         "Tossing exec %s/%s (%s): %s",
175                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
176                                                         humanize.IBytes(uint64(pktSize)), argsStr,
177                                                 )
178                                         },
179                                 )
180                                 isBad = true
181                                 goto Closing
182                         }
183                         if pkt.Type == PktTypeExec {
184                                 if err = decompressor.Reset(pipeR); err != nil {
185                                         log.Fatalln(err)
186                                 }
187                         }
188                         if !dryRun {
189                                 cmd := exec.Command(cmdline[0], append(cmdline[1:], args...)...)
190                                 cmd.Env = append(
191                                         cmd.Env,
192                                         "NNCP_SELF="+ctx.Self.Id.String(),
193                                         "NNCP_SENDER="+sender.Id.String(),
194                                         "NNCP_NICE="+strconv.Itoa(int(pkt.Nice)),
195                                 )
196                                 if pkt.Type == PktTypeExec {
197                                         cmd.Stdin = decompressor
198                                 } else {
199                                         cmd.Stdin = pipeR
200                                 }
201                                 output, err := cmd.Output()
202                                 if err != nil {
203                                         ctx.LogE("rx-hande", les, err, func(les LEs) string {
204                                                 return fmt.Sprintf(
205                                                         "Tossing exec %s/%s (%s): %s: handling",
206                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
207                                                         humanize.IBytes(uint64(pktSize)), argsStr,
208                                                 )
209                                         })
210                                         isBad = true
211                                         goto Closing
212                                 }
213                                 if len(sendmail) > 0 && ctx.NotifyExec != nil {
214                                         notify, exists := ctx.NotifyExec[sender.Name+"."+handle]
215                                         if !exists {
216                                                 notify, exists = ctx.NotifyExec["*."+handle]
217                                         }
218                                         if exists {
219                                                 cmd := exec.Command(
220                                                         sendmail[0],
221                                                         append(sendmail[1:], notify.To)...,
222                                                 )
223                                                 cmd.Stdin = newNotification(notify, fmt.Sprintf(
224                                                         "Exec from %s: %s", sender.Name, argsStr,
225                                                 ), output)
226                                                 if err = cmd.Run(); err != nil {
227                                                         ctx.LogE("rx-notify", les, err, func(les LEs) string {
228                                                                 return fmt.Sprintf(
229                                                                         "Tossing exec %s/%s (%s): %s: notifying",
230                                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
231                                                                         humanize.IBytes(uint64(pktSize)), argsStr,
232                                                                 )
233                                                         })
234                                                 }
235                                         }
236                                 }
237                         }
238                         ctx.LogI("rx", les, func(les LEs) string {
239                                 return fmt.Sprintf(
240                                         "Got exec from %s to %s (%s)",
241                                         ctx.NodeName(job.PktEnc.Sender), argsStr,
242                                         humanize.IBytes(uint64(pktSize)),
243                                 )
244                         })
245                         if !dryRun {
246                                 if doSeen {
247                                         if fd, err := os.Create(job.Path + SeenSuffix); err == nil {
248                                                 fd.Close() // #nosec G104
249                                         }
250                                 }
251                                 if err = os.Remove(job.Path); err != nil {
252                                         ctx.LogE("rx-notify", les, err, func(les LEs) string {
253                                                 return fmt.Sprintf(
254                                                         "Tossing exec %s/%s (%s): %s: notifying",
255                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
256                                                         humanize.IBytes(uint64(pktSize)), argsStr,
257                                                 )
258                                         })
259                                         isBad = true
260                                 } else if ctx.HdrUsage {
261                                         os.Remove(job.Path + HdrSuffix)
262                                 }
263                         }
264
265                 case PktTypeFile:
266                         if noFile {
267                                 goto Closing
268                         }
269                         dst := string(pkt.Path[:int(pkt.PathLen)])
270                         les = append(les, LE{"Type", "file"}, LE{"Dst", dst})
271                         if filepath.IsAbs(dst) {
272                                 ctx.LogE(
273                                         "rx-non-rel", les, errors.New("non-relative destination path"),
274                                         func(les LEs) string {
275                                                 return fmt.Sprintf(
276                                                         "Tossing file %s/%s (%s): %s",
277                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
278                                                         humanize.IBytes(uint64(pktSize)), dst,
279                                                 )
280                                         },
281                                 )
282                                 isBad = true
283                                 goto Closing
284                         }
285                         incoming := ctx.Neigh[*job.PktEnc.Sender].Incoming
286                         if incoming == nil {
287                                 ctx.LogE(
288                                         "rx-no-incoming", les, errors.New("incoming is not allowed"),
289                                         func(les LEs) string {
290                                                 return fmt.Sprintf(
291                                                         "Tossing file %s/%s (%s): %s",
292                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
293                                                         humanize.IBytes(uint64(pktSize)), dst,
294                                                 )
295                                         },
296                                 )
297                                 isBad = true
298                                 goto Closing
299                         }
300                         dir := filepath.Join(*incoming, path.Dir(dst))
301                         if err = os.MkdirAll(dir, os.FileMode(0777)); err != nil {
302                                 ctx.LogE("rx-mkdir", les, err, func(les LEs) string {
303                                         return fmt.Sprintf(
304                                                 "Tossing file %s/%s (%s): %s: mkdir",
305                                                 ctx.NodeName(job.PktEnc.Sender), pktName,
306                                                 humanize.IBytes(uint64(pktSize)), dst,
307                                         )
308                                 })
309                                 isBad = true
310                                 goto Closing
311                         }
312                         if !dryRun {
313                                 tmp, err := TempFile(dir, "file")
314                                 if err != nil {
315                                         ctx.LogE("rx-mktemp", les, err, func(les LEs) string {
316                                                 return fmt.Sprintf(
317                                                         "Tossing file %s/%s (%s): %s: mktemp",
318                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
319                                                         humanize.IBytes(uint64(pktSize)), dst,
320                                                 )
321                                         })
322                                         isBad = true
323                                         goto Closing
324                                 }
325                                 les = append(les, LE{"Tmp", tmp.Name()})
326                                 ctx.LogD("rx-tmp-created", les, func(les LEs) string {
327                                         return fmt.Sprintf(
328                                                 "Tossing file %s/%s (%s): %s: created: %s",
329                                                 ctx.NodeName(job.PktEnc.Sender), pktName,
330                                                 humanize.IBytes(uint64(pktSize)), dst, tmp.Name(),
331                                         )
332                                 })
333                                 bufW := bufio.NewWriter(tmp)
334                                 if _, err = CopyProgressed(
335                                         bufW, pipeR, "Rx file",
336                                         append(les, LE{"FullSize", pktSize}),
337                                         ctx.ShowPrgrs,
338                                 ); err != nil {
339                                         ctx.LogE("rx-copy", les, err, func(les LEs) string {
340                                                 return fmt.Sprintf(
341                                                         "Tossing file %s/%s (%s): %s: copying",
342                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
343                                                         humanize.IBytes(uint64(pktSize)), dst,
344                                                 )
345                                         })
346                                         isBad = true
347                                         goto Closing
348                                 }
349                                 if err = bufW.Flush(); err != nil {
350                                         tmp.Close() // #nosec G104
351                                         ctx.LogE("rx-flush", les, err, func(les LEs) string {
352                                                 return fmt.Sprintf(
353                                                         "Tossing file %s/%s (%s): %s: flushing",
354                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
355                                                         humanize.IBytes(uint64(pktSize)), dst,
356                                                 )
357                                         })
358                                         isBad = true
359                                         goto Closing
360                                 }
361                                 if err = tmp.Sync(); err != nil {
362                                         tmp.Close() // #nosec G104
363                                         ctx.LogE("rx-sync", les, err, func(les LEs) string {
364                                                 return fmt.Sprintf(
365                                                         "Tossing file %s/%s (%s): %s: syncing",
366                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
367                                                         humanize.IBytes(uint64(pktSize)), dst,
368                                                 )
369                                         })
370                                         isBad = true
371                                         goto Closing
372                                 }
373                                 if err = tmp.Close(); err != nil {
374                                         ctx.LogE("rx-close", les, err, func(les LEs) string {
375                                                 return fmt.Sprintf(
376                                                         "Tossing file %s/%s (%s): %s: closing",
377                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
378                                                         humanize.IBytes(uint64(pktSize)), dst,
379                                                 )
380                                         })
381                                         isBad = true
382                                         goto Closing
383                                 }
384                                 dstPathOrig := filepath.Join(*incoming, dst)
385                                 dstPath := dstPathOrig
386                                 dstPathCtr := 0
387                                 for {
388                                         if _, err = os.Stat(dstPath); err != nil {
389                                                 if os.IsNotExist(err) {
390                                                         break
391                                                 }
392                                                 ctx.LogE("rx-stat", les, err, func(les LEs) string {
393                                                         return fmt.Sprintf(
394                                                                 "Tossing file %s/%s (%s): %s: stating: %s",
395                                                                 ctx.NodeName(job.PktEnc.Sender), pktName,
396                                                                 humanize.IBytes(uint64(pktSize)), dst, dstPath,
397                                                         )
398                                                 })
399                                                 isBad = true
400                                                 goto Closing
401                                         }
402                                         dstPath = dstPathOrig + "." + strconv.Itoa(dstPathCtr)
403                                         dstPathCtr++
404                                 }
405                                 if err = os.Rename(tmp.Name(), dstPath); err != nil {
406                                         ctx.LogE("rx-rename", les, err, func(les LEs) string {
407                                                 return fmt.Sprintf(
408                                                         "Tossing file %s/%s (%s): %s: renaming",
409                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
410                                                         humanize.IBytes(uint64(pktSize)), dst,
411                                                 )
412                                         })
413                                         isBad = true
414                                 }
415                                 if err = DirSync(*incoming); err != nil {
416                                         ctx.LogE("rx-dirsync", les, err, func(les LEs) string {
417                                                 return fmt.Sprintf(
418                                                         "Tossing file %s/%s (%s): %s: dirsyncing",
419                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
420                                                         humanize.IBytes(uint64(pktSize)), dst,
421                                                 )
422                                         })
423                                         isBad = true
424                                 }
425                                 les = les[:len(les)-1] // delete Tmp
426                         }
427                         ctx.LogI("rx", les, func(les LEs) string {
428                                 return fmt.Sprintf(
429                                         "Got file %s (%s) from %s",
430                                         dst, humanize.IBytes(uint64(pktSize)),
431                                         ctx.NodeName(job.PktEnc.Sender),
432                                 )
433                         })
434                         if !dryRun {
435                                 if doSeen {
436                                         if fd, err := os.Create(job.Path + SeenSuffix); err == nil {
437                                                 fd.Close() // #nosec G104
438                                         }
439                                 }
440                                 if err = os.Remove(job.Path); err != nil {
441                                         ctx.LogE("rx-remove", les, err, func(les LEs) string {
442                                                 return fmt.Sprintf(
443                                                         "Tossing file %s/%s (%s): %s: removing",
444                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
445                                                         humanize.IBytes(uint64(pktSize)), dst,
446                                                 )
447                                         })
448                                         isBad = true
449                                 } else if ctx.HdrUsage {
450                                         os.Remove(job.Path + HdrSuffix)
451                                 }
452                                 if len(sendmail) > 0 && ctx.NotifyFile != nil {
453                                         cmd := exec.Command(
454                                                 sendmail[0],
455                                                 append(sendmail[1:], ctx.NotifyFile.To)...,
456                                         )
457                                         cmd.Stdin = newNotification(ctx.NotifyFile, fmt.Sprintf(
458                                                 "File from %s: %s (%s)",
459                                                 ctx.Neigh[*job.PktEnc.Sender].Name,
460                                                 dst,
461                                                 humanize.IBytes(uint64(pktSize)),
462                                         ), nil)
463                                         if err = cmd.Run(); err != nil {
464                                                 ctx.LogE("rx-notify", les, err, func(les LEs) string {
465                                                         return fmt.Sprintf(
466                                                                 "Tossing file %s/%s (%s): %s: notifying",
467                                                                 ctx.NodeName(job.PktEnc.Sender), pktName,
468                                                                 humanize.IBytes(uint64(pktSize)), dst,
469                                                         )
470                                                 })
471                                         }
472                                 }
473                         }
474
475                 case PktTypeFreq:
476                         if noFreq {
477                                 goto Closing
478                         }
479                         src := string(pkt.Path[:int(pkt.PathLen)])
480                         les := append(les, LE{"Type", "freq"}, LE{"Src", src})
481                         if filepath.IsAbs(src) {
482                                 ctx.LogE(
483                                         "rx-non-rel", les, errors.New("non-relative source path"),
484                                         func(les LEs) string {
485                                                 return fmt.Sprintf(
486                                                         "Tossing freq %s/%s (%s): %s: notifying",
487                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
488                                                         humanize.IBytes(uint64(pktSize)), src,
489                                                 )
490                                         },
491                                 )
492                                 isBad = true
493                                 goto Closing
494                         }
495                         dstRaw, err := ioutil.ReadAll(pipeR)
496                         if err != nil {
497                                 ctx.LogE("rx-read", les, err, func(les LEs) string {
498                                         return fmt.Sprintf(
499                                                 "Tossing freq %s/%s (%s): %s: reading",
500                                                 ctx.NodeName(job.PktEnc.Sender), pktName,
501                                                 humanize.IBytes(uint64(pktSize)), src,
502                                         )
503                                 })
504                                 isBad = true
505                                 goto Closing
506                         }
507                         dst := string(dstRaw)
508                         les = append(les, LE{"Dst", dst})
509                         sender := ctx.Neigh[*job.PktEnc.Sender]
510                         freqPath := sender.FreqPath
511                         if freqPath == nil {
512                                 ctx.LogE(
513                                         "rx-no-freq", les, errors.New("freqing is not allowed"),
514                                         func(les LEs) string {
515                                                 return fmt.Sprintf(
516                                                         "Tossing freq %s/%s (%s): %s -> %s",
517                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
518                                                         humanize.IBytes(uint64(pktSize)), src, dst,
519                                                 )
520                                         },
521                                 )
522                                 isBad = true
523                                 goto Closing
524                         }
525                         if !dryRun {
526                                 err = ctx.TxFile(
527                                         sender,
528                                         pkt.Nice,
529                                         filepath.Join(*freqPath, src),
530                                         dst,
531                                         sender.FreqChunked,
532                                         sender.FreqMinSize,
533                                         sender.FreqMaxSize,
534                                 )
535                                 if err != nil {
536                                         ctx.LogE("rx-tx", les, err, func(les LEs) string {
537                                                 return fmt.Sprintf(
538                                                         "Tossing freq %s/%s (%s): %s -> %s: txing",
539                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
540                                                         humanize.IBytes(uint64(pktSize)), src, dst,
541                                                 )
542                                         })
543                                         isBad = true
544                                         goto Closing
545                                 }
546                         }
547                         ctx.LogI("rx", les, func(les LEs) string {
548                                 return fmt.Sprintf(
549                                         "Got file request %s to %s",
550                                         src, ctx.NodeName(job.PktEnc.Sender),
551                                 )
552                         })
553                         if !dryRun {
554                                 if doSeen {
555                                         if fd, err := os.Create(job.Path + SeenSuffix); err == nil {
556                                                 fd.Close() // #nosec G104
557                                         }
558                                 }
559                                 if err = os.Remove(job.Path); err != nil {
560                                         ctx.LogE("rx-remove", les, err, func(les LEs) string {
561                                                 return fmt.Sprintf(
562                                                         "Tossing freq %s/%s (%s): %s -> %s: removing",
563                                                         ctx.NodeName(job.PktEnc.Sender), pktName,
564                                                         humanize.IBytes(uint64(pktSize)), src, dst,
565                                                 )
566                                         })
567                                         isBad = true
568                                 } else if ctx.HdrUsage {
569                                         os.Remove(job.Path + HdrSuffix)
570                                 }
571                                 if len(sendmail) > 0 && ctx.NotifyFreq != nil {
572                                         cmd := exec.Command(
573                                                 sendmail[0],
574                                                 append(sendmail[1:], ctx.NotifyFreq.To)...,
575                                         )
576                                         cmd.Stdin = newNotification(ctx.NotifyFreq, fmt.Sprintf(
577                                                 "Freq from %s: %s", sender.Name, src,
578                                         ), nil)
579                                         if err = cmd.Run(); err != nil {
580                                                 ctx.LogE("rx-notify", les, err, func(les LEs) string {
581                                                         return fmt.Sprintf(
582                                                                 "Tossing freq %s/%s (%s): %s -> %s: notifying",
583                                                                 ctx.NodeName(job.PktEnc.Sender), pktName,
584                                                                 humanize.IBytes(uint64(pktSize)), src, dst,
585                                                         )
586                                                 })
587                                         }
588                                 }
589                         }
590
591                 case PktTypeTrns:
592                         if noTrns {
593                                 goto Closing
594                         }
595                         dst := new([MTHSize]byte)
596                         copy(dst[:], pkt.Path[:int(pkt.PathLen)])
597                         nodeId := NodeId(*dst)
598                         node, known := ctx.Neigh[nodeId]
599                         les := append(les, LE{"Type", "trns"}, LE{"Dst", nodeId})
600                         logMsg := func(les LEs) string {
601                                 return fmt.Sprintf(
602                                         "Tossing trns %s/%s (%s): %s",
603                                         ctx.NodeName(job.PktEnc.Sender),
604                                         pktName,
605                                         humanize.IBytes(uint64(pktSize)),
606                                         nodeId.String(),
607                                 )
608                         }
609                         if !known {
610                                 ctx.LogE("rx-unknown", les, errors.New("unknown node"), logMsg)
611                                 isBad = true
612                                 goto Closing
613                         }
614                         ctx.LogD("rx-tx", les, logMsg)
615                         if !dryRun {
616                                 if err = ctx.TxTrns(node, job.PktEnc.Nice, pktSize, pipeR); err != nil {
617                                         ctx.LogE("rx", les, err, func(les LEs) string {
618                                                 return logMsg(les) + ": txing"
619                                         })
620                                         isBad = true
621                                         goto Closing
622                                 }
623                         }
624                         ctx.LogI("rx", les, func(les LEs) string {
625                                 return fmt.Sprintf(
626                                         "Got transitional packet from %s to %s (%s)",
627                                         ctx.NodeName(job.PktEnc.Sender),
628                                         ctx.NodeName(&nodeId),
629                                         humanize.IBytes(uint64(pktSize)),
630                                 )
631                         })
632                         if !dryRun {
633                                 if doSeen {
634                                         if fd, err := os.Create(job.Path + SeenSuffix); err == nil {
635                                                 fd.Close() // #nosec G104
636                                         }
637                                 }
638                                 if err = os.Remove(job.Path); err != nil {
639                                         ctx.LogE("rx", les, err, func(les LEs) string {
640                                                 return fmt.Sprintf(
641                                                         "Tossing trns %s/%s (%s): %s: removing",
642                                                         ctx.NodeName(job.PktEnc.Sender),
643                                                         pktName,
644                                                         humanize.IBytes(uint64(pktSize)),
645                                                         ctx.NodeName(&nodeId),
646                                                 )
647                                         })
648                                         isBad = true
649                                 } else if ctx.HdrUsage {
650                                         os.Remove(job.Path + HdrSuffix)
651                                 }
652                         }
653
654                 default:
655                         ctx.LogE(
656                                 "rx-type-unknown", les, errors.New("unknown type"),
657                                 func(les LEs) string {
658                                         return fmt.Sprintf(
659                                                 "Tossing %s/%s (%s)",
660                                                 ctx.NodeName(job.PktEnc.Sender),
661                                                 pktName,
662                                                 humanize.IBytes(uint64(pktSize)),
663                                         )
664                                 },
665                         )
666                         isBad = true
667                 }
668         Closing:
669                 pipeR.Close() // #nosec G104
670         }
671         return isBad
672 }
673
674 func (ctx *Ctx) AutoToss(
675         nodeId *NodeId,
676         nice uint8,
677         doSeen, noFile, noFreq, noExec, noTrns bool,
678 ) (chan struct{}, chan bool) {
679         finish := make(chan struct{})
680         badCode := make(chan bool)
681         go func() {
682                 bad := false
683                 for {
684                         select {
685                         case <-finish:
686                                 badCode <- bad
687                                 break
688                         default:
689                         }
690                         time.Sleep(time.Second)
691                         bad = !ctx.Toss(nodeId, nice, false, doSeen, noFile, noFreq, noExec, noTrns) || bad
692                 }
693         }()
694         return finish, badCode
695 }