nncp-rm
nncp-stat
nncp-toss
+nncp-trns
nncp-xfer
* nncp-file::
* nncp-exec::
* nncp-freq::
+* nncp-trns::
Packets sharing commands
@include cmd/nncp-file.texi
@include cmd/nncp-exec.texi
@include cmd/nncp-freq.texi
+@include cmd/nncp-trns.texi
@include cmd/nncp-xfer.texi
@include cmd/nncp-bundle.texi
@include cmd/nncp-toss.texi
--- /dev/null
+@node nncp-trns
+@section nncp-trns
+
+@example
+$ nncp-trns [options] -via NODEx[,...] NODE:PKT
+$ nncp-trns [options] -via NODEx[,...] /path/to/PKT
+
+@end example
+
+Transit specified encrypted packet via another @option{NODEx}es.
+Just manual transition packets creator. Normally you should not use that
+command at all, preferring automatic wrapping in transitional packets
+using the general @option{-via} option and configuration's files one.
@multitable {XXXXX} {XXXX-XX-XX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
@headitem Version @tab Date @tab Size @tab Tarball @tab SHA256 checksum
+@item @ref{Release 7_1_1, 7.1.1} @tab 2021-07-06 @tab 1132 KiB
+@tab @url{download/nncp-7.1.1.tar.xz, link} @url{download/nncp-7.1.1.tar.xz.sig, sign}
+@tab @code{B741C9E3 EC3DB342 893FE081 888C40E4 B94E4298 E5C1A8E0 BA4D179C C239CCCA}
+
@item @ref{Release 7_1_0, 7.1.0} @tab 2021-07-04 @tab 1142 KiB
@tab @url{download/nncp-7.1.0.tar.xz, link} @url{download/nncp-7.1.0.tar.xz.sig, sign}
@tab @code{D3BC010F 5D86BB59 E07A2A84 2FF9C73B 4C2F780B 807EF25C E4BC477C E40764A6}
@end example
@example
-$ nncp-file nodelist-20210704.rec area:nodelist-updates:
+$ nncp-file nodelist-20210704.rec.zst area:nodelist-updates:
$ nncp-toss -node self
@end example
@item
@code{nodeC} does not relay it to anyone. Just stores
-@file{nodelist-20210704.rec} in the incoming directory.
+@file{nodelist-20210704.rec.zst} in the incoming directory.
@item
@code{nodeD} receives packets from both @code{nodeA} and @code{nodeB}.
relay it to the @code{nodeA} also, that will silently remove it when
tossing, because it was already seen.
-@strong{TODO}: we must not relay packet to the node also presenting as
-the sender of the area's message. Obviously it has seen it.
-
@item
When @code{nodeC} sends message to the area, then @code{nodeA} will
receive it twice from @code{nodeB} and @code{nodeD}, ignoring one of
@node Новости
@section Новости
+@node Релиз 7.2.0
+@subsection Релиз 7.2.0
+@itemize
+
+@item
+Появилась @command{nncp-trns} команда для ручного создания транзитных пакетов.
+
+@item
+Если у целевой ноды транзитного пакета задан @option{via} маршрут, то
+использовать его, а не игнорировать.
+
+@item
+Не отправлять multicast пакет оригинатору сообщения, очевидно точно
+видящего свой собственный пакет.
+
+@item
+Намного меньшее потребление памяти во время MTH хэширования когда
+смещение равно нулю: когда пакет не является докачиванием, а например
+проверяется @command{nncp-check} командой.
+
+@end itemize
+
@node Релиз 7.1.1
@subsection Релиз 7.1.1
@itemize
See also this page @ref{Новости, on russian}.
+@node Release 7_2_0
+@section Release 7.2.0
+@itemize
+
+@item
+@command{nncp-trns} command appeared for manual transition packets creation.
+
+@item
+If destination node of transitional packet has non empty @option{via}
+route, then do not ignore, but use it.
+
+@item
+Do not relay multicast packet to area message's originator, that
+obviously has seen its own packet.
+
+@item
+Much less memory usage during MTH hashing when offset is zero: when
+packet is not resumed, but for example checked with @command{nncp-check}
+command.
+
+@end itemize
+
@node Release 7_1_1
@section Release 7.1.1
@itemize
@item
Fixed failing directories fsync after @file{.seen} file creation.
+
@end itemize
@node Release 7_1_0
rm -fr nncp.html
MAKEINFO_OPTS="$MAKEINFO_OPTS --html --css-include style.css"
+MAKEINFO_OPTS="$MAKEINFO_OPTS --set-customization-variable FORMAT_MENU=menu"
MAKEINFO_OPTS="$MAKEINFO_OPTS --set-customization-variable SHOW_TITLE=0"
MAKEINFO_OPTS="$MAKEINFO_OPTS --set-customization-variable DATE_IN_HEADER=1"
MAKEINFO_OPTS="$MAKEINFO_OPTS --set-customization-variable TOP_NODE_UP_URL=index.html"
PORTNAME= nncp
-DISTVERSION= 7.1.0
+DISTVERSION= 7.2.0
CATEGORIES= net
MASTER_SITES= http://www.nncpgo.org/download/
bin/nncp-rm
bin/nncp-stat
bin/nncp-toss
+bin/nncp-trns
bin/nncp-xfer
@dir etc/newsyslog.conf.d
@sample etc/nncp.conf.sample etc/newsyslog.conf.d/nncp.conf
return !(ctx.checkXxIsBad(nodeId, TRx) || ctx.checkXxIsBad(nodeId, TTx))
}
-func (ctx *Ctx) CheckNoCK(nodeId *NodeId, hshValue *[MTHSize]byte, mth *MTH) (int64, error) {
+func (ctx *Ctx) CheckNoCK(nodeId *NodeId, hshValue *[MTHSize]byte, mth MTH) (int64, error) {
dirToSync := filepath.Join(ctx.Spool, nodeId.String(), string(TRx))
pktName := Base32Codec.EncodeToString(hshValue[:])
pktPath := filepath.Join(dirToSync, pktName)
if mth == nil {
gut, err = Check(fd, size, hshValue[:], les, ctx.ShowPrgrs)
} else {
- mth.PktName = pktName
+ mth.SetPktName(pktName)
if _, err = mth.PrependFrom(bufio.NewReaderSize(fd, MTHSize)); err != nil {
return 0, err
}
var (
fn = flag.String("file", "", "Read the file instead of stdin")
seek = flag.Uint64("seek", 0, "Seek the file, hash, rewind, hash remaining")
+ forceFat = flag.Bool("force-fat", false, "Force MTHFat implementation usage")
showPrgrs = flag.Bool("progress", false, "Progress showing")
debug = flag.Bool("debug", false, "Print MTH steps calculations")
version = flag.Bool("version", false, "Print version information")
}
size = fi.Size()
}
- mth := nncp.MTHNew(size, int64(*seek))
+ var mth nncp.MTH
+ if *forceFat {
+ mth = nncp.MTHFatNew(size, int64(*seek))
+ } else {
+ mth = nncp.MTHNew(size, int64(*seek))
+ }
var debugger sync.WaitGroup
if *debug {
fmt.Println("Leaf BLAKE3 key:", hex.EncodeToString(nncp.MTHLeafKey[:]))
fmt.Println("Node BLAKE3 key:", hex.EncodeToString(nncp.MTHNodeKey[:]))
- mth.Events = make(chan nncp.MTHEvent)
+ events := mth.Events()
debugger.Add(1)
go func() {
- for e := range mth.Events {
+ for e := range events {
var t string
switch e.Type {
case nncp.MTHEventAppend:
log.Fatalln(err)
}
if *showPrgrs {
- mth.PktName = *fn
+ mth.SetPktName(*fn)
}
if _, err = mth.PrependFrom(bufio.NewReaderSize(fd, nncp.MTHBlockSize)); err != nil {
log.Fatalln(err)
case nncp.PktTypeTrns:
path = nncp.Base32Codec.EncodeToString(pkt.Path[:pkt.PathLen])
node, err := ctx.FindNode(path)
- if err != nil {
+ if err == nil {
path = fmt.Sprintf("%s (%s)", path, node.Name)
}
case nncp.PktTypeArea:
recipientNode := ctx.Neigh[*pktEnc.Recipient]
if recipientNode == nil {
area = ctx.AreaId2Area[nncp.AreaId(*pktEnc.Recipient)]
- recipientName = "area " + area.Name
+ if area != nil {
+ recipientName = "area " + area.Name
+ }
} else {
recipientName = recipientNode.Name
}
--- /dev/null
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
+
+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
+the Free Software Foundation, version 3 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// Wrap existing encrypted packet to transition ones.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "go.cypherpunks.ru/nncp/v7"
+)
+
+func usage() {
+ fmt.Fprintf(os.Stderr, nncp.UsageHeader())
+ fmt.Fprintf(os.Stderr, "nncp-trns -- transit existing encrypted packet\n\n")
+ fmt.Fprintf(os.Stderr, "Usage: %s [options] -via NODEx[,...] NODE:PKT\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, " (to transit SPOOL/NODE/tx/PKT)\n")
+ fmt.Fprintf(os.Stderr, " %s [options] -via NODEx[,...] /path/to/PKT\nOptions:\n",
+ os.Args[0])
+ flag.PrintDefaults()
+}
+
+func main() {
+ var (
+ cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
+ niceRaw = flag.String("nice", nncp.NicenessFmt(nncp.DefaultNiceFile), "Outbound packet niceness")
+ viaOverride = flag.String("via", "", "Override Via path to destination node")
+ spoolPath = flag.String("spool", "", "Override path to spool")
+ logPath = flag.String("log", "", "Override path to logfile")
+ quiet = flag.Bool("quiet", false, "Print only errors")
+ showPrgrs = flag.Bool("progress", false, "Force progress showing")
+ omitPrgrs = flag.Bool("noprogress", false, "Omit progress showing")
+ debug = flag.Bool("debug", false, "Print debug messages")
+ version = flag.Bool("version", false, "Print version information")
+ warranty = flag.Bool("warranty", false, "Print warranty information")
+ )
+ log.SetFlags(log.Lshortfile)
+ flag.Usage = usage
+ flag.Parse()
+ if *warranty {
+ fmt.Println(nncp.Warranty)
+ return
+ }
+ if *version {
+ fmt.Println(nncp.VersionGet())
+ return
+ }
+ if flag.NArg() != 1 {
+ usage()
+ os.Exit(1)
+ }
+ nice, err := nncp.NicenessParse(*niceRaw)
+ if err != nil {
+ log.Fatalln(err)
+ }
+
+ ctx, err := nncp.CtxFromCmdline(
+ *cfgPath,
+ *spoolPath,
+ *logPath,
+ *quiet,
+ *showPrgrs,
+ *omitPrgrs,
+ *debug,
+ )
+ if err != nil {
+ log.Fatalln("Error during initialization:", err)
+ }
+ if ctx.Self == nil {
+ log.Fatalln("Config lacks private keys")
+ }
+ ctx.Umask()
+
+ var pktPath string
+ var pktName string
+ if _, err = os.Stat(flag.Arg(0)); err == nil {
+ pktPath = flag.Arg(0)
+ pktName = filepath.Base(pktPath)
+ } else {
+ splitted := strings.Split(flag.Arg(0), ":")
+ if len(splitted) != 2 {
+ log.Fatalln("Invalid NODE:PKT specification")
+ }
+ node, err := ctx.FindNode(splitted[0])
+ if err != nil {
+ log.Fatalln("Invalid NODE specified:", err)
+ }
+ pktPath = filepath.Join(
+ ctx.Spool, node.Id.String(), string(nncp.TTx), splitted[1],
+ )
+ pktName = filepath.Base(splitted[1])
+ }
+
+ fd, err := os.Open(pktPath)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ fi, err := fd.Stat()
+ if err != nil {
+ log.Fatalln(err)
+ }
+ pktEnc, _, err := ctx.HdrRead(fd)
+ if err != nil {
+ log.Fatalln(err)
+ }
+ if _, err = fd.Seek(0, io.SeekStart); err != nil {
+ log.Fatalln(err)
+ }
+
+ node := ctx.Neigh[*pktEnc.Recipient]
+ nncp.ViaOverride(*viaOverride, ctx, node)
+ via := node.Via[:len(node.Via)-1]
+ node = ctx.Neigh[*node.Via[len(node.Via)-1]]
+ node.Via = via
+
+ pktTrns, err := nncp.NewPkt(nncp.PktTypeTrns, 0, pktEnc.Recipient[:])
+ if err != nil {
+ panic(err)
+ }
+ if _, err = ctx.Tx(node, pktTrns, nice, fi.Size(), 0, fd, pktName, nil); err != nil {
+ log.Fatalln(err)
+ }
+}
func (ctx *Ctx) IsEnoughSpace(want int64) bool {
var s unix.Statfs_t
if err := unix.Statfs(ctx.Spool, &s); err != nil {
- log.Fatalln(err)
+ log.Fatalln("Can not stat spool:", err)
}
return int64(s.Bavail)*int64(s.Bsize) > want
}
import (
"bytes"
"errors"
+ "hash"
"io"
"lukechampine.com/blake3"
Hsh []byte
}
-type MTH struct {
+type MTH interface {
+ hash.Hash
+ PrependFrom(r io.Reader) (int, error)
+ SetPktName(n string)
+ PrependSize() int64
+ Events() chan MTHEvent
+}
+
+type MTHFat struct {
size int64
- PrependSize int64
+ prependSize int64
skip int64
skipped bool
hasher *blake3.Hasher
hashes [][MTHSize]byte
buf *bytes.Buffer
finished bool
- Events chan MTHEvent
- PktName string
+ events chan MTHEvent
+ pktName string
}
-func MTHNew(size, offset int64) *MTH {
- mth := MTH{
+func MTHFatNew(size, offset int64) MTH {
+ mth := MTHFat{
hasher: blake3.New(MTHSize, MTHLeafKey[:]),
buf: bytes.NewBuffer(make([]byte, 0, 2*MTHBlockSize)),
}
skip = size - offset
}
mth.size = size
- mth.PrependSize = prependSize
+ mth.prependSize = prependSize
mth.skip = skip
mth.hashes = make([][MTHSize]byte, prepends, 1+size/MTHBlockSize)
return &mth
}
-func (mth *MTH) Reset() { panic("not implemented") }
+func (mth *MTHFat) Events() chan MTHEvent {
+ mth.events = make(chan MTHEvent)
+ return mth.events
+}
+
+func (mth *MTHFat) SetPktName(pktName string) { mth.pktName = pktName }
+
+func (mth *MTHFat) PrependSize() int64 { return mth.prependSize }
-func (mth *MTH) Size() int { return MTHSize }
+func (mth *MTHFat) Reset() { panic("not implemented") }
-func (mth *MTH) BlockSize() int { return MTHBlockSize }
+func (mth *MTHFat) Size() int { return MTHSize }
-func (mth *MTH) Write(data []byte) (int, error) {
+func (mth *MTHFat) BlockSize() int { return MTHBlockSize }
+
+func (mth *MTHFat) Write(data []byte) (int, error) {
if mth.finished {
return 0, errors.New("already Sum()ed")
}
mth.hasher.Sum(h[:0])
mth.hasher.Reset()
mth.hashes = append(mth.hashes, *h)
- if mth.Events != nil {
- mth.Events <- MTHEvent{
+ if mth.events != nil {
+ mth.events <- MTHEvent{
MTHEventAppend,
0, len(mth.hashes) - 1,
mth.hashes[len(mth.hashes)-1][:],
return n, err
}
-func (mth *MTH) PrependFrom(r io.Reader) (int, error) {
+func (mth *MTHFat) PrependFrom(r io.Reader) (int, error) {
if mth.finished {
return 0, errors.New("already Sum()ed")
}
var err error
buf := make([]byte, MTHBlockSize)
var i, n, read int
- fullsize := mth.PrependSize
- les := LEs{{"Pkt", mth.PktName}, {"FullSize", fullsize}, {"Size", 0}}
- for mth.PrependSize >= MTHBlockSize {
+ fullsize := mth.prependSize
+ les := LEs{{"Pkt", mth.pktName}, {"FullSize", fullsize}, {"Size", 0}}
+ for mth.prependSize >= MTHBlockSize {
n, err = io.ReadFull(r, buf)
read += n
- mth.PrependSize -= MTHBlockSize
+ mth.prependSize -= MTHBlockSize
if err != nil {
return read, err
}
}
mth.hasher.Sum(mth.hashes[i][:0])
mth.hasher.Reset()
- if mth.Events != nil {
- mth.Events <- MTHEvent{MTHEventPrepend, 0, i, mth.hashes[i][:]}
+ if mth.events != nil {
+ mth.events <- MTHEvent{MTHEventPrepend, 0, i, mth.hashes[i][:]}
}
- if mth.PktName != "" {
+ if mth.pktName != "" {
les[len(les)-1].V = int64(read)
Progress("check", les)
}
i++
}
- if mth.PrependSize > 0 {
- n, err = io.ReadFull(r, buf[:mth.PrependSize])
+ if mth.prependSize > 0 {
+ n, err = io.ReadFull(r, buf[:mth.prependSize])
read += n
if err != nil {
return read, err
}
- if _, err = mth.hasher.Write(buf[:mth.PrependSize]); err != nil {
+ if _, err = mth.hasher.Write(buf[:mth.prependSize]); err != nil {
panic(err)
}
mth.hasher.Sum(mth.hashes[i][:0])
mth.hasher.Reset()
- if mth.Events != nil {
- mth.Events <- MTHEvent{MTHEventPrepend, 0, i, mth.hashes[i][:]}
+ if mth.events != nil {
+ mth.events <- MTHEvent{MTHEventPrepend, 0, i, mth.hashes[i][:]}
}
- if mth.PktName != "" {
+ if mth.pktName != "" {
les[len(les)-1].V = fullsize
Progress("check", les)
}
return read, nil
}
-func (mth *MTH) Sum(b []byte) []byte {
+func (mth *MTHFat) Sum(b []byte) []byte {
if mth.finished {
return append(b, mth.hashes[0][:]...)
}
mth.hasher.Sum(h[:0])
mth.hasher.Reset()
mth.hashes = append(mth.hashes, *h)
- if mth.Events != nil {
- mth.Events <- MTHEvent{
+ if mth.events != nil {
+ mth.events <- MTHEvent{
MTHEventAppend,
0, len(mth.hashes) - 1,
mth.hashes[len(mth.hashes)-1][:],
mth.hasher.Sum(h[:0])
mth.hasher.Reset()
mth.hashes = append(mth.hashes, *h)
- if mth.Events != nil {
- mth.Events <- MTHEvent{MTHEventAppend, 0, 0, mth.hashes[0][:]}
+ if mth.events != nil {
+ mth.events <- MTHEvent{MTHEventAppend, 0, 0, mth.hashes[0][:]}
}
fallthrough
case 1:
mth.hashes = append(mth.hashes, mth.hashes[0])
- if mth.Events != nil {
- mth.Events <- MTHEvent{MTHEventAppend, 0, 1, mth.hashes[1][:]}
+ if mth.events != nil {
+ mth.events <- MTHEvent{MTHEventAppend, 0, 1, mth.hashes[1][:]}
}
}
mth.hasher = blake3.New(MTHSize, MTHNodeKey[:])
mth.hasher.Sum(h[:0])
mth.hasher.Reset()
hashesUp = append(hashesUp, *h)
- if mth.Events != nil {
- mth.Events <- MTHEvent{
+ if mth.events != nil {
+ mth.events <- MTHEvent{
MTHEventFold,
level, len(hashesUp) - 1,
hashesUp[len(hashesUp)-1][:],
}
if len(mth.hashes)%2 == 1 {
hashesUp = append(hashesUp, mth.hashes[len(mth.hashes)-1])
- if mth.Events != nil {
- mth.Events <- MTHEvent{
+ if mth.events != nil {
+ mth.events <- MTHEvent{
MTHEventAppend,
level, len(hashesUp) - 1,
hashesUp[len(hashesUp)-1][:],
level++
}
mth.finished = true
- if mth.Events != nil {
- close(mth.Events)
+ if mth.events != nil {
+ close(mth.events)
}
return append(b, mth.hashes[0][:]...)
}
+
+type MTHSeqEnt struct {
+ l int
+ h [MTHSize]byte
+}
+
+type MTHSeq struct {
+ hasherLeaf *blake3.Hasher
+ hasherNode *blake3.Hasher
+ hashes []MTHSeqEnt
+ buf *bytes.Buffer
+ events chan MTHEvent
+ ctrs []int
+ finished bool
+}
+
+func MTHSeqNew() *MTHSeq {
+ mth := MTHSeq{
+ hasherLeaf: blake3.New(MTHSize, MTHLeafKey[:]),
+ hasherNode: blake3.New(MTHSize, MTHNodeKey[:]),
+ buf: bytes.NewBuffer(make([]byte, 0, 2*MTHBlockSize)),
+ ctrs: make([]int, 1, 2),
+ }
+ return &mth
+}
+
+func (mth *MTHSeq) Reset() { panic("not implemented") }
+
+func (mth *MTHSeq) Size() int { return MTHSize }
+
+func (mth *MTHSeq) BlockSize() int { return MTHBlockSize }
+
+func (mth *MTHSeq) PrependFrom(r io.Reader) (int, error) {
+ panic("must not reach that code")
+}
+
+func (mth *MTHSeq) Events() chan MTHEvent {
+ mth.events = make(chan MTHEvent)
+ return mth.events
+}
+
+func (mth *MTHSeq) SetPktName(pktName string) {}
+
+func (mth *MTHSeq) PrependSize() int64 { return 0 }
+
+func (mth *MTHSeq) leafAdd() {
+ ent := MTHSeqEnt{l: 0}
+ mth.hasherLeaf.Sum(ent.h[:0])
+ mth.hasherLeaf.Reset()
+ mth.hashes = append(mth.hashes, ent)
+ if mth.events != nil {
+ mth.events <- MTHEvent{
+ MTHEventAppend, 0, mth.ctrs[0],
+ mth.hashes[len(mth.hashes)-1].h[:],
+ }
+ }
+ mth.ctrs[0]++
+}
+
+func (mth *MTHSeq) incr(l int) {
+ if len(mth.ctrs) <= l {
+ mth.ctrs = append(mth.ctrs, 0)
+ } else {
+ mth.ctrs[l]++
+ }
+}
+
+func (mth *MTHSeq) fold() {
+ for len(mth.hashes) >= 2 {
+ if mth.hashes[len(mth.hashes)-2].l != mth.hashes[len(mth.hashes)-1].l {
+ break
+ }
+ if _, err := mth.hasherNode.Write(mth.hashes[len(mth.hashes)-2].h[:]); err != nil {
+ panic(err)
+ }
+ if _, err := mth.hasherNode.Write(mth.hashes[len(mth.hashes)-1].h[:]); err != nil {
+ panic(err)
+ }
+ mth.hashes = mth.hashes[:len(mth.hashes)-1]
+ end := &mth.hashes[len(mth.hashes)-1]
+ end.l++
+ mth.incr(end.l)
+ mth.hasherNode.Sum(end.h[:0])
+ mth.hasherNode.Reset()
+ if mth.events != nil {
+ mth.events <- MTHEvent{MTHEventFold, end.l, mth.ctrs[end.l], end.h[:]}
+ }
+ }
+}
+
+func (mth *MTHSeq) Write(data []byte) (int, error) {
+ if mth.finished {
+ return 0, errors.New("already Sum()ed")
+ }
+ n, err := mth.buf.Write(data)
+ if err != nil {
+ return n, err
+ }
+ for mth.buf.Len() >= MTHBlockSize {
+ if _, err = mth.hasherLeaf.Write(mth.buf.Next(MTHBlockSize)); err != nil {
+ return n, err
+ }
+ mth.leafAdd()
+ mth.fold()
+ }
+ return n, err
+}
+
+func (mth *MTHSeq) Sum(b []byte) []byte {
+ if mth.finished {
+ return append(b, mth.hashes[0].h[:]...)
+ }
+ if mth.buf.Len() > 0 {
+ if _, err := mth.hasherLeaf.Write(mth.buf.Next(MTHBlockSize)); err != nil {
+ panic(err)
+ }
+ mth.leafAdd()
+ mth.fold()
+ }
+ switch mth.ctrs[0] {
+ case 0:
+ if _, err := mth.hasherLeaf.Write(nil); err != nil {
+ panic(err)
+ }
+ mth.leafAdd()
+ fallthrough
+ case 1:
+ mth.hashes = append(mth.hashes, mth.hashes[0])
+ mth.ctrs[0]++
+ if mth.events != nil {
+ mth.events <- MTHEvent{
+ MTHEventAppend, 0, mth.ctrs[0],
+ mth.hashes[len(mth.hashes)-1].h[:],
+ }
+ }
+ mth.fold()
+ }
+ for len(mth.hashes) >= 2 {
+ l := mth.hashes[len(mth.hashes)-2].l
+ mth.incr(l)
+ mth.hashes[len(mth.hashes)-1].l = l
+ if mth.events != nil {
+ mth.events <- MTHEvent{
+ MTHEventAppend, l, mth.ctrs[l],
+ mth.hashes[len(mth.hashes)-1].h[:],
+ }
+ }
+ mth.fold()
+ }
+ mth.finished = true
+ if mth.events != nil {
+ close(mth.events)
+ }
+ return append(b, mth.hashes[0].h[:]...)
+}
+
+func MTHNew(size, offset int64) MTH {
+ if offset == 0 {
+ return MTHSeqNew()
+ }
+ return MTHFatNew(size, offset)
+}
"lukechampine.com/blake3"
)
-func TestMTHSymmetric(t *testing.T) {
+func TestMTHFatSymmetric(t *testing.T) {
xof := blake3.New(32, nil).XOF()
f := func(size uint32, offset uint32) bool {
size %= 2 * 1024 * 1024
}
offset = offset % size
- mth := MTHNew(int64(size), 0)
+ mth := MTHFatNew(int64(size), 0)
if _, err := io.Copy(mth, bytes.NewReader(data)); err != nil {
panic(err)
}
hsh0 := mth.Sum(nil)
- mth = MTHNew(int64(size), int64(offset))
+ mth = MTHFatNew(int64(size), int64(offset))
if _, err := io.Copy(mth, bytes.NewReader(data[int(offset):])); err != nil {
panic(err)
}
return false
}
- mth = MTHNew(0, 0)
+ mth = MTHFatNew(0, 0)
mth.Write(data)
if bytes.Compare(hsh0, mth.Sum(nil)) != 0 {
return false
}
data = append(data, 0)
- mth = MTHNew(int64(size)+1, 0)
+ mth = MTHFatNew(int64(size)+1, 0)
if _, err := io.Copy(mth, bytes.NewReader(data)); err != nil {
panic(err)
}
return false
}
- mth = MTHNew(int64(size)+1, int64(offset))
+ mth = MTHFatNew(int64(size)+1, int64(offset))
if _, err := io.Copy(mth, bytes.NewReader(data[int(offset):])); err != nil {
panic(err)
}
return false
}
- mth = MTHNew(0, 0)
+ mth = MTHFatNew(0, 0)
mth.Write(data)
if bytes.Compare(hsh00, mth.Sum(nil)) != 0 {
return false
}
}
+func TestMTHSeqAndFatEqual(t *testing.T) {
+ xof := blake3.New(32, nil).XOF()
+ f := func(size uint32, offset uint32) bool {
+ size %= 10 * 1024 * 1024
+ data := make([]byte, int(size), int(size)+1)
+ if _, err := io.ReadFull(xof, data); err != nil {
+ panic(err)
+ }
+ fat := MTHFatNew(int64(size), 0)
+ if _, err := io.Copy(fat, bytes.NewReader(data)); err != nil {
+ panic(err)
+ }
+ hshFat := fat.Sum(nil)
+ seq := MTHSeqNew()
+ if _, err := io.Copy(seq, bytes.NewReader(data)); err != nil {
+ panic(err)
+ }
+ return bytes.Compare(hshFat, seq.Sum(nil)) == 0
+ }
+ if err := quick.Check(f, nil); err != nil {
+ t.Error(err)
+ }
+}
+
func TestMTHNull(t *testing.T) {
- mth := MTHNew(0, 0)
- if _, err := mth.Write(nil); err != nil {
+ fat := MTHFatNew(0, 0)
+ if _, err := fat.Write(nil); err != nil {
t.Error(err)
}
- mth.Sum(nil)
+ hshFat := fat.Sum(nil)
+
+ seq := MTHSeqNew()
+ if _, err := seq.Write(nil); err != nil {
+ t.Error(err)
+ }
+ if bytes.Compare(hshFat, seq.Sum(nil)) != 0 {
+ t.FailNow()
+ }
}
const Base32Encoded32Len = 52
var (
- Version string = "7.1.1"
+ Version string = "7.2.0"
Base32Codec *base32.Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
)
)
type MTHAndOffset struct {
- mth *MTH
+ mth MTH
offset uint64
}
type SPCheckerTask struct {
nodeId *NodeId
hsh *[MTHSize]byte
- mth *MTH
+ mth MTH
done chan []byte
}
}
if hasherAndOffset != nil {
delete(state.fileHashers, filePath)
- if hasherAndOffset.mth.PrependSize == 0 {
+ if hasherAndOffset.mth.PrependSize() == 0 {
if bytes.Compare(hasherAndOffset.mth.Sum(nil), file.Hash[:]) != 0 {
state.Ctx.LogE(
"sp-file-bad-checksum", lesp,
}
ctx.LogD("rx-tx", les, logMsg)
if !dryRun {
- if err = ctx.TxTrns(node, nice, int64(pktSize), pipeR); err != nil {
- ctx.LogE("rx", les, err, func(les LEs) string {
- return logMsg(les) + ": txing"
- })
- return err
+ if len(node.Via) == 0 {
+ if err = ctx.TxTrns(node, nice, int64(pktSize), pipeR); err != nil {
+ ctx.LogE("rx", les, err, func(les LEs) string {
+ return logMsg(les) + ": txing"
+ })
+ return err
+ }
+ } else {
+ via := node.Via[:len(node.Via)-1]
+ node = ctx.Neigh[*node.Via[len(node.Via)-1]]
+ node = &Node{Id: node.Id, Via: via, ExchPub: node.ExchPub}
+ pktTrns, err := NewPkt(PktTypeTrns, 0, nodeId[:])
+ if err != nil {
+ panic(err)
+ }
+ if _, err = ctx.Tx(
+ node,
+ pktTrns,
+ nice,
+ int64(pktSize), 0,
+ pipeR,
+ pktName,
+ nil,
+ ); err != nil {
+ ctx.LogE("rx", les, err, func(les LEs) string {
+ return logMsg(les) + ": txing"
+ })
+ return err
+ }
}
}
ctx.LogI("rx", les, func(les LEs) string {
})
continue
}
- if nodeId != sender.Id {
+ if nodeId != sender.Id && nodeId != pktEnc.Sender {
ctx.LogI("rx-area-echo", lesEcho, logMsgNode)
if _, err = ctx.Tx(
node, &pkt, nice, int64(pktSize), 0, fullPipeR, pktName, nil,
)
if err != nil {
pipeW.CloseWithError(err)
- go func() { <-errs }()
+ <-errs
return err
}
pipeW.Close()
isBad = true
continue
}
+ sender := ctx.Neigh[*job.PktEnc.Sender]
+ if sender == nil {
+ err := errors.New("unknown node")
+ ctx.LogE("rx-open", les, err, func(les LEs) string {
+ return fmt.Sprintf(
+ "Tossing %s/%s",
+ ctx.NodeName(job.PktEnc.Sender), pktName,
+ )
+ })
+ isBad = true
+ continue
+ }
errs := make(chan error, 1)
var sharedKey []byte
Retry:
pipeR,
pktName,
les,
- ctx.Neigh[*job.PktEnc.Sender],
+ sender,
job.PktEnc.Nice,
uint64(pktSizeWithoutEnc(job.Size)),
job.Path,
if err != nil {
isBad = true
fd.Close()
- go func() { <-errs }()
+ <-errs
continue
}
if err = <-errs; err == JobRepeatProcess {