]> Cypherpunks.ru repositories - nncp.git/commitdiff
Merge branch 'develop' 4.0
authorSergey Matveev <stargrave@stargrave.org>
Sun, 28 Apr 2019 13:47:27 +0000 (16:47 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sun, 28 Apr 2019 13:47:27 +0000 (16:47 +0300)
79 files changed:
.gitmodules
BSDmakefile
GNUmakefile
VERSION
common.mk
doc/about.ru.texi
doc/cmds.texi
doc/download.texi
doc/eblob.texi
doc/index.texi
doc/integrity.texi
doc/news.ru.texi
doc/news.texi
doc/pkt.texi
doc/style.css
doc/usecases.ru.texi
doc/usecases.texi
makedist.sh
ports/nncp/Makefile
ports/nncp/pkg-descr
src/chacha20 [deleted symlink]
src/cypherpunks.ru/balloon [deleted submodule]
src/cypherpunks.ru/nncp/call.go
src/cypherpunks.ru/nncp/cfg.go
src/cypherpunks.ru/nncp/check.go
src/cypherpunks.ru/nncp/chunked.go
src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go
src/cypherpunks.ru/nncp/cmd/nncp-call/main.go
src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go
src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go
src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go
src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go
src/cypherpunks.ru/nncp/cmd/nncp-check/main.go
src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go
src/cypherpunks.ru/nncp/cmd/nncp-exec/main.go
src/cypherpunks.ru/nncp/cmd/nncp-file/main.go
src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go
src/cypherpunks.ru/nncp/cmd/nncp-log/main.go
src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go
src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go
src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go
src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go
src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go
src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go
src/cypherpunks.ru/nncp/ctx.go
src/cypherpunks.ru/nncp/eblob.go
src/cypherpunks.ru/nncp/go.mod [new file with mode: 0644]
src/cypherpunks.ru/nncp/go.sum [new file with mode: 0644]
src/cypherpunks.ru/nncp/humanizer.go
src/cypherpunks.ru/nncp/jobs.go
src/cypherpunks.ru/nncp/lockdir.go
src/cypherpunks.ru/nncp/log.go
src/cypherpunks.ru/nncp/nncp.go
src/cypherpunks.ru/nncp/node.go
src/cypherpunks.ru/nncp/pkt.go
src/cypherpunks.ru/nncp/pkt_test.go
src/cypherpunks.ru/nncp/sp.go
src/cypherpunks.ru/nncp/tmp.go
src/cypherpunks.ru/nncp/toss.go
src/cypherpunks.ru/nncp/toss_test.go
src/cypherpunks.ru/nncp/tx.go
src/cypherpunks.ru/nncp/tx_test.go
src/cypherpunks.ru/nncp/vendor/cypherpunks.ru/balloon [new submodule]
src/cypherpunks.ru/nncp/vendor/github.com/davecgh/go-xdr [moved from src/github.com/davecgh/go-xdr with 100% similarity]
src/cypherpunks.ru/nncp/vendor/github.com/dustin/go-humanize [new submodule]
src/cypherpunks.ru/nncp/vendor/github.com/flynn/noise [moved from src/github.com/flynn/noise with 100% similarity]
src/cypherpunks.ru/nncp/vendor/github.com/gorhill/cronexpr [moved from src/github.com/gorhill/cronexpr with 100% similarity]
src/cypherpunks.ru/nncp/vendor/golang.org/x/crypto [new submodule]
src/cypherpunks.ru/nncp/vendor/golang.org/x/net [new submodule]
src/cypherpunks.ru/nncp/vendor/golang.org/x/sys [new submodule]
src/cypherpunks.ru/nncp/vendor/gopkg.in/check.v1 [new submodule]
src/cypherpunks.ru/nncp/vendor/gopkg.in/yaml.v2 [new submodule]
src/cypherpunks.ru/nncp/via.go
src/github.com/dustin/go-humanize [deleted submodule]
src/golang.org/x/crypto [deleted submodule]
src/golang.org/x/net [deleted submodule]
src/golang.org/x/sys [deleted submodule]
src/gopkg.in/check.v1 [deleted submodule]
src/gopkg.in/yaml.v2 [deleted submodule]

index fb795d974753b761176066547db5061ebe284a0b..12047c59d61e15c6d1f438e21bd3f31059cf3489 100644 (file)
@@ -1,32 +1,32 @@
-[submodule "src/github.com/davecgh/go-xdr"]
-       path = src/github.com/davecgh/go-xdr
+[submodule "src/cypherpunks.ru/nncp/vendor/github.com/davecgh/go-xdr"]
+       path = src/cypherpunks.ru/nncp/vendor/github.com/davecgh/go-xdr
        url = https://github.com/davecgh/go-xdr.git
-[submodule "src/github.com/dustin/go-humanize"]
-       path = src/github.com/dustin/go-humanize
+[submodule "src/cypherpunks.ru/nncp/vendor/github.com/dustin/go-humanize"]
+       path = src/cypherpunks.ru/nncp/vendor/github.com/dustin/go-humanize
        url = https://github.com/dustin/go-humanize.git
-[submodule "src/github.com/flynn/noise"]
-       path = src/github.com/flynn/noise
+[submodule "src/cypherpunks.ru/nncp/vendor/github.com/flynn/noise"]
+       path = src/cypherpunks.ru/nncp/vendor/github.com/flynn/noise
        url = https://github.com/flynn/noise.git
-[submodule "src/gopkg.in/check.v1"]
-       path = src/gopkg.in/check.v1
+[submodule "src/cypherpunks.ru/nncp/vendor/gopkg.in/check.v1"]
+       path = src/cypherpunks.ru/nncp/vendor/gopkg.in/check.v1
        url = https://github.com/go-check/check.git
        branch = v1
-[submodule "src/gopkg.in/yaml.v2"]
-       path = src/gopkg.in/yaml.v2
+[submodule "src/cypherpunks.ru/nncp/vendor/gopkg.in/yaml.v2"]
+       path = src/cypherpunks.ru/nncp/vendor/gopkg.in/yaml.v2
        url = https://github.com/go-yaml/yaml.git
        branch = v2
-[submodule "src/golang.org/x/crypto"]
-       path = src/golang.org/x/crypto
+[submodule "src/cypherpunks.ru/nncp/vendor/golang.org/x/crypto"]
+       path = src/cypherpunks.ru/nncp/vendor/golang.org/x/crypto
        url = https://go.googlesource.com/crypto
-[submodule "src/golang.org/x/sys"]
-       path = src/golang.org/x/sys
+[submodule "src/cypherpunks.ru/nncp/vendor/golang.org/x/sys"]
+       path = src/cypherpunks.ru/nncp/vendor/golang.org/x/sys
        url = https://go.googlesource.com/sys
-[submodule "src/golang.org/x/net"]
-       path = src/golang.org/x/net
+[submodule "src/cypherpunks.ru/nncp/vendor/golang.org/x/net"]
+       path = src/cypherpunks.ru/nncp/vendor/golang.org/x/net
        url = https://go.googlesource.com/net
-[submodule "src/github.com/gorhill/cronexpr"]
-       path = src/github.com/gorhill/cronexpr
+[submodule "src/cypherpunks.ru/nncp/vendor/github.com/gorhill/cronexpr"]
+       path = src/cypherpunks.ru/nncp/vendor/github.com/gorhill/cronexpr
        url = https://github.com/gorhill/cronexpr.git
-[submodule "src/cypherpunks.ru/balloon"]
-       path = src/cypherpunks.ru/balloon
+[submodule "src/cypherpunks.ru/nncp/vendor/cypherpunks.ru/balloon"]
+       path = src/cypherpunks.ru/nncp/vendor/cypherpunks.ru/balloon
        url = git://git.cypherpunks.ru/balloon.git
index b57abb478822e7791fef04ac3a0532aae8b0e703..63c724c3499450e58a502112a4e7176cd1ede947 100644 (file)
@@ -1,4 +1,3 @@
-GOPATH != pwd
 VERSION != cat VERSION
 
 include common.mk
index 79efdfc08c749e956eb3812594a8386a93567d04..5af4eb08aa90362082b1816f2f27554e8a2b7d7c 100644 (file)
@@ -1,4 +1,3 @@
-GOPATH = $(shell pwd)
 VERSION = $(shell cat VERSION)
 
 include common.mk
diff --git a/VERSION b/VERSION
index 2f4b60750dc3500b0e4cf08f316a960a7ca42b40..5186d07068cfed4c3f4705df79c73e115dd35c43 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.4
+4.0
index 145e84397c3edbb4a656913382eda6dcc950d392..602d754be0f7f9fabb8e0b00fe15f466e3db9884 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -1,4 +1,5 @@
 PREFIX ?= /usr/local
+GO ?= go
 
 SENDMAIL ?= /usr/sbin/sendmail
 CFGPATH ?= $(PREFIX)/etc/nncp.yaml
@@ -15,6 +16,7 @@ LDFLAGS = \
        -X cypherpunks.ru/nncp.DefaultSendmailPath=$(SENDMAIL) \
        -X cypherpunks.ru/nncp.DefaultSpoolPath=$(SPOOLPATH) \
        -X cypherpunks.ru/nncp.DefaultLogPath=$(LOGPATH)
+BUILDMOD ?= -mod=vendor
 
 ALL = \
        nncp-bundle \
@@ -36,67 +38,24 @@ ALL = \
        nncp-toss \
        nncp-xfer
 
-all: $(ALL)
-
-nncp-bundle:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-bundle
-
-nncp-call:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-call
-
-nncp-caller:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-caller
-
-nncp-cfgenc:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgenc
-
-nncp-cfgmin:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgmin
-
-nncp-cfgnew:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgnew
-
-nncp-check:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-check
-
-nncp-daemon:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-daemon
+SRC := $(PWD)/src/cypherpunks.ru/nncp
+BIN := $(PWD)/bin
+GOPATH ?= $(PWD)/gopath
 
-nncp-exec:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-exec
-
-nncp-file:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-file
-
-nncp-freq:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-freq
-
-nncp-log:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-log
-
-nncp-pkt:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-pkt
-
-nncp-reass:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-reass
-
-nncp-rm:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-rm
-
-nncp-stat:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-stat
+all: $(ALL)
 
-nncp-toss:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-toss
+$(BIN):
+       mkdir -p $(BIN)
 
-nncp-xfer:
-       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-xfer
+$(ALL): $(BIN)
+       cd $(SRC) ; GOPATH=$(GOPATH) $(GO) build $(BUILDMOD) -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/$@
+       mv $(SRC)/$@ $(BIN)
 
 test:
-       GOPATH=$(GOPATH) go test -failfast cypherpunks.ru/nncp/...
+       cd $(SRC) ; GOPATH=$(GOPATH) $(GO) test $(BUILDMOD) -failfast cypherpunks.ru/nncp/...
 
 clean:
-       rm -f $(ALL)
+       rm -rf bin
 
 .PHONY: doc
 
@@ -105,7 +64,7 @@ doc:
 
 install: all doc
        mkdir -p $(BINDIR)
-       cp -f $(ALL) $(BINDIR)
+       (cd bin ; cp -f $(ALL) $(BINDIR))
        for e in $(ALL) ; do chmod 755 $(BINDIR)/$$e ; done
        mkdir -p $(INFODIR)
        cp -f doc/nncp.info $(INFODIR)
index 3b2664c863661fc799f2a900003317f9d3437157..497bc40a2aab8858d0b0c3957cb36946b3852a0d 100644 (file)
@@ -1,6 +1,8 @@
 @node Об утилитах
 @section Подробнее об утилитах NNCP
 
+@verbatiminclude pedro.txt
+
 @strong{NNCP} (Node to Node copy) это набор утилит упрощающий безопасный
 обмен файлами, почтой и командами в режиме сохранить-и-переслать.
 
index 2f6f386f66a558a058749551799af09e409dd540..ac665ace0d3a1c166a31f10ba8f685a11c5a3bdc 100644 (file)
@@ -20,8 +20,6 @@ Nearly all commands have the following common options:
 @item -replynice
     Set desired reply packet @ref{Niceness, niceness level}. Only freq
     and exec packets look at that niceness level.
-@item -node
-    Process only single specified node.
 @item -via
     Override @ref{CfgVia, via} configuration option for destination node.
     Specified nodes must be separated with comma: @verb{|NODE1,NODE2|}.
@@ -97,6 +95,8 @@ their integrity.
     [-onlinedeadline INT]
     [-maxonlinetime INT]
     [-rx|-tx]
+    [-list]
+    [-pkts PKT,PKT,...]
     [-rxrate INT]
     [-txrate INT]
     NODE[:ADDR] [FORCEADDR]
@@ -115,7 +115,11 @@ only outbound transmission is performed. @option{-onlinedeadline}
 overrides @ref{CfgOnlineDeadline, @emph{onlinedeadline}}.
 @option{-maxonlinetime} overrides @ref{CfgMaxOnlineTime,
 @emph{maxonlinetime}}. @option{-rxrate}/@option{-txrate} override
-@ref{CfgXxRate, rxrate/txrate}.
+@ref{CfgXxRate, rxrate/txrate}. @option{-list} option allows you to list
+packets of remote node, without any transmission.
+
+You can specify what packets your want to download, by specifying
+@option{-pkts} option with comma-separated list of packets identifiers.
 
 @node nncp-caller
 @section nncp-caller
@@ -301,12 +305,13 @@ This command queues file in @ref{Spool, spool} directory immediately
 If @file{SRC} equals to @file{-}, then create an encrypted temporary
 file and copy everything taken from stdin to it and use for outbound
 packet creation. Pay attention that if you want to send 1 GiB of data
-taken from stdin, then you have to have 2 GiB of disk space for that
-temporary file and resulting encrypted packet. You can control where
-temporary file will be stored using @env{TMPDIR} environment variable.
-Encryption is performed with @url{https://cr.yp.to/chacha.html,
-ChaCha20} algorithm. Data is splitted on 128 KiB blocks. Each block is
-encrypted with increasing nonce counter.
+taken from stdin, then you have to have more than 2 GiB of disk space
+for that temporary file and resulting encrypted packet. You can control
+where temporary file will be stored using @env{TMPDIR} environment
+variable. Encryption is performed in AEAD mode with
+@url{https://cr.yp.to/chacha.html, ChaCha20}-@url{https://en.wikipedia.org/wiki/Poly1305, Poly1305}
+algorithms. Data is splitted on 128 KiB blocks. Each block is encrypted
+with increasing nonce counter.
 
 If @option{-chunked} is specified, then source file will be split
 @ref{Chunked, on chunks}. @option{INT} is the desired chunk size in
@@ -350,6 +355,7 @@ Parse @ref{Log, log} file and print out its records in human-readable form.
 @verbatim
 % nncp-pkt [options] < pkt
 % nncp-pkt [options] [-decompress] -dump < pkt > payload
+% nncp-pkt -overheads
 @end verbatim
 
 Low level packet parser. Normally it should not be used, but can help in
@@ -382,6 +388,8 @@ And with the @option{-dump} option it will give you the actual payload
 tries to zlib-decompress the data from plain packet (useful for mail
 packets).
 
+@option{-overheads} options print encrypted, plain and size header overheads.
+
 @node nncp-reass
 @section nncp-reass
 
@@ -475,25 +483,26 @@ ones. If @option{-seen} option is specified, then delete only
 @section nncp-stat
 
 @verbatim
-% nncp-stat [options]
+% nncp-stat [options] [-node NODE]
 @end verbatim
 
 Print current @ref{Spool, spool} statistics about unsent and unprocessed
-packets. For each node and each niceness level there will be printed how
-many packets (with the total size) are in inbound (Rx) and outbound (Tx)
-queues.
+packets. For each node (unless @option{-node} specified) and each
+niceness level there will be printed how many packets (with the total
+size) are in inbound (Rx) and outbound (Tx) queues.
 
 @node nncp-toss
 @section nncp-toss
 
 @verbatim
 % nncp-toss [options]
+    [-node NODE]
     [-dryrun]
     [-cycle INT]
     [-seen]
     [-nofile]
     [-nofreq]
-    [-nomail]
+    [-noexec]
     [-notrns]
 @end verbatim
 
@@ -515,14 +524,14 @@ successful tossing of @file{XXX} packet. @ref{nncp-xfer},
 inbound packets that has been already seen, processed and tossed. This
 is helpful to prevent duplicates.
 
-@option{-nofile}, @option{-nofreq}, @option{-nomail}, @option{-notrns}
+@option{-nofile}, @option{-nofreq}, @option{-noexec}, @option{-notrns}
 options allow to disable any kind of packet types processing.
 
 @node nncp-xfer
 @section nncp-xfer
 
 @verbatim
-% nncp-xfer [options] [-mkdir] [-keep] [-rx|-tx] DIR
+% nncp-xfer [options] [-node NODE] [-mkdir] [-keep] [-rx|-tx] DIR
 @end verbatim
 
 Search for directory in @file{DIR} containing inbound packets for us and
index 65bc9f933fb485b7faf9087a7dd7898a6338d1fb..0722f0413c236d8e642371a64e347bf041a65d43 100644 (file)
@@ -20,79 +20,82 @@ Tarballs include all necessary required libraries:
 @item @code{golang.org/x/sys} @tab BSD 3-Clause
 @end multitable
 
-@multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
-@headitem Version @tab Size @tab Tarball @tab SHA256 checksum
+@multitable {XXXXX} {XXXX-XX-XX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
+@headitem Version @tab Date @tab Size @tab Tarball @tab SHA256 checksum
 
-@item @ref{Release 3.3, 3.3} @tab 1152 KiB
+@item @ref{Release 3.4, 3.4} @tab 2018-06-10 @tab 1154 KiB
+@tab @url{download/nncp-3.4.tar.xz, link} @url{download/nncp-3.4.tar.xz.sig, sign}
+@tab @code{9796C4CB 7B670FC7 5FEED3CD 467CA556 B230387D 935B09BB 4B19FD57 FD17FFBA}
+
+@item @ref{Release 3.3, 3.3} @tab 2018-06-02 @tab 1152 KiB
 @tab @url{download/nncp-3.3.tar.xz, link} @url{download/nncp-3.3.tar.xz.sig, sign}
 @tab @code{1F8FA9B4 6125D8A9 0608298B A1ED87E1 12DB2D8B 81C766DE F4DFE191 C7B1BFC2}
 
-@item @ref{Release 3.2, 3.2} @tab 1147 KiB
+@item @ref{Release 3.2, 3.2} @tab 2018-05-27 @tab 1147 KiB
 @tab @url{download/nncp-3.2.tar.xz, link} @url{download/nncp-3.2.tar.xz.sig, sign}
 @tab @code{BE76802F 1E273D1D E91F0648 A7CB23C5 989F5390 A36F2D0C FD873046 51B9141E}
 
-@item @ref{Release 3.1, 3.1} @tab 1145 KiB
+@item @ref{Release 3.1, 3.1} @tab 2018-02-18 @tab 1145 KiB
 @tab @url{download/nncp-3.1.tar.xz, link} @url{download/nncp-3.1.tar.xz.sig, sign}
 @tab @code{B9344516 4230B58E 8AAADAA2 066F37F2 493CCB71 B025126B BCAD8FAD 6535149F}
 
-@item @ref{Release 3.0, 3.0} @tab 993 KiB
+@item @ref{Release 3.0, 3.0} @tab 2017-12-30 @tab 993 KiB
 @tab @url{download/nncp-3.0.tar.xz, link} @url{download/nncp-3.0.tar.xz.sig, sign}
 @tab @code{248B2257 2F576E79 A19672E9 B82EB649 18FC95A9 194408C0 67EA4DD3 0468286D}
 
-@item @ref{Release 2.0, 2.0} @tab 986 KiB
+@item @ref{Release 2.0, 2.0} @tab 2017-12-02 @tab 986 KiB
 @tab @url{download/nncp-2.0.tar.xz, link} @url{download/nncp-2.0.tar.xz.sig, sign}
 @tab @code{BEF31B13 FB25381E A511FB77 067798AB 27409238 BDF5600F E2EADB29 E5E78996}
 
-@item @ref{Release 1.0, 1.0} @tab 987 KiB
+@item @ref{Release 1.0, 1.0} @tab 2017-12-02 @tab 987 KiB
 @tab @url{download/nncp-1.0.tar.xz, link} @url{download/nncp-1.0.tar.xz.sig, sign}
 @tab @code{68BF7803 CD25F59A 56D9FD6C 695002B5 BFBAF591 8A6583F4 3139FC28 CA1AB4AF}
 
-@item @ref{Release 0.12, 0.12} @tab 978 KiB
+@item @ref{Release 0.12, 0.12} @tab 2017-10-08 @tab 978 KiB
 @tab @url{download/nncp-0.12.tar.xz, link} @url{download/nncp-0.12.tar.xz.sig, sign}
 @tab @code{707B4005 97753B29 73A5F3E5 DAB51B92 21CC296D 690EF4BC ADE93E0D 2595A5F2}
 
-@item @ref{Release 0.11, 0.11} @tab 1031 KiB
+@item @ref{Release 0.11, 0.11} @tab 2017-08-21 @tab 1031 KiB
 @tab @url{download/nncp-0.11.tar.xz, link} @url{download/nncp-0.11.tar.xz.sig, sign}
 @tab @code{D0F73C3B ADBF6B8B 13641A61 4D34F65F 20AF4C84 90894331 BF1F1609 2D65E719}
 
-@item @ref{Release 0.10, 0.10} @tab 949 KiB
+@item @ref{Release 0.10, 0.10} @tab 2017-07-04 @tab 949 KiB
 @tab @url{download/nncp-0.10.tar.xz, link} @url{download/nncp-0.10.tar.xz.sig, sign}
 @tab @code{DCE7C762 2F9281EB 282F1A67 5CA6500E 854F2DEC D60F3264 07872B91 4F4E6FA0}
 
-@item @ref{Release 0.9, 0.9} @tab 942 KiB
+@item @ref{Release 0.9, 0.9} @tab 2017-05-17 @tab 942 KiB
 @tab @url{download/nncp-0.9.tar.xz, link} @url{download/nncp-0.9.tar.xz.sig, sign}
 @tab @code{8D0765A5 F9D81086 7E1F5AB4 52A9464D C5035CCB 4E09A29A 9C9A4934 1A72AB2C}
 
-@item @ref{Release 0.8, 0.8} @tab 932 KiB
+@item @ref{Release 0.8, 0.8} @tab 2017-04-30 @tab 932 KiB
 @tab @url{download/nncp-0.8.tar.xz, link} @url{download/nncp-0.8.tar.xz.sig, sign}
 @tab @code{9BD607D5 C5551857 B7E9277D 0E857936 1DB7353A E0F1556E EA9B1D91 8305B184}
 
-@item @ref{Release 0.7, 0.7} @tab 783 KiB
+@item @ref{Release 0.7, 0.7} @tab 2017-04-02 @tab 783 KiB
 @tab @url{download/nncp-0.7.tar.xz, link} @url{download/nncp-0.7.tar.xz.sig, sign}
 @tab @code{D3407323 F89296DD 743FA764 51964B43 794E61BE 0E1D2DD4 ABD02042 B94FFC4F}
 
-@item @ref{Release 0.6, 0.6} @tab 746 KiB
+@item @ref{Release 0.6, 0.6} @tab 2017-02-05 @tab 746 KiB
 @tab @url{download/nncp-0.6.tar.xz, link} @url{download/nncp-0.6.tar.xz.sig, sign}
 @tab @code{DCFEE3F9 F669AC28 563C50DB 67BB8B43 0CFF4AB6 EC770ACE B5378D0B B40C0656}
 
-@item @ref{Release 0.5, 0.5} @tab 743 KiB
+@item @ref{Release 0.5, 0.5} @tab 2017-01-19 @tab 743 KiB
 @tab @url{download/nncp-0.5.tar.xz, link} @url{download/nncp-0.5.tar.xz.sig, sign}
 @tab @code{D98F9149 5A6D6726 4C659640 1AD7F400 271A58CE 5D8D4AC5 5D1CF934 59BEDFA6}
 
-@item @ref{Release 0.4, 0.4} @tab 741 KiB
+@item @ref{Release 0.4, 0.4} @tab 2017-01-17 @tab 741 KiB
 @tab @url{download/nncp-0.4.tar.xz, link} @url{download/nncp-0.4.tar.xz.sig, sign}
 @tab @code{93577327 B3DEBFE3 A80BEB0D 8325B2E6 0939EC55 4DBB05F3 4CA34B99 229C3722}
 
-
-@item @ref{Release 0.3, 0.3} @tab 741 KiB
+@item @ref{Release 0.3, 0.3} @tab 2017-01-17 @tab 741 KiB
 @tab @url{download/nncp-0.3.tar.xz, link} @url{download/nncp-0.3.tar.xz.sig, sign}
 @tab @code{6E76EC5E 6B575C65 BF2D6388 870F2A1C 417D63E4 1628CAA1 BB499D0D 0634473B}
 
-@item @ref{Release 0.2, 0.2} @tab 740 KiB
+@item @ref{Release 0.2, 0.2} @tab 2017-01-17 @tab 740 KiB
 @tab @url{download/nncp-0.2.tar.xz, link} @url{download/nncp-0.2.tar.xz.sig, sign}
 @tab @code{00BEAC5A 0C4083B0 42E3152B ACA6FF20 12768B82 CE24D716 8E04279C ECE14DB7}
 
-@item 0.1 @tab 720 KiB
+@item 0.1 @tab 2017-01-10 @tab 720 KiB
 @tab @url{download/nncp-0.1.tar.xz, link} @url{download/nncp-0.1.tar.xz.sig, sign}
 @tab @code{8F71D65B 70865EBF FE802CDF A5C14D00 A9FD6559 FD722E60 5D97E82C 5E2412C2}
 
index 74e6661021ff879bbfa312684c6bde892f4d8d9f..45de886d3defe40b7a82905107f64a79293d02ca 100644 (file)
@@ -32,16 +32,16 @@ winner).
 Eblob is an @url{https://tools.ietf.org/html/rfc4506, XDR}-encoded structure:
 
 @verbatim
-+-------+------------------+------------+
-| MAGIC | S | T | P | SALT | BLOB | MAC |
-+-------+------------------+------------+
++-------+------------------+------+
+| MAGIC | S | T | P | SALT | BLOB |
++-------+------------------+------+
 @end verbatim
 
 @multitable @columnfractions 0.2 0.3 0.5
 @headitem @tab XDR type @tab Value
 @item Magic number @tab
     8-byte, fixed length opaque data @tab
-    @verb{|N N C P B 0x00 0x00 0x02|}
+    @verb{|N N C P B 0x00 0x00 0x03|}
 @item S, T, P @tab
     unsigned integer @tab
     Space cost, time cost and parallel jobs number
@@ -50,22 +50,19 @@ Eblob is an @url{https://tools.ietf.org/html/rfc4506, XDR}-encoded structure:
     Randomly generated salt
 @item Blob @tab
     variable length opaque data @tab
-    Encrypted data itself
-@item MAC @tab
-    32 bytes, fixed length opaque data @tab
-    BLAKE2b-256 MAC of encrypted blob
+    Authenticated and Encrypted data itself
 @end multitable
 
 @enumerate
 @item generate the main key using @code{balloon(BLAKE2b-256, S, T, P,
 salt, password)}
 @item initialize @url{https://blake2.net/, BLAKE2Xb} XOF with generated
-main key and 96-byte output length
-@item feed @verb{|N N C P B 0x00 0x00 0x02|} magic number to XOF
-@item read 32-bytes of blob encryption key
-@item read 64-bytes of blob authentication key
-@item encrypt the blob using @url{https://cr.yp.to/chacha.html,
-ChaCha20}. Blob is splitted on 128 KiB blocks. Each block is encrypted
-with increasing nonce counter
-@item authenticate ciphertext with MAC
+main key and 32-byte output length
+@item feed @verb{|N N C P B 0x00 0x00 0x03|} magic number to XOF
+@item read 32-bytes of blob AEAD encryption key
+@item encrypt and authenticate blob using
+    @url{https://cr.yp.to/chacha.html, ChaCha20}-@url{https://en.wikipedia.org/wiki/Poly1305, Poly1305}.
+    Blob is splitted on 128 KiB blocks. Each block is encrypted with
+    increasing nonce counter. Eblob packet itself, with empty blob
+    field, is fed as an additional authenticated data
 @end enumerate
index e2df9bd28d8f138d2739ba0077d579a5db15ff63..e9f067d64a2e07b418f833bc419c4349544b069f 100644 (file)
@@ -6,7 +6,7 @@
 This manual is for NNCP (Node to Node copy) --  collection of utilities
 simplifying secure store-and-forward files and mail exchanging.
 
-Copyright @copyright{} 2016-2018 @email{stargrave@@stargrave.org, Sergey Matveev}
+Copyright @copyright{} 2016-2019 @email{stargrave@@stargrave.org, Sergey Matveev}
 
 @quotation
 Permission is granted to copy, distribute and/or modify this document
index 67edc7240558ce8b92fe475e2af418ebd8f6fab5..c597a0f8c34d14a5d4e63cd9d82c20c53d240c9e 100644 (file)
@@ -23,7 +23,6 @@ uid   NNCP releases <releases at nncpgo dot org>
 % gpg --keyserver hkp://keys.gnupg.net/ --recv-keys 0x2B25868E75A1A953
 % gpg --auto-key-locate dane --locate-keys releases at nncpgo dot org
 % gpg --auto-key-locate wkd --locate-keys releases at nncpgo dot org
-% gpg --auto-key-locate pka --locate-keys releases at nncpgo dot org
 @end verbatim
 
 @item
index 46e635732d303246423e68198c843d445a918054..a17c2efa0a73405d7af8a896e43ef993b3d2a1de 100644 (file)
@@ -1,6 +1,34 @@
 @node Новости
 @section Новости
 
+@node Релиз 4.0
+@subsection Релиз 4.0
+@itemize
+@item
+@strong{Несовместимое} изменение формата зашифрованных и eblob пакетов:
+используется AEAD режим шифрования с 128 КиБ блоками, так как раньше
+@command{nncp-toss} не проверял MAC зашифрованного пакета прежде чем
+отсылать дешифрованные данные внешней команде. Старые версии не
+поддерживаются.
+@item
+Проверка доступного места перед копированием во время работы
+@command{nncp-xfer}, @command{nncp-daemon}, @command{nncp-call(er)}.
+@item
+@command{nncp-call} имеет возможность только показывать список пакетов
+на удалённой машине, без их передачи.
+@item
+@command{nncp-call} имеет возможность передавать только чётко указанные пакеты.
+@item
+Восстановлена работоспособность @option{xxrate} настройки в
+@option{calls} секции конфигурационного файла.
+@item
+Зависимые библиотеки обновлены.
+@item
+Небольшие исправления ошибок.
+@item
+Начало использования @code{go.mod} подсистемы.
+@end itemize
+
 @node Релиз 3.4
 @subsection Релиз 3.4
 @itemize
index 16f1d6f3e3cfe3ab53d05051f57b1fdde00efae9..ed1bf0839333802891b7cff8d32ddae98c6dd562 100644 (file)
@@ -3,6 +3,33 @@
 
 See also this page @ref{Новости, on russian}.
 
+@node Release 4.0
+@section Release 4.0
+@itemize
+@item
+@strong{Incompatible} encrypted and eblob packet format change: AEAD
+encryption mode with 128 KiB blocks is used now, because previously
+@command{nncp-toss} did not verify encrypted packet's MAC before feeding
+decrypted data to external command. Older versions are not supported.
+@item
+Available free space checking before copying in @command{nncp-xfer},
+@command{nncp-daemon}, @command{nncp-call(er)}.
+@item
+@command{nncp-call} has ability only to list packets on remote node,
+without their transmission.
+@item
+@command{nncp-call} has ability to transfer only specified packets.
+@item
+Workability of @option{xxrate} preference in @option{calls}
+configuration file section.
+@item
+Dependant libraries are updated.
+@item
+Minor bugfixes.
+@item
+Begin using of @code{go.mod} subsystem.
+@end itemize
+
 @node Release 3.4
 @section Release 3.4
 @itemize
index b1c2c38b37cb5d7ad0f2254645d6e8120b94b1e8..d216f93e96d9d05e614c30f9f39a343b833ab552 100644 (file)
@@ -28,7 +28,7 @@ drive.
 @headitem @tab XDR type @tab Value
 @item Magic number @tab
     8-byte, fixed length opaque data @tab
-    @verb{|N N C P P 0x00 0x00 0x01|}
+    @verb{|N N C P P 0x00 0x00 0x02|}
 @item Payload type @tab
     unsigned integer @tab
     0 (file), 1 (freq), 2 (exec), 3 (transition)
@@ -78,11 +78,11 @@ storages and that are synchronized between TCP daemons.
 Each encrypted packet has the following header:
 
 @verbatim
-  +------------ HEADER --------------------+   +-------- ENCRYPTED --------+
- /                                          \ /                             \
-+--------------------------------------------+------------+----...-----------+------+
-| MAGIC | NICE | SENDER | RCPT | EPUB | SIGN | SIZE | MAC | CIPHERTEXT | MAC | JUNK |
-+-------------------------------------/------\------------+----...-----------+------+
+  +------------ HEADER --------------------+   +------------- ENCRYPTED -------------+
+ /                                          \ /                                       \
++--------------------------------------------+------+---------+----------...---+------+
+| MAGIC | NICE | SENDER | RCPT | EPUB | SIGN | SIZE | BLOCK 0 | BLOCK 1  ...   | JUNK |
++-------------------------------------/------\------+---------+----------...---+------+
                                      /        \
                       +-------------------------------------+
                       | MAGIC | NICE | SENDER | RCPT | EPUB |
@@ -93,7 +93,7 @@ Each encrypted packet has the following header:
 @headitem @tab XDR type @tab Value
 @item Magic number @tab
     8-byte, fixed length opaque data @tab
-    @verb{|N N C P E 0x00 0x00 0x03|}
+    @verb{|N N C P E 0x00 0x00 0x04|}
 @item Niceness @tab
     unsigned integer @tab
     1-255, packet @ref{Niceness, niceness} level
@@ -113,12 +113,12 @@ Each encrypted packet has the following header:
 
 Signature is calculated over all previous fields.
 
-All following encryption is done using @url{https://cr.yp.to/chacha.html,
-ChaCha20} algorithm. Data is splitted on 128 KiB blocks. Each block is
-encrypted with increasing nonce counter. @url{https://blake2.net/,
-BLAKE2b-256} MAC is appended to the ciphertext.
+All following encryption is done in AEAD mode using
+@url{https://cr.yp.to/chacha.html, ChaCha20}-@url{https://en.wikipedia.org/wiki/Poly1305, Poly1305}
+algorithms. Data is splitted on 128 KiB blocks. Each block is encrypted with
+increasing nonce counter.
 
-After the headers comes an encrypted payload size and MAC of that size.
+Authenticated and encrypted size come after the header:
 
 @multitable @columnfractions 0.2 0.3 0.5
 @headitem @tab XDR type @tab Value
@@ -127,7 +127,7 @@ After the headers comes an encrypted payload size and MAC of that size.
     Payload size.
 @end multitable
 
-Next comes the actual encrypted payload with corresponding MAC.
+Then comes the actual payload.
 
 Each node has static @strong{exchange} and @strong{signature} keypairs.
 When node A want to send encrypted packet to node B, it:
@@ -143,18 +143,15 @@ When node A want to send encrypted packet to node B, it:
 @item derive the keys:
     @enumerate
     @item initialize @url{https://blake2.net/, BLAKE2Xb} XOF with
-    derived ephemeral key and 224-byte output length
-    @item feed @verb{|N N C P E 0x00 0x00 0x03|} magic number to XOF
-    @item read 32-bytes of "size" encryption key (for ChaCha20)
-    @item read 64-bytes of "size" authentication key (for BLAKE2b-MAC)
-    @item read 32-bytes of payload encryption key
-    @item read 64-bytes of payload authentication key
-    @item optionally read 32-bytes pad generation key (for ChaCha20)
+    derived ephemeral key and 96-byte output length
+    @item feed @verb{|N N C P E 0x00 0x00 0x04|} magic number to XOF
+    @item read 32-bytes of "size" AEAD encryption key
+    @item read 32-bytes of payload AEAD encryption key
+    @item optionally read 32-bytes pad generation key
     @end enumerate
-@item encrypts size, appends its ciphertext to the header
-@item appends MAC tag over that ciphertext
-@item encrypts and appends payload ciphertext
-@item appends MAC tag over that payload ciphertext
+@item encrypts size, appends its authenticated ciphertext to the header
+@item encrypts payload, appends its authenticated ciphertext
 @item possibly appends any kind of "junk" noise data to hide real
-    payload's size from the adversary
+    payload's size from the adversary (generated using XOF with
+    unlimited output length)
 @end enumerate
index dc94af994247750d9bd208a37707c59ec75fda20..0dec0f2156face811f2b44954c850c6327a7fce9 100644 (file)
@@ -1,10 +1,11 @@
 <style type="text/css"><!--
 body {
     margin: auto;
-    max-width: 800px;
+    width: 80em;
     background-color: #AEBECE;
 }
 h1, h2, h3, h4 { text-align: center }
 h1, h2, h3, h4, strong { color: #900090 }
 pre { background-color: #CCCCCC }
+table, th, td { border: 1px solid black }
 --></style>
index 3e3caba2dbdd889cb32f761c52ebb3db131401c2..da05881bcab828d0e636e16d18fab1df05a2f8ea 100644 (file)
@@ -8,10 +8,12 @@
 * Медленная/дорогая связь для больших объёмов данных, плохой QoS: UsecaseQoSRU.
 * Экстремальные наземные окружающие условия, нет связи: UsecaseNoLinkRU.
 * Односторонняя широковещательная связь: UsecaseBroadcastRU.
+* Спутниковые каналы связи: UsecaseSatelliteLinksRU.
 * Частные, изолированные MitM/Sybil-устойчивые сети: UsecaseF2FRU.
 * Высоко защищённые изолированные компьютеры с воздушным зазором: UsecaseAirgapRU.
 * Обход сетевой цензуры, здоровье: UsecaseCensorRU.
 * Разведка, шпионаж, тайная агентура: UsecaseSpyRU.
+* Дешёвая ночная связь: UsecaseCallerRU.
 @end menu
 
 @node UsecaseMailRU
@@ -104,9 +106,9 @@ NNCP поддерживает @ref{Niceness, приоритезацию траф
 опцию:
 
 @verbatim
-% nncp-file -nice 32 myfile node:dst
-% nncp-xfer -nice 192 /mnt/shared
-% nncp-call -nice 224 bob
+% nncp-file -nice FLASH myfile node:dst
+% nncp-xfer -nice PRIORITY /mnt/shared
+% nncp-call -nice NORMAL bob
 [...]
 @end verbatim
 
@@ -182,6 +184,34 @@ NNCP поддерживает @ref{Niceness, приоритезацию траф
 переотправлять широковещательные рассылки время от времени, повышая
 шансы на то, что получатель примет их, регулярно слушая рассылку.
 
+@node UsecaseSatelliteLinksRU
+@subsection Спутниковые каналы связи
+
+Спутниковые каналы связи имеют @strong{очень} большие задержки вместе с
+высокими пропускными способностями. Вы можете посылать мегабиты данных в
+секунду, но они достигнут удалённой стороны только спустя полсекунды!
+Большинство протоколов обмена файлами, таких как
+@url{https://en.wikipedia.org/wiki/Files_transferred_over_shell_protocol, FISH},
+@url{https://ru.wikipedia.org/wiki/FTP, FTP},
+@url{https://ru.wikipedia.org/wiki/SCP, scp},
+@url{https://en.wikipedia.org/wiki/XMODEM, XMODEM} will perform very
+будут работать очень плохо из-за большого количества приёмо-передач
+(round-trips). Каждая передача файла явно генерирует пакеты запросов и
+подтверждений, посылаемые поверх канала связи. Удалённая сторона ничего
+не будет делать пока она их не получит. Более того, не все протоколы
+позволяют делать дуплексную отправку данных (когда обе стороны посылают
+данные одновременно).
+
+@ref{Sync, Протокол синхронизации} (SP) NNCP пытается решить все эти
+особенности за счёт сокращения количества приёмо-передач, количества
+проходящих пакетов. Все списки файлов, запросов на скачивание файла
+группируются вместе (pipelined) в один огромный пакет. Только запросы на
+остановку передачи и подтверждения успешного приёма файла явно
+посылаются. Можно запросить чтобы SP только принимал или отправлял
+пакеты для нашей ноды. SP может игнорировать файлы с маленьким
+приоритетом. Полные списки файлов отправляются уже на этапе процедуры
+рукопожатия.
+
 @node UsecaseF2FRU
 @subsection Частные, изолированные MitM/Sybil-устойчивые сети
 
@@ -297,7 +327,7 @@ neigh:
 
 Эти ребята знают насколько небезопасен Интернет, несовместим с
 понятием приватности. Им необходим быстрый сброс и забор данных. Нет
-возможности провести несколько итераций туда-обратно (round trip) --
+возможности провести несколько итераций приёмо-передач (round-trips) --
 только сбросить данные, выстрелить и забыть. Опять же, это может быть
 переносной накопитель и/или
 @url{https://en.wikipedia.org/wiki/USB_dead_drop, USB тайник} (dead drop),
@@ -316,7 +346,7 @@ Bluetooth и WiFi могут быть и довольно быстрыми, по
 
 Общение узлов между собой происходит в, так называемой, @ref{Spool,
 спул} области: директории содержащей только необработанные зашифрованные
-пакеты. После передачи пакета вы всё-равно не сможете его прочитать:
+пакеты. После передачи пакета вы всё равно не сможете его прочитать:
 необходимо запустить другую фазу: @ref{nncp-toss, распаковку}, которая
 использует ваши приватные криптографические ключи. То есть, даже если вы
 потеряете свой компьютер, устройства хранения и тому прочее -- это не
@@ -326,3 +356,47 @@ Bluetooth и WiFi могут быть и довольно быстрыми, по
 файлов и почтовых сообщений) может и должна бы быть произведена на
 отдельном компьютере (@ref{nncp-cfgmin} команда может помочь с созданием
 конфигурационного файла без приватных ключей для этой цели).
+
+Если вы действительно хотите взять с собой приватные ключи, то
+@ref{nncp-cfgenc} команда способна зашифровать ваш конфигурационный
+файл. Парольная фраза вами введённая усиливается функцией нагружающей и
+центральный процессор и память.
+
+@node UsecaseCallerRU
+@subsection Дешёвая ночная связь
+
+Стоимость Интернет/телефонного трафика может варьироваться, в
+зависимости от времени дня. Ночные звонки/соединения могут быть дешевле
+в два раза. Вы хотите посылать ваши файлы в это время, но позволять
+изредка проходить высокоприоритетной почте в любое время. А также вы
+хотите проходить любому трафику когда узел доступен через ЛВС (LAN).
+
+Вы легко можете настроить ваши предпочтения в @ref{Call, настройках
+звонков} для @ref{nncp-caller} команды, используемой при online связи.
+
+@verbatim
+neigh:
+  [...]
+  some-node:
+    [...]
+    addrs:
+      lan: "[fe80::be5f:f4ff:fedd:2752%igb0]:5400"
+      wan: "some-node.com:5400"
+    calls:
+      -
+        cron: "*/1 * * * *"
+        addr: lan
+        nice: MAX
+        onlinedeadline: 3600
+      -
+        cron: "*/10 * * * *"
+        addr: wan
+        nice: PRIORITY
+        xx: rx
+      -
+        cron: "*/1 0-7 * * *"
+        addr: wan
+        nice: BULK
+        onlinedeadline: 3600
+        maxonlinetime: 3600
+@end verbatim
index c2764350451617e2f310231653adfc99d38b83cb..1c0a750bfe15e5254bed8e6c30bfaa72ec6410c7 100644 (file)
@@ -10,10 +10,12 @@ See also this page @ref{Сценарии, on russian}.
 * Slow/expensive link for high-volume data, bad QoS: UsecaseQoS.
 * Extreme terrestrial environments, no link: UsecaseNoLink.
 * One-way broadcasting communications: UsecaseBroadcast.
+* Satellite links: UsecaseSatelliteLinks.
 * Private, isolated MitM/Sybil-resistant networks: UsecaseF2F.
 * Highly secure isolated air-gap computers: UsecaseAirgap.
 * Network censorship bypassing, health: UsecaseCensor.
 * Reconnaissance, spying, intelligence, covert agents: UsecaseSpy.
+* Cheap night transfers: UsecaseCaller.
 @end menu
 
 @node UsecaseMail
@@ -99,9 +101,9 @@ niceness level, that will guarantee that it will be processed earlier or
 later than the other ones. Nearly all commands has corresponding option:
 
 @verbatim
-% nncp-file -nice 32 myfile node:dst
-% nncp-xfer -nice 192 /mnt/shared
-% nncp-call -nice 224 bob
+% nncp-file -nice FLASH myfile node:dst
+% nncp-xfer -nice PRIORITY /mnt/shared
+% nncp-call -nice NORMAL bob
 [...]
 @end verbatim
 
@@ -172,6 +174,32 @@ With built-in packet duplicates detection ability, you can retransmit
 your broadcasts from time to time, to increase chances the recipient
 will catch them by regular stream listening.
 
+@node UsecaseSatelliteLinks
+@section Satellite links
+
+Satellite links have @strong{very} high delays together with high
+bandwidths. You can send several megabits of data per second, but they
+will reach the remote side only after half a second!
+Most file sharing protocols like
+@url{https://en.wikipedia.org/wiki/Files_transferred_over_shell_protocol, FISH},
+@url{https://en.wikipedia.org/wiki/FTP, FTP},
+@url{https://en.wikipedia.org/wiki/Secure_copy, scp},
+@url{https://en.wikipedia.org/wiki/XMODEM, XMODEM}
+will perform very badly because of round-trips quantity. Each file
+transmission explicitly generates request and acknowledgement packets
+that are send over the link. Remote side won't do anything until it
+receives them. Moreover not all protocols allow duplex data
+transmission (when both sides are sending data simultaneously).
+
+NNCP's @ref{Sync, synchronization protocol} (SP) tries to mitigate all
+that issues by reducing number of round-trips, number of packets passing
+through. All file lists, file download requests are grouped together
+(pipelined) in one huge packet. Only transmission halt and successful
+file download acknowledgements are sent explicitly. SP could be asked
+only either to upload or download packets for our node. SP could ignore
+files with low priority. Full files listing is passing even during the
+handshake procedure.
+
 @node UsecaseF2F
 @section Private, isolated MitM/Sybil-resistant networks
 
@@ -305,3 +333,47 @@ same device. Tossing (reading those encrypted packets and extracting
 transferred files and mail messages) could and should be done on a
 separate computer (@ref{nncp-cfgmin} command could help creating
 configuration file without private keys for that purpose).
+
+If you really want to carry your private keys, then @ref{nncp-cfgenc}
+command will be able to encrypt your configuration file. Passphrase you
+enter is strengthened with both CPU and memory hard function.
+
+@node UsecaseCaller
+@section Cheap night transfers
+
+Your Internet/telephone traffic price can vary, depending on daytime.
+Night calls/connections could be twice as cheaper. You wish to send your
+files at that time, but keep high priority email infrequently passing
+through in anytime. Also you wish to pass any kind of traffic when the
+node is available through the LAN.
+
+You can easily set your preferences in @ref{Call, call
+configurations} for @ref{nncp-caller} command used in online
+communications.
+
+@verbatim
+neigh:
+  [...]
+  some-node:
+    [...]
+    addrs:
+      lan: "[fe80::be5f:f4ff:fedd:2752%igb0]:5400"
+      wan: "some-node.com:5400"
+    calls:
+      -
+        cron: "*/1 * * * *"
+        addr: lan
+        nice: MAX
+        onlinedeadline: 3600
+      -
+        cron: "*/10 * * * *"
+        addr: wan
+        nice: PRIORITY
+        xx: rx
+      -
+        cron: "*/1 0-7 * * *"
+        addr: wan
+        nice: BULK
+        onlinedeadline: 3600
+        maxonlinetime: 3600
+@end verbatim
index 8e487d815fb3bfce278c94aa259aa8cc26bc5e68..7051f078da630d541ff39be1c02f0dfefc81122d 100755 (executable)
@@ -5,19 +5,23 @@ tmp=$(mktemp -d)
 release=$1
 [ -n "$release" ]
 
+vendor=src/cypherpunks.ru/nncp/vendor
+
 git clone . $tmp/nncp-$release
 repos="
-    src/github.com/davecgh/go-xdr
-    src/github.com/dustin/go-humanize
-    src/github.com/flynn/noise
-    src/golang.org/x/crypto
-    src/golang.org/x/net
-    src/golang.org/x/sys
-    src/gopkg.in/check.v1
-    src/gopkg.in/yaml.v2
+    cypherpunks.ru/balloon
+    github.com/davecgh/go-xdr
+    github.com/dustin/go-humanize
+    github.com/flynn/noise
+    github.com/gorhill/cronexpr
+    golang.org/x/crypto
+    golang.org/x/net
+    golang.org/x/sys
+    gopkg.in/check.v1
+    gopkg.in/yaml.v2
 "
 for repo in $repos; do
-    git clone $repo $tmp/nncp-$release/$repo
+    git clone $vendor/$repo $tmp/nncp-$release/$vendor/$repo
 done
 cd $tmp/nncp-$release
 git checkout $release
@@ -25,43 +29,48 @@ git submodule update --init
 
 cat > $tmp/includes <<EOF
 golang.org/x/crypto/AUTHORS
-golang.org/x/crypto/CONTRIBUTORS
-golang.org/x/crypto/LICENSE
-golang.org/x/crypto/PATENTS
-golang.org/x/crypto/README.md
 golang.org/x/crypto/blake2b
 golang.org/x/crypto/blake2s
 golang.org/x/crypto/chacha20poly1305
+golang.org/x/crypto/CONTRIBUTORS
 golang.org/x/crypto/curve25519
 golang.org/x/crypto/ed25519
+golang.org/x/crypto/go.mod
+golang.org/x/crypto/go.sum
 golang.org/x/crypto/internal/chacha20
 golang.org/x/crypto/internal/subtle
+golang.org/x/crypto/LICENSE
 golang.org/x/crypto/nacl
+golang.org/x/crypto/PATENTS
 golang.org/x/crypto/poly1305
+golang.org/x/crypto/README.md
 golang.org/x/crypto/salsa20
 golang.org/x/crypto/ssh/terminal
 golang.org/x/net/AUTHORS
 golang.org/x/net/CONTRIBUTORS
+golang.org/x/net/go.mod
+golang.org/x/net/go.sum
 golang.org/x/net/LICENSE
+golang.org/x/net/netutil
 golang.org/x/net/PATENTS
 golang.org/x/net/README.md
-golang.org/x/net/netutil
 golang.org/x/sys/AUTHORS
 golang.org/x/sys/CONTRIBUTORS
+golang.org/x/sys/cpu
+golang.org/x/sys/go.mod
 golang.org/x/sys/LICENSE
 golang.org/x/sys/PATENTS
 golang.org/x/sys/README.md
-golang.org/x/sys/cpu
 golang.org/x/sys/unix
 EOF
-tar cfCI - src $tmp/includes | tar xfC - $tmp
-rm -fr src/golang.org
-mv $tmp/golang.org src/
-rm -fr $tmp/golang.org $tmp/includes
+tar cfCI - $vendor $tmp/includes | tar xfC - $tmp
+rm -fr $vendor/golang.org
+mv $tmp/golang.org $vendor
+rm $tmp/includes
 
 find src -name .travis.yml -delete
-rm -fr src/github.com/davecgh/go-xdr/xdr
-rm -fr src/github.com/gorhill/cronexpr/cronexpr src/github.com/gorhill/cronexpr/APLv2
+rm -fr $vendor/github.com/davecgh/go-xdr/xdr
+rm $vendor/github.com/gorhill/cronexpr/APLv2
 rm -fr ports
 rm makedist.sh
 
@@ -80,17 +89,19 @@ find . -name .gitignore -delete
 rm .gitmodules
 
 cd ..
-tar cvf nncp-"$release".tar nncp-"$release"
-xz -9 nncp-"$release".tar
-gpg --detach-sign --sign --local-user 0x2B25868E75A1A953 nncp-"$release".tar.xz
-mv $tmp/nncp-"$release".tar.xz $tmp/nncp-"$release".tar.xz.sig $cur/doc/nncp.html/download
+tar cvf nncp-"$release".tar --uid=0 --gid=0 --numeric-owner nncp-"$release"
+xz -9v nncp-"$release".tar
+gpg --detach-sign --sign --local-user releases@nncpgo.org nncp-"$release".tar.xz
+mv -v $tmp/nncp-"$release".tar.xz $tmp/nncp-"$release".tar.xz.sig $cur/doc/nncp.html/download
 
 tarball=$cur/doc/nncp.html/download/nncp-"$release".tar.xz
-size=$(( $(cat $tarball | wc -c) / 1024 ))
+size=$(( $(stat -f %z $tarball) / 1024 ))
 hash=$(gpg --print-md SHA256 < $tarball)
+release_date=$(date "+%Y-%m-%d")
+
 cat <<EOF
 An entry for documentation:
-@item @ref{Release $release, $release} @tab $size KiB
+@item @ref{Release $release, $release} @tab $release_date @tab $size KiB
 @tab @url{download/nncp-${release}.tar.xz, link} @url{download/nncp-${release}.tar.xz.sig, sign}
 @tab @code{$hash}
 EOF
index 3a494907d793f1fc3605500e8924bb63bf639256..77a7f974dffac3e5b750d61d827ccf997946d80a 100644 (file)
@@ -1,7 +1,7 @@
 # $FreeBSD: head/net/nncp/Makefile 471003 2018-05-27 20:24:00Z krion $
 
 PORTNAME=      nncp
-DISTVERSION=   3.3
+DISTVERSION=   3.4
 CATEGORIES=    net
 MASTER_SITES=  http://www.nncpgo.org/download/
 
index ce972a9da8cb0b31df2397fed5d95ce9316f4230..1e1418f127039b0cebfc2b7e83d424c460a5620d 100644 (file)
@@ -1,5 +1,5 @@
 NNCP (Node to Node copy) is a collection of utilities simplifying
-secure store-and-forward files and mail exchanging.
+secure store-and-forward files, mail and commands exchanging.
 
 This utilities are intended to help build up small size (dozens of
 nodes) ad-hoc friend-to-friend (F2F) statically routed darknet
diff --git a/src/chacha20 b/src/chacha20
deleted file mode 120000 (symlink)
index 883eae7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-golang.org/x/crypto/internal/chacha20
\ No newline at end of file
diff --git a/src/cypherpunks.ru/balloon b/src/cypherpunks.ru/balloon
deleted file mode 160000 (submodule)
index 92c1617..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 92c161713a13cc78403468c09f1557eb7c4a3a4a
index 6a2c38b740870f249c05162732b3b059c0adba20..56c51bfcd181ad5f13e2e66ae7209943e42bb0b8 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -42,7 +42,10 @@ func (ctx *Ctx) CallNode(
        nice uint8,
        xxOnly TRxTx,
        rxRate, txRate int,
-       onlineDeadline, maxOnlineTime uint) (isGood bool) {
+       onlineDeadline, maxOnlineTime uint,
+       listOnly bool,
+       onlyPkts map[[32]byte]bool,
+) (isGood bool) {
        for _, addr := range addrs {
                sds := SDS{"node": node.Id, "addr": addr}
                ctx.LogD("call", sds, "dialing")
@@ -52,17 +55,19 @@ func (ctx *Ctx) CallNode(
                        continue
                }
                ctx.LogD("call", sds, "connected")
-               state, err := ctx.StartI(
-                       conn,
-                       node.Id,
-                       nice,
-                       xxOnly,
-                       rxRate,
-                       txRate,
-                       onlineDeadline,
-                       maxOnlineTime,
-               )
-               if err == nil {
+               state := SPState{
+                       Ctx:            ctx,
+                       Node:           node,
+                       Nice:           nice,
+                       onlineDeadline: onlineDeadline,
+                       maxOnlineTime:  maxOnlineTime,
+                       xxOnly:         xxOnly,
+                       rxRate:         rxRate,
+                       txRate:         txRate,
+                       listOnly:       listOnly,
+                       onlyPkts:       onlyPkts,
+               }
+               if err = state.StartI(conn); err == nil {
                        ctx.LogI("call-start", sds, "connected")
                        state.Wait()
                        ctx.LogI("call-finish", SDS{
index 75d12a54fb33d63837d4037b37bdb7d56e914cfe..bcd547ec91c5e13cbc6052d69c6164e35e9c3f6f 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -214,12 +214,12 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                        return nil, errors.New("xx field must be either \"rx\" or \"tx\"")
                }
 
-               rxRate := 0
-               if callYml.RxRate != nil && *callYml.RxRate > 0 {
+               rxRate := defRxRate
+               if callYml.RxRate != nil {
                        rxRate = *callYml.RxRate
                }
-               txRate := 0
-               if callYml.TxRate != nil && *callYml.TxRate > 0 {
+               txRate := defTxRate
+               if callYml.TxRate != nil {
                        txRate = *callYml.TxRate
                }
 
@@ -371,7 +371,7 @@ func (nodeOur *NodeOur) ToYAML() string {
 
 func CfgParse(data []byte) (*Ctx, error) {
        var err error
-       if bytes.Compare(data[:8], MagicNNCPBv2[:]) == 0 {
+       if bytes.Compare(data[:8], MagicNNCPBv3[:]) == 0 {
                os.Stderr.WriteString("Passphrase:")
                password, err := terminal.ReadPassword(0)
                if err != nil {
index 53dd549a0faaf2d47607957f7a5f69f1fd28d2d9..c83c80b1b804ac0b27e47b4a5c70cc4e621da6c1 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
index 417e2a0e897629047a37401c0df66b1fe854847e..02b488b3a92d448788c13983871c7039df8af491 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
index 86dc099843bb578080eb0e2e8be471aef033caa7..553a6d11a15afa325a0d69ba09c9933b57e968c1 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Create/digest stream of NNCP encrypted packets
+// Create/digest stream of NNCP encrypted packets.
 package main
 
 import (
@@ -236,7 +236,7 @@ func main() {
                                ctx.LogD("nncp-bundle", sds, "Bad packet structure")
                                continue
                        }
-                       if pktEnc.Magic != nncp.MagicNNCPEv3 {
+                       if pktEnc.Magic != nncp.MagicNNCPEv4 {
                                ctx.LogD("nncp-bundle", sds, "Bad packet magic number")
                                continue
                        }
@@ -367,7 +367,9 @@ func main() {
                                        if err = bufTmp.Flush(); err != nil {
                                                log.Fatalln("Error during flushing:", err)
                                        }
-                                       tmp.Sync()
+                                       if err = tmp.Sync(); err != nil {
+                                               log.Fatalln("Error during syncing:", err)
+                                       }
                                        tmp.Close()
                                        if err = os.MkdirAll(selfPath, os.FileMode(0700)); err != nil {
                                                log.Fatalln("Error during mkdir:", err)
index d8adc60001c046c2fb26c9b2a9d2c57497fe78d2..7919b6a55386c836c5fd4644b15bb29cead89e04 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Call NNCP TCP daemon
+// Call NNCP TCP daemon.
 package main
 
 import (
@@ -39,18 +39,20 @@ func usage() {
 
 func main() {
        var (
-               cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw   = flag.String("nice", nncp.NicenessFmt(255), "Minimal required niceness")
-               rxOnly    = flag.Bool("rx", false, "Only receive packets")
-               txOnly    = flag.Bool("tx", false, "Only transmit packets")
-               rxRate    = flag.Int("rxrate", 0, "Maximal receive rate, pkts/sec")
-               txRate    = flag.Int("txrate", 0, "Maximal transmit rate, pkts/sec")
-               spoolPath = flag.String("spool", "", "Override path to spool")
-               logPath   = flag.String("log", "", "Override path to logfile")
-               quiet     = flag.Bool("quiet", false, "Print only errors")
-               debug     = flag.Bool("debug", false, "Print debug messages")
-               version   = flag.Bool("version", false, "Print version information")
-               warranty  = flag.Bool("warranty", false, "Print warranty information")
+               cfgPath     = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
+               niceRaw     = flag.String("nice", nncp.NicenessFmt(255), "Minimal required niceness")
+               rxOnly      = flag.Bool("rx", false, "Only receive packets")
+               txOnly      = flag.Bool("tx", false, "Only transmit packets")
+               listOnly    = flag.Bool("list", false, "Only list remote packets")
+               onlyPktsRaw = flag.String("pkts", "", "Recieve only that packets, comma separated")
+               rxRate      = flag.Int("rxrate", 0, "Maximal receive rate, pkts/sec")
+               txRate      = flag.Int("txrate", 0, "Maximal transmit rate, pkts/sec")
+               spoolPath   = flag.String("spool", "", "Override path to spool")
+               logPath     = flag.String("log", "", "Override path to logfile")
+               quiet       = flag.Bool("quiet", false, "Print only errors")
+               debug       = flag.Bool("debug", false, "Print debug messages")
+               version     = flag.Bool("version", false, "Print version information")
+               warranty    = flag.Bool("warranty", false, "Print warranty information")
 
                onlineDeadline = flag.Uint("onlinedeadline", 0, "Override onlinedeadline option")
                maxOnlineTime  = flag.Uint("maxonlinetime", 0, "Override maxonlinetime option")
@@ -123,6 +125,21 @@ func main() {
                }
        }
 
+       var onlyPkts map[[32]byte]bool
+       if len(*onlyPktsRaw) > 0 {
+               splitted = strings.Split(*onlyPktsRaw, ",")
+               onlyPkts = make(map[[32]byte]bool, len(splitted))
+               for _, pktIdRaw := range splitted {
+                       pktId, err := nncp.FromBase32(pktIdRaw)
+                       if err != nil {
+                               log.Fatalln("Invalid packet specified: ", err)
+                       }
+                       pktIdArr := new([32]byte)
+                       copy(pktIdArr[:], pktId)
+                       onlyPkts[*pktIdArr] = true
+               }
+       }
+
        if !ctx.CallNode(
                node,
                addrs,
@@ -132,6 +149,8 @@ func main() {
                *txRate,
                *onlineDeadline,
                *maxOnlineTime,
+               *listOnly,
+               onlyPkts,
        ) {
                os.Exit(1)
        }
index d9609420b2bb46a330b9c4e90016aa0ba88760e2..19307153a7e57c3f02eebf6f584f338a0ff31e4d 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Croned NNCP TCP daemon caller
+// Croned NNCP TCP daemon caller.
 package main
 
 import (
@@ -132,6 +132,8 @@ func main() {
                                                        call.TxRate,
                                                        call.OnlineDeadline,
                                                        call.MaxOnlineTime,
+                                                       false,
+                                                       nil,
                                                )
                                                node.Lock()
                                                node.Busy = false
index 3ad1d69784d44da48d21dbe74bf982e197395c9a..be009f4e81e51ed6da38db857fe642a7e61937ac 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -79,7 +79,7 @@ func main() {
                if _, err := xdr.Unmarshal(bytes.NewReader(data), &eblob); err != nil {
                        log.Fatalln(err)
                }
-               if eblob.Magic != nncp.MagicNNCPBv2 {
+               if eblob.Magic != nncp.MagicNNCPBv3 {
                        log.Fatalln(errors.New("Unknown eblob type"))
                }
                fmt.Println("Strengthening function: Balloon with BLAKE2b-256")
index cf1626e8498de9549b81fb053ad86fde323a20c7..7ddbb0462339d5580359f432d39349153f3af36f 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
index a119e3830989d73de494f3d9741c53b8e3f28cfc..274cdad06377e0f672569ecdaaabcf602cfce847 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
index f426fd455d65374a4707b753ab293220612af9fa..a99bd2f0c20ee62c10d2029147fbe045919431c1 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Verify NNCP Rx/Tx packets checksum
+// Verify NNCP Rx/Tx packets checksum.
 package main
 
 import (
@@ -75,7 +75,9 @@ func main() {
                if nodeOnly != nil && nodeId != *nodeOnly.Id {
                        continue
                }
-               isBad = isBad || ctx.Check(node.Id)
+               if !ctx.Check(node.Id) {
+                       isBad = true
+               }
        }
        if isBad {
                os.Exit(1)
index 770556815e21790c7b827d6fd04b2494051f5991..3f2897206eb745600025f80e3f98205bc1746def 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// NNCP TCP daemon
+// NNCP TCP daemon.
 package main
 
 import (
@@ -61,8 +61,11 @@ func (ic *InetdConn) SetWriteDeadline(t time.Time) error {
 }
 
 func performSP(ctx *nncp.Ctx, conn nncp.ConnDeadlined, nice uint8) {
-       state, err := ctx.StartR(conn, nice, "")
-       if err == nil {
+       state := nncp.SPState{
+               Ctx:  ctx,
+               Nice: nice,
+       }
+       if err := state.StartR(conn); err == nil {
                ctx.LogI("call-start", nncp.SDS{"node": state.Node.Id}, "connected")
                state.Wait()
                ctx.LogI("call-finish", nncp.SDS{
@@ -75,7 +78,7 @@ func performSP(ctx *nncp.Ctx, conn nncp.ConnDeadlined, nice uint8) {
                }, "")
        } else {
                nodeId := "unknown"
-               if state != nil && state.Node != nil {
+               if state.Node != nil {
                        nodeId = state.Node.Id.String()
                }
                ctx.LogE("call-start", nncp.SDS{"node": nodeId, "err": err}, "")
index 0cf781063ccc3e22b9e422d847528c53a561220c..bdcf5a3cb4893a7dac7c82333502b146439ae11b 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Send execution command via NNCP
+// Send execution command via NNCP.
 package main
 
 import (
index 6e7eaf5ab8fabbb2a888e2647350b2ac21b54780..b7f31ab870c0c972dadba8ef0c8ceaed510465a1 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Send file via NNCP
+// Send file via NNCP.
 package main
 
 import (
index 14f2fea68ed0970d8dd0ca7a20d537d4ec2e5bb3..32b62bfb9b2e7c94472a68505f44ecbb0b9018a1 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Send file request via NNCP
+// Send file request via NNCP.
 package main
 
 import (
index c578ec9a773a675817b442eb8dbbd9807b104333..aea1d8b63e346aa96ea251faa73d2ce23bf797f3 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Read NNCP logs
+// Read NNCP logs.
 package main
 
 import (
index ff77eb45195ac188be4a0872aeaa5ddb3768ca47..1ccea9e7def2aa244f6f10612c54832d1b321ed4 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Parse raw NNCP packet
+// Parse raw NNCP packet.
 package main
 
 import (
@@ -31,7 +31,6 @@ import (
 
        "cypherpunks.ru/nncp"
        "github.com/davecgh/go-xdr/xdr2"
-       "golang.org/x/crypto/blake2b"
 )
 
 func usage() {
@@ -44,6 +43,7 @@ func usage() {
 
 func main() {
        var (
+               overheads  = flag.Bool("overheads", false, "Print packet overheads")
                dump       = flag.Bool("dump", false, "Write decrypted/parsed payload to stdout")
                decompress = flag.Bool("decompress", false, "Try to zlib decompress dumped data")
                cfgPath    = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
@@ -61,8 +61,18 @@ func main() {
                return
        }
 
+       if *overheads {
+               fmt.Printf(
+                       "Plain: %d\nEncrypted: %d\nSize: %d\n",
+                       nncp.PktOverhead,
+                       nncp.PktEncOverhead,
+                       nncp.PktSizeOverhead,
+               )
+               return
+       }
+
        var err error
-       beginning := make([]byte, nncp.PktOverhead-8-2*blake2b.Size256)
+       beginning := make([]byte, nncp.PktOverhead)
        if _, err = io.ReadFull(os.Stdin, beginning); err != nil {
                log.Fatalln("Not enough data to read")
        }
@@ -121,7 +131,7 @@ func main() {
        }
        var pktEnc nncp.PktEnc
        _, err = xdr.Unmarshal(bytes.NewReader(beginning), &pktEnc)
-       if err == nil && pktEnc.Magic == nncp.MagicNNCPEv3 {
+       if err == nil && pktEnc.Magic == nncp.MagicNNCPEv4 {
                if *dump {
                        ctx, err := nncp.CtxFromCmdline(*cfgPath, "", "", false, false)
                        if err != nil {
index 06549b8426a50f3f9ccfa913926472ad523bdfe4..fe068b5326871e7349cc606778e803e6d944b239 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Send file via NNCP
+// Reassembly chunked file.
 package main
 
 import (
@@ -203,9 +203,13 @@ func process(ctx *nncp.Ctx, path string, keep, dryRun, stdout, dumpMeta bool) bo
                        }
                }
        }
-       dstW.Flush()
+       if err = dstW.Flush(); err != nil {
+               log.Fatalln("Can not flush:", err)
+       }
        if tmp != nil {
-               tmp.Sync()
+               if err = tmp.Sync(); err != nil {
+                       log.Fatalln("Can not sync:", err)
+               }
                tmp.Close()
        }
        ctx.LogD("nncp-reass", sds, "written")
@@ -316,10 +320,10 @@ func main() {
        }
 
        if flag.NArg() > 0 {
-               if !process(ctx, flag.Arg(0), *keep, *dryRun, *stdout, *dumpMeta) {
-                       os.Exit(1)
+               if process(ctx, flag.Arg(0), *keep, *dryRun, *stdout, *dumpMeta) {
+                       return
                }
-               return
+               os.Exit(1)
        }
 
        hasErrors := false
@@ -333,7 +337,9 @@ func main() {
                                if _, seen := seenMetaPaths[metaPath]; seen {
                                        continue
                                }
-                               hasErrors = hasErrors || !process(ctx, metaPath, *keep, *dryRun, false, false)
+                               if !process(ctx, metaPath, *keep, *dryRun, false, false) {
+                                       hasErrors = true
+                               }
                                seenMetaPaths[metaPath] = struct{}{}
                        }
                }
@@ -342,7 +348,9 @@ func main() {
                        log.Fatalln("Specified -node does not allow incoming")
                }
                for _, metaPath := range findMetas(ctx, *nodeOnly.Incoming) {
-                       hasErrors = hasErrors || !process(ctx, metaPath, *keep, *dryRun, false, false)
+                       if !process(ctx, metaPath, *keep, *dryRun, false, false) {
+                               hasErrors = true
+                       }
                }
        }
        if hasErrors {
index 7ce924fce88081e0dc21154a42e51e22ee8f59ad..029837906111e74e202b89fbc8459bf741a4b10d 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Remove packet from the queue
+// Remove packet from the queue.
 package main
 
 import (
index fcc872f7333d6d253210642384a7b8baf48fe761..a4fbfcbbf1e73949cca65b0a1a5dddffadcf7309 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Show queued NNCP Rx/Tx stats
+// Show queued NNCP Rx/Tx stats.
 package main
 
 import (
index bb4a58684c710e41012185632e5e285bf048a392..a87923c0c322ca6f0a937aa4f118744c6224f700 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Process inbound NNCP packets
+// Process inbound NNCP packets.
 package main
 
 import (
index 01fe150cce0a8dcdd254d1c6bc7c62ce4b856f54..e4a8653e73df3df9f6c45ab8c0da5d10f480bbb3 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-// Copy NNCP inbound and outbounds packets
+// Exchange NNCP inbound and outbounds packets with external directory.
 package main
 
 import (
@@ -162,6 +162,7 @@ func main() {
                        }
                        filename := filepath.Join(dir.Name(), fiInt.Name())
                        sds["file"] = filename
+                       delete(sds, "size")
                        fd, err := os.Open(filename)
                        if err != nil {
                                ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "open")
@@ -170,7 +171,7 @@ func main() {
                        }
                        var pktEnc nncp.PktEnc
                        _, err = xdr.Unmarshal(fd, &pktEnc)
-                       if err != nil || pktEnc.Magic != nncp.MagicNNCPEv3 {
+                       if err != nil || pktEnc.Magic != nncp.MagicNNCPEv4 {
                                ctx.LogD("nncp-xfer", sds, "is not a packet")
                                fd.Close()
                                continue
@@ -180,13 +181,18 @@ func main() {
                                fd.Close()
                                continue
                        }
+                       sds["size"] = strconv.FormatInt(fiInt.Size(), 10)
+                       if !ctx.IsEnoughSpace(fiInt.Size()) {
+                               ctx.LogE("nncp-xfer", sds, "is not enough space")
+                               fd.Close()
+                               continue
+                       }
                        fd.Seek(0, 0)
                        tmp, err := ctx.NewTmpFileWHash()
                        if err != nil {
                                log.Fatalln(err)
                        }
-                       copied, err := io.Copy(tmp.W, bufio.NewReader(fd))
-                       if err != nil {
+                       if _, err = io.CopyN(tmp.W, bufio.NewReader(fd), fiInt.Size()); err != nil {
                                ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "copy")
                                isBad = true
                                fd.Close()
@@ -201,9 +207,7 @@ func main() {
                        )); err != nil {
                                log.Fatalln(err)
                        }
-                       ctx.LogI("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{
-                               "size": strconv.FormatInt(copied, 10),
-                       }), "")
+                       ctx.LogI("nncp-xfer", sds, "")
                        if !*keep {
                                if err = os.Remove(filename); err != nil {
                                        ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "remove")
@@ -309,14 +313,19 @@ Tx:
                                isBad = true
                                continue
                        }
-                       err = bufW.Flush()
-                       tmp.Sync()
-                       tmp.Close()
-                       if err != nil {
-                               ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "copy")
+                       if err = bufW.Flush(); err != nil {
+                               tmp.Close()
+                               ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "flush")
+                               isBad = true
+                               continue
+                       }
+                       if err = tmp.Sync(); err != nil {
+                               tmp.Close()
+                               ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "sync")
                                isBad = true
                                continue
                        }
+                       tmp.Close()
                        if err = os.Rename(tmp.Name(), filepath.Join(dstPath, pktName)); err != nil {
                                ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "rename")
                                isBad = true
index ab3fc6699d7328030b0e77efce69ddfa767e027a..fb7246a5f15ff0e2943fe18ef3400a0075fc0020 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -21,8 +21,11 @@ package nncp
 import (
        "errors"
        "io/ioutil"
+       "log"
        "os"
        "path/filepath"
+
+       "golang.org/x/sys/unix"
 )
 
 type Ctx struct {
@@ -103,3 +106,11 @@ func CtxFromCmdline(cfgPath, spoolPath, logPath string, quiet, debug bool) (*Ctx
        ctx.Debug = debug
        return ctx, nil
 }
+
+func (ctx *Ctx) IsEnoughSpace(want int64) bool {
+       var s unix.Statfs_t
+       if err := unix.Statfs(ctx.Spool, &s); err != nil {
+               log.Fatalln(err)
+       }
+       return s.Bavail*int64(s.Bsize) > want
+}
index b25f5ab208dd9269848c42dad5d06a9c2175f1d0..816388dfbb98878655153dbe6060c934461c743c 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -21,15 +21,12 @@ package nncp
 import (
        "bytes"
        "crypto/rand"
-       "crypto/subtle"
-       "errors"
        "hash"
-       "io"
 
-       "chacha20"
        "cypherpunks.ru/balloon"
        "github.com/davecgh/go-xdr/xdr2"
        "golang.org/x/crypto/blake2b"
+       "golang.org/x/crypto/chacha20poly1305"
 )
 
 const (
@@ -39,7 +36,7 @@ const (
 )
 
 var (
-       MagicNNCPBv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'B', 0, 0, 2}
+       MagicNNCPBv3 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'B', 0, 0, 3}
 )
 
 type EBlob struct {
@@ -49,7 +46,6 @@ type EBlob struct {
        PCost uint32
        Salt  *[32]byte
        Blob  []byte
-       MAC   *[blake2b.Size256]byte
 }
 
 func blake256() hash.Hash {
@@ -69,46 +65,31 @@ func NewEBlob(sCost, tCost, pCost int, password, data []byte) ([]byte, error) {
        if _, err = rand.Read(salt[:]); err != nil {
                return nil, err
        }
-       key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost)
-       kdf, err := blake2b.NewXOF(32+64, key)
-       if err != nil {
-               return nil, err
-       }
-       if _, err = kdf.Write(MagicNNCPBv2[:]); err != nil {
-               return nil, err
-       }
-       keyEnc := new([32]byte)
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
-               return nil, err
-       }
-       keyAuth := make([]byte, 64)
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
-               return nil, err
-       }
-       mac, err := blake2b.New256(keyAuth)
-       if err != nil {
-               return nil, err
-       }
-       chacha20.XORKeyStream(data, data, new([16]byte), keyEnc)
-       if _, err = mac.Write(data); err != nil {
-               return nil, err
-       }
-       macTag := new([blake2b.Size256]byte)
-       mac.Sum(macTag[:0])
        eblob := EBlob{
-               Magic: MagicNNCPBv2,
+               Magic: MagicNNCPBv3,
                SCost: uint32(sCost),
                TCost: uint32(tCost),
                PCost: uint32(pCost),
                Salt:  salt,
-               Blob:  data,
-               MAC:   macTag,
+               Blob:  nil,
        }
-       var eblobRaw bytes.Buffer
-       if _, err = xdr.Marshal(&eblobRaw, &eblob); err != nil {
+       var eblobBuf bytes.Buffer
+       if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
                return nil, err
        }
-       return eblobRaw.Bytes(), nil
+       key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost)
+       aead, err := chacha20poly1305.New(key)
+       if err != nil {
+               return nil, err
+       }
+       buf := make([]byte, 0, len(data)+aead.Overhead())
+       buf = aead.Seal(buf, make([]byte, aead.NonceSize()), data, eblobBuf.Bytes())
+       eblob.Blob = buf
+       eblobBuf.Reset()
+       if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
+               return nil, err
+       }
+       return eblobBuf.Bytes(), nil
 }
 
 func DeEBlob(eblobRaw, password []byte) ([]byte, error) {
@@ -117,7 +98,7 @@ func DeEBlob(eblobRaw, password []byte) ([]byte, error) {
        if _, err = xdr.Unmarshal(bytes.NewReader(eblobRaw), &eblob); err != nil {
                return nil, err
        }
-       if eblob.Magic != MagicNNCPBv2 {
+       if eblob.Magic != MagicNNCPBv3 {
                return nil, BadMagic
        }
        key := balloon.H(
@@ -128,31 +109,25 @@ func DeEBlob(eblobRaw, password []byte) ([]byte, error) {
                int(eblob.TCost),
                int(eblob.PCost),
        )
-       kdf, err := blake2b.NewXOF(32+64, key)
+       aead, err := chacha20poly1305.New(key)
        if err != nil {
                return nil, err
        }
-       if _, err = kdf.Write(MagicNNCPBv2[:]); err != nil {
-               return nil, err
-       }
-       keyEnc := new([32]byte)
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
-               return nil, err
-       }
-       keyAuth := make([]byte, 64)
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
+
+       ciphertext := eblob.Blob
+       eblob.Blob = nil
+       var eblobBuf bytes.Buffer
+       if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
                return nil, err
        }
-       mac, err := blake2b.New256(keyAuth)
+       data, err := aead.Open(
+               ciphertext[:0],
+               make([]byte, aead.NonceSize()),
+               ciphertext,
+               eblobBuf.Bytes(),
+       )
        if err != nil {
                return nil, err
        }
-       if _, err = mac.Write(eblob.Blob); err != nil {
-               return nil, err
-       }
-       if subtle.ConstantTimeCompare(mac.Sum(nil), eblob.MAC[:]) != 1 {
-               return nil, errors.New("Unauthenticated blob")
-       }
-       chacha20.XORKeyStream(eblob.Blob, eblob.Blob, new([16]byte), keyEnc)
-       return eblob.Blob, nil
+       return data, nil
 }
diff --git a/src/cypherpunks.ru/nncp/go.mod b/src/cypherpunks.ru/nncp/go.mod
new file mode 100644 (file)
index 0000000..59cfe97
--- /dev/null
@@ -0,0 +1,15 @@
+module cypherpunks.ru/nncp
+
+require (
+       cypherpunks.ru/balloon v0.0.0-20190427214838-0e07700b0279
+       github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892
+       github.com/dustin/go-humanize v1.0.0
+       github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6
+       github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75
+       golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734
+       golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6
+       golang.org/x/sys v0.0.0-20190426135247-a129542de9ae
+       gopkg.in/yaml.v2 v2.2.2
+)
+
+replace cypherpunks.ru/balloon => git.cypherpunks.ru/balloon.git v0.0.0-20190427214838-0e07700b0279
diff --git a/src/cypherpunks.ru/nncp/go.sum b/src/cypherpunks.ru/nncp/go.sum
new file mode 100644 (file)
index 0000000..cada0a9
--- /dev/null
@@ -0,0 +1,25 @@
+git.cypherpunks.ru/balloon.git v0.0.0-20190427214838-0e07700b0279 h1:UtJj64EdBav9c3gXvDzuVhfKv0dSOUu/8rA709WRyBg=
+git.cypherpunks.ru/balloon.git v0.0.0-20190427214838-0e07700b0279/go.mod h1:MMNkZjNnjCkWMS+luQsSoSp6CCzhQiowH2uvfy5KgG8=
+github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892 h1:qg9VbHo1TlL0KDM0vYvBG9EY0X0Yku5WYIPoFWt8f6o=
+github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892/go.mod h1:CTDl0pzVzE5DEzZhPfvhY/9sPFMQIxaJ9VAMs9AagrE=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as=
+github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ=
+github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY=
+github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=
+golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6 h1:FP8hkuE6yUEaJnK7O2eTuejKWwW+Rhfj80dQ2JcKxCU=
+golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190426135247-a129542de9ae h1:mQLHiymj/JXKnnjc62tb7nD5pZLs940/sXJu+Xp3DBA=
+golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
index cea9546b150d619f9022bb415fd686949f3a390b..68031d833a982174e463f3c33517f5013893c92b 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -149,6 +149,8 @@ func (ctx *Ctx) Humanize(s string) string {
                }
                if err, exists := sds["err"]; exists {
                        msg += ": " + err
+               } else {
+                       msg += " " + rem
                }
        case "nncp-bundle":
                switch sds["xx"] {
@@ -196,6 +198,29 @@ func (ctx *Ctx) Humanize(s string) string {
                        humanize.IBytes(uint64(rx)), humanize.IBytes(uint64(rxs)),
                        humanize.IBytes(uint64(tx)), humanize.IBytes(uint64(txs)),
                )
+       case "sp-info":
+               nice, err := NicenessParse(sds["nice"])
+               if err != nil {
+                       return s
+               }
+               msg = fmt.Sprintf(
+                       "Packet %s (%s) (nice %s)",
+                       sds["hash"],
+                       size,
+                       NicenessFmt(nice),
+               )
+               offsetParsed, err := strconv.ParseUint(sds["offset"], 10, 64)
+               if err != nil {
+                       return s
+               }
+               sizeParsed, err := strconv.ParseUint(sds["size"], 10, 64)
+               if err != nil {
+                       return s
+               }
+               msg += fmt.Sprintf(": %d%%", 100*offsetParsed/sizeParsed)
+               if len(rem) > 0 {
+                       msg += ": " + rem
+               }
        case "sp-infos":
                switch sds["xx"] {
                case "rx":
@@ -206,6 +231,8 @@ func (ctx *Ctx) Humanize(s string) string {
                        return s
                }
                msg += fmt.Sprintf("%s packets, %s", sds["pkts"], size)
+       case "sp-process":
+               msg = fmt.Sprintf("%s has %s (%s): %s", nodeS, sds["hash"], size, rem)
        case "sp-file":
                switch sds["xx"] {
                case "rx":
index 019fe70aa72be0ff7bda20ade657eb0ab3b11ceb..ee7052d6d8d490228f12e512b031ca2af36ca495 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -19,6 +19,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package nncp
 
 import (
+       "io"
        "os"
        "path/filepath"
        "strconv"
@@ -64,11 +65,11 @@ func (ctx *Ctx) Jobs(nodeId *NodeId, xx TRxTx) chan Job {
                                continue
                        }
                        var pktEnc PktEnc
-                       if _, err = xdr.Unmarshal(fd, &pktEnc); err != nil || pktEnc.Magic != MagicNNCPEv3 {
+                       if _, err = xdr.Unmarshal(fd, &pktEnc); err != nil || pktEnc.Magic != MagicNNCPEv4 {
                                fd.Close()
                                continue
                        }
-                       fd.Seek(0, 0)
+                       fd.Seek(0, io.SeekStart)
                        ctx.LogD("jobs", SDS{
                                "xx":   string(xx),
                                "node": pktEnc.Sender,
index cc94cb690905389ad963c411eda2f088decf34bc..556e22d41491034322d388b7e95df166c9a99f94 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
index 55d8844a8edad89ddff96d76810206c7d4ba245b..5ba13e96f96d108c70e57f020a765ccd40457a8b 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
index 490214065073e8bbd7792351a48966ca47e25e14..32f2eb6c3ed072edd498cd2a3450fbd4072f9c8b 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -48,7 +48,7 @@ func VersionGet() string {
 
 func UsageHeader() string {
        return VersionGet() + `
-Copyright (C) 2016-2017 Sergey Matveev
+Copyright (C) 2016-2019 Sergey Matveev
 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
 This is free software: you are free to change and redistribute it.
 There is NO WARRANTY, to the extent permitted by law.
index b6cdcc65f9b09f08d55530c71d8e4f0922c61f16..e903567bcdbff6befad0842cf61252938ff13b9d 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
index 1d1ff34bf0b082dc3bce06381513b3927372889a..b2ab591c539a3497fb4a2d03f88f0c4e23eda93b 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -20,25 +20,26 @@ package nncp
 
 import (
        "bytes"
+       "crypto/cipher"
        "crypto/rand"
-       "crypto/subtle"
        "encoding/binary"
        "errors"
        "io"
 
-       "chacha20"
        "github.com/davecgh/go-xdr/xdr2"
        "golang.org/x/crypto/blake2b"
+       "golang.org/x/crypto/chacha20poly1305"
        "golang.org/x/crypto/curve25519"
        "golang.org/x/crypto/ed25519"
        "golang.org/x/crypto/nacl/box"
+       "golang.org/x/crypto/poly1305"
 )
 
 type PktType uint8
 
 const (
        EncBlkSize = 128 * (1 << 10)
-       KDFXOFSize = 2*(32+64) + 32
+       KDFXOFSize = chacha20poly1305.KeySize * 2
 
        PktTypeFile PktType = iota
        PktTypeFreq PktType = iota
@@ -52,12 +53,13 @@ const (
 
 var (
        MagicNNCPPv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 2}
-       MagicNNCPEv3 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 3}
+       MagicNNCPEv4 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 4}
        BadMagic     error   = errors.New("Unknown magic number")
        BadPktType   error   = errors.New("Unknown packet type")
 
-       PktOverhead    int64
-       PktEncOverhead int64
+       PktOverhead     int64
+       PktEncOverhead  int64
+       PktSizeOverhead int64 = 8 + poly1305.TagSize
 )
 
 type Pkt struct {
@@ -95,7 +97,7 @@ func init() {
        if err != nil {
                panic(err)
        }
-       PktOverhead = 8 + blake2b.Size256 + int64(n) + blake2b.Size256
+       PktOverhead = int64(n)
        buf.Reset()
 
        dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
@@ -103,7 +105,7 @@ func init() {
                panic(err)
        }
        pktEnc := PktEnc{
-               Magic:     MagicNNCPEv3,
+               Magic:     MagicNNCPEv4,
                Nice:      123,
                Sender:    dummyId,
                Recipient: dummyId,
@@ -132,42 +134,60 @@ func NewPkt(typ PktType, nice uint8, path []byte) (*Pkt, error) {
        return &pkt, nil
 }
 
-type DevZero struct{}
-
-func (d DevZero) Read(b []byte) (n int, err error) {
-       for n = 0; n < len(b); n++ {
-               b[n] = 0
-       }
-       return
-}
-
-func ae(keyEnc *[32]byte, r io.Reader, w io.Writer) (int, error) {
+func aeadProcess(
+       aead cipher.AEAD,
+       nonce []byte,
+       doEncrypt bool,
+       r io.Reader,
+       w io.Writer,
+) (int, error) {
        var blkCtr uint64
-       ciphNonce := new([16]byte)
-       ciphCtr := ciphNonce[8:]
-       buf := make([]byte, EncBlkSize)
+       ciphCtr := nonce[len(nonce)-8:]
+       buf := make([]byte, EncBlkSize+aead.Overhead())
+       var toRead []byte
+       var toWrite []byte
        var n int
-       var written int
+       var readBytes int
        var err error
+       if doEncrypt {
+               toRead = buf[:EncBlkSize]
+       } else {
+               toRead = buf
+       }
        for {
-               n, err = io.ReadFull(r, buf)
+               n, err = io.ReadFull(r, toRead)
                if err != nil {
                        if err == io.EOF {
                                break
                        }
                        if err != io.ErrUnexpectedEOF {
-                               return written + n, err
+                               return readBytes + n, err
                        }
                }
-               written += n
+               readBytes += n
                blkCtr++
                binary.BigEndian.PutUint64(ciphCtr, blkCtr)
-               chacha20.XORKeyStream(buf[:n], buf[:n], ciphNonce, keyEnc)
-               if _, err = w.Write(buf[:n]); err != nil {
-                       return written, err
+               if doEncrypt {
+                       toWrite = aead.Seal(buf[:0], nonce, buf[:n], nil)
+               } else {
+                       toWrite, err = aead.Open(buf[:0], nonce, buf[:n], nil)
+                       if err != nil {
+                               return readBytes, err
+                       }
+               }
+               if _, err = w.Write(toWrite); err != nil {
+                       return readBytes, err
                }
        }
-       return written, nil
+       return readBytes, nil
+}
+
+func sizeWithTags(size int64) (fullSize int64) {
+       fullSize = size + (size/EncBlkSize)*poly1305.TagSize
+       if size%EncBlkSize != 0 {
+               fullSize += poly1305.TagSize
+       }
+       return
 }
 
 func PktEncWrite(
@@ -177,7 +197,8 @@ func PktEncWrite(
        nice uint8,
        size, padSize int64,
        data io.Reader,
-       out io.Writer) error {
+       out io.Writer,
+) error {
        pubEph, prvEph, err := box.GenerateKey(rand.Reader)
        if err != nil {
                return err
@@ -187,7 +208,7 @@ func PktEncWrite(
                return err
        }
        tbs := PktTbs{
-               Magic:     MagicNNCPEv3,
+               Magic:     MagicNNCPEv4,
                Nice:      nice,
                Sender:    our.Id,
                Recipient: their.Id,
@@ -200,7 +221,7 @@ func PktEncWrite(
        signature := new([ed25519.SignatureSize]byte)
        copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
        pktEnc := PktEnc{
-               Magic:     MagicNNCPEv3,
+               Magic:     MagicNNCPEv4,
                Nice:      nice,
                Sender:    our.Id,
                Recipient: their.Id,
@@ -216,71 +237,46 @@ func PktEncWrite(
        if err != nil {
                return err
        }
-       if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil {
+       if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
                return err
        }
 
-       keyEnc := new([32]byte)
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
+       key := make([]byte, chacha20poly1305.KeySize)
+       if _, err = io.ReadFull(kdf, key); err != nil {
                return err
        }
-       keyAuth := make([]byte, 64)
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
-               return err
-       }
-       mac, err := blake2b.New256(keyAuth)
+       aead, err := chacha20poly1305.New(key)
        if err != nil {
                return err
        }
+       nonce := make([]byte, aead.NonceSize())
 
-       sizeBuf := make([]byte, 8)
-       binary.BigEndian.PutUint64(sizeBuf, uint64(size))
-       chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc)
-       if _, err = out.Write(sizeBuf); err != nil {
-               return err
-       }
-       if _, err = mac.Write(sizeBuf); err != nil {
-               return err
-       }
-       if _, err = out.Write(mac.Sum(nil)); err != nil {
+       fullSize := pktBuf.Len() + int(size)
+       sizeBuf := make([]byte, 8+aead.Overhead())
+       binary.BigEndian.PutUint64(sizeBuf, uint64(sizeWithTags(int64(fullSize))))
+       if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], nil)); err != nil {
                return err
        }
 
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
-               return err
-       }
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
-               return err
-       }
-       mac, err = blake2b.New256(keyAuth)
-       if err != nil {
-               return err
-       }
        lr := io.LimitedReader{R: data, N: size}
        mr := io.MultiReader(&pktBuf, &lr)
-       mw := io.MultiWriter(out, mac)
-       fullSize := pktBuf.Len() + int(size)
-       written, err := ae(keyEnc, mr, mw)
+       written, err := aeadProcess(aead, nonce, true, mr, out)
        if err != nil {
                return err
        }
        if written != fullSize {
                return io.ErrUnexpectedEOF
        }
-       if _, err = out.Write(mac.Sum(nil)); err != nil {
-               return err
-       }
        if padSize > 0 {
-               if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
+               if _, err = io.ReadFull(kdf, key); err != nil {
                        return err
                }
-               lr = io.LimitedReader{R: DevZero{}, N: padSize}
-               written, err = ae(keyEnc, &lr, out)
+               kdf, err = blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
                if err != nil {
                        return err
                }
-               if written != int(padSize) {
-                       return io.ErrUnexpectedEOF
+               if _, err = io.CopyN(out, kdf, padSize); err != nil {
+                       return err
                }
        }
        return nil
@@ -288,7 +284,7 @@ func PktEncWrite(
 
 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
        tbs := PktTbs{
-               Magic:     MagicNNCPEv3,
+               Magic:     MagicNNCPEv4,
                Nice:      pktEnc.Nice,
                Sender:    their.Id,
                Recipient: our.Id,
@@ -305,13 +301,14 @@ func PktEncRead(
        our *NodeOur,
        nodes map[NodeId]*Node,
        data io.Reader,
-       out io.Writer) (*Node, int64, error) {
+       out io.Writer,
+) (*Node, int64, error) {
        var pktEnc PktEnc
        _, err := xdr.Unmarshal(data, &pktEnc)
        if err != nil {
                return nil, 0, err
        }
-       if pktEnc.Magic != MagicNNCPEv3 {
+       if pktEnc.Magic != MagicNNCPEv4 {
                return nil, 0, BadMagic
        }
        their, known := nodes[*pktEnc.Sender]
@@ -334,66 +331,37 @@ func PktEncRead(
        if err != nil {
                return their, 0, err
        }
-       if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil {
+       if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
                return their, 0, err
        }
 
-       keyEnc := new([32]byte)
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
-               return their, 0, err
-       }
-       keyAuth := make([]byte, 64)
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
+       key := make([]byte, chacha20poly1305.KeySize)
+       if _, err = io.ReadFull(kdf, key); err != nil {
                return their, 0, err
        }
-       mac, err := blake2b.New256(keyAuth)
+       aead, err := chacha20poly1305.New(key)
        if err != nil {
                return their, 0, err
        }
+       nonce := make([]byte, aead.NonceSize())
 
-       sizeBuf := make([]byte, 8)
+       sizeBuf := make([]byte, 8+aead.Overhead())
        if _, err = io.ReadFull(data, sizeBuf); err != nil {
                return their, 0, err
        }
-       if _, err = mac.Write(sizeBuf); err != nil {
-               return their, 0, err
-       }
-       tag := make([]byte, blake2b.Size256)
-       if _, err = io.ReadFull(data, tag); err != nil {
-               return their, 0, err
-       }
-       if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
-               return their, 0, errors.New("Unauthenticated size")
-       }
-       chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc)
-       size := int64(binary.BigEndian.Uint64(sizeBuf))
-
-       if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
-               return their, size, err
-       }
-       if _, err = io.ReadFull(kdf, keyAuth); err != nil {
-               return their, size, err
-       }
-       mac, err = blake2b.New256(keyAuth)
+       sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, nil)
        if err != nil {
                return their, 0, err
        }
+       size := int64(binary.BigEndian.Uint64(sizeBuf))
 
-       fullSize := PktOverhead + size - 8 - 2*blake2b.Size256
-       lr := io.LimitedReader{R: data, N: fullSize}
-       tr := io.TeeReader(&lr, mac)
-       written, err := ae(keyEnc, tr, out)
+       lr := io.LimitedReader{R: data, N: size}
+       written, err := aeadProcess(aead, nonce, false, &lr, out)
        if err != nil {
                return their, int64(written), err
        }
-       if written != int(fullSize) {
+       if written != int(size) {
                return their, int64(written), io.ErrUnexpectedEOF
        }
-       if _, err = io.ReadFull(data, tag); err != nil {
-               return their, size, err
-       }
-       if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
-               return their, size, errors.New("Unauthenticated payload")
-       }
        return their, size, nil
 }
index 5775e7327b18eb5fff91c3a276814528c97eae80..43602d733ac199555ce11bc5d8ec26ff4ed48daa 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -120,7 +120,7 @@ func TestPktEncRead(t *testing.T) {
                if *node.Id != *node1.Id {
                        return false
                }
-               if sizeGot != int64(size) {
+               if sizeGot != sizeWithTags(PktOverhead+int64(size)) {
                        return false
                }
                var pktBuf bytes.Buffer
index d3ffb9f142a08e4d1bae7a2d88a963650267ee4f..f28251459510edd97f8df3b77c6f5b8164297ac8 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -170,11 +170,11 @@ func payloadsSplit(payloads [][]byte) [][]byte {
 }
 
 type SPState struct {
-       ctx            *Ctx
+       Ctx            *Ctx
        Node           *Node
+       Nice           uint8
        onlineDeadline uint
        maxOnlineTime  uint
-       nice           uint8
        hs             *noise.HandshakeState
        csOur          *noise.CipherState
        csTheir        *noise.CipherState
@@ -197,6 +197,8 @@ type SPState struct {
        rxRate         int
        txRate         int
        isDead         bool
+       listOnly       bool
+       onlyPkts       map[[32]byte]bool
        sync.RWMutex
 }
 
@@ -213,8 +215,8 @@ func (state *SPState) NotAlive() bool {
 }
 
 func (state *SPState) dirUnlock() {
-       state.ctx.UnlockDir(state.rxLock)
-       state.ctx.UnlockDir(state.txLock)
+       state.Ctx.UnlockDir(state.rxLock)
+       state.Ctx.UnlockDir(state.txLock)
 }
 
 func (state *SPState) WriteSP(dst io.Writer, payload []byte) error {
@@ -280,68 +282,52 @@ func (ctx *Ctx) infosOur(nodeId *NodeId, nice uint8, seen *map[[32]byte]uint8) [
        return payloadsSplit(payloads)
 }
 
-func (ctx *Ctx) StartI(
-       conn ConnDeadlined,
-       nodeId *NodeId,
-       nice uint8,
-       xxOnly TRxTx,
-       rxRate, txRate int,
-       onlineDeadline, maxOnlineTime uint) (*SPState, error) {
-       err := ctx.ensureRxDir(nodeId)
+func (state *SPState) StartI(conn ConnDeadlined) error {
+       nodeId := state.Node.Id
+       err := state.Ctx.ensureRxDir(nodeId)
        if err != nil {
-               return nil, err
+               return err
        }
        var rxLock *os.File
-       if xxOnly == "" || xxOnly == TRx {
-               rxLock, err = ctx.LockDir(nodeId, TRx)
+       if !state.listOnly && (state.xxOnly == "" || state.xxOnly == TRx) {
+               rxLock, err = state.Ctx.LockDir(nodeId, TRx)
                if err != nil {
-                       return nil, err
+                       return err
                }
        }
        var txLock *os.File
-       if xxOnly == "" || xxOnly == TTx {
-               txLock, err = ctx.LockDir(nodeId, TTx)
+       if !state.listOnly && (state.xxOnly == "" || state.xxOnly == TTx) {
+               txLock, err = state.Ctx.LockDir(nodeId, TTx)
                if err != nil {
-                       return nil, err
+                       return err
                }
        }
        started := time.Now()
-       node := ctx.Neigh[*nodeId]
        conf := noise.Config{
                CipherSuite: NoiseCipherSuite,
                Pattern:     noise.HandshakeIK,
                Initiator:   true,
                StaticKeypair: noise.DHKey{
-                       Private: ctx.Self.NoisePrv[:],
-                       Public:  ctx.Self.NoisePub[:],
+                       Private: state.Ctx.Self.NoisePrv[:],
+                       Public:  state.Ctx.Self.NoisePub[:],
                },
-               PeerStatic: node.NoisePub[:],
+               PeerStatic: state.Node.NoisePub[:],
        }
        hs, err := noise.NewHandshakeState(conf)
        if err != nil {
-               return nil, err
-       }
-       state := SPState{
-               ctx:            ctx,
-               hs:             hs,
-               Node:           node,
-               onlineDeadline: onlineDeadline,
-               maxOnlineTime:  maxOnlineTime,
-               nice:           nice,
-               payloads:       make(chan []byte),
-               infosTheir:     make(map[[32]byte]*SPInfo),
-               infosOurSeen:   make(map[[32]byte]uint8),
-               started:        started,
-               rxLock:         rxLock,
-               txLock:         txLock,
-               xxOnly:         xxOnly,
-               rxRate:         rxRate,
-               txRate:         txRate,
+               return err
        }
+       state.hs = hs
+       state.payloads = make(chan []byte)
+       state.infosTheir = make(map[[32]byte]*SPInfo)
+       state.infosOurSeen = make(map[[32]byte]uint8)
+       state.started = started
+       state.rxLock = rxLock
+       state.txLock = txLock
 
        var infosPayloads [][]byte
-       if xxOnly == "" || xxOnly == TTx {
-               infosPayloads = ctx.infosOur(nodeId, nice, &state.infosOurSeen)
+       if !state.listOnly && (state.xxOnly == "" || state.xxOnly == TTx) {
+               infosPayloads = state.Ctx.infosOur(nodeId, state.Nice, &state.infosOurSeen)
        }
        var firstPayload []byte
        if len(infosPayloads) > 0 {
@@ -357,122 +343,119 @@ func (ctx *Ctx) StartI(
        buf, _, _, err = state.hs.WriteMessage(nil, firstPayload)
        if err != nil {
                state.dirUnlock()
-               return nil, err
+               return err
        }
-       sds := SDS{"node": nodeId, "nice": strconv.Itoa(int(nice))}
-       ctx.LogD("sp-start", sds, "sending first message")
+       sds := SDS{"node": nodeId, "nice": strconv.Itoa(int(state.Nice))}
+       state.Ctx.LogD("sp-start", sds, "sending first message")
        conn.SetWriteDeadline(time.Now().Add(DefaultDeadline * time.Second))
        if err = state.WriteSP(conn, buf); err != nil {
-               ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
+               state.Ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
                state.dirUnlock()
-               return nil, err
+               return err
        }
-       ctx.LogD("sp-start", sds, "waiting for first message")
+       state.Ctx.LogD("sp-start", sds, "waiting for first message")
        conn.SetReadDeadline(time.Now().Add(DefaultDeadline * time.Second))
        if buf, err = state.ReadSP(conn); err != nil {
-               ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
+               state.Ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
                state.dirUnlock()
-               return nil, err
+               return err
        }
        payload, state.csOur, state.csTheir, err = state.hs.ReadMessage(nil, buf)
        if err != nil {
-               ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
+               state.Ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
                state.dirUnlock()
-               return nil, err
+               return err
        }
-       ctx.LogD("sp-start", sds, "starting workers")
+       state.Ctx.LogD("sp-start", sds, "starting workers")
        err = state.StartWorkers(conn, infosPayloads, payload)
        if err != nil {
-               ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
+               state.Ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
                state.dirUnlock()
-               return nil, err
+               return err
        }
-       return &state, err
+       return err
 }
 
-func (ctx *Ctx) StartR(conn ConnDeadlined, nice uint8, xxOnly TRxTx) (*SPState, error) {
+func (state *SPState) StartR(conn ConnDeadlined) error {
        started := time.Now()
        conf := noise.Config{
                CipherSuite: NoiseCipherSuite,
                Pattern:     noise.HandshakeIK,
                Initiator:   false,
                StaticKeypair: noise.DHKey{
-                       Private: ctx.Self.NoisePrv[:],
-                       Public:  ctx.Self.NoisePub[:],
+                       Private: state.Ctx.Self.NoisePrv[:],
+                       Public:  state.Ctx.Self.NoisePub[:],
                },
        }
        hs, err := noise.NewHandshakeState(conf)
        if err != nil {
-               return nil, err
-       }
-       state := SPState{
-               ctx:          ctx,
-               hs:           hs,
-               nice:         nice,
-               payloads:     make(chan []byte),
-               infosOurSeen: make(map[[32]byte]uint8),
-               infosTheir:   make(map[[32]byte]*SPInfo),
-               started:      started,
-               xxOnly:       xxOnly,
+               return err
        }
+       xxOnly := TRxTx("")
+       state.hs = hs
+       state.payloads = make(chan []byte)
+       state.infosOurSeen = make(map[[32]byte]uint8)
+       state.infosTheir = make(map[[32]byte]*SPInfo)
+       state.started = started
+       state.xxOnly = xxOnly
        var buf []byte
        var payload []byte
-       ctx.LogD(
+       state.Ctx.LogD(
                "sp-start",
-               SDS{"nice": strconv.Itoa(int(nice))},
+               SDS{"nice": strconv.Itoa(int(state.Nice))},
                "waiting for first message",
        )
        conn.SetReadDeadline(time.Now().Add(DefaultDeadline * time.Second))
        if buf, err = state.ReadSP(conn); err != nil {
-               ctx.LogE("sp-start", SDS{"err": err}, "")
-               return nil, err
+               state.Ctx.LogE("sp-start", SDS{"err": err}, "")
+               return err
        }
        if payload, _, _, err = state.hs.ReadMessage(nil, buf); err != nil {
-               ctx.LogE("sp-start", SDS{"err": err}, "")
-               return nil, err
+               state.Ctx.LogE("sp-start", SDS{"err": err}, "")
+               return err
        }
 
        var node *Node
-       for _, node = range ctx.Neigh {
+       for _, node = range state.Ctx.Neigh {
                if subtle.ConstantTimeCompare(state.hs.PeerStatic(), node.NoisePub[:]) == 1 {
                        break
                }
        }
        if node == nil {
                peerId := ToBase32(state.hs.PeerStatic())
-               ctx.LogE("sp-start", SDS{"peer": peerId}, "unknown")
-               return nil, errors.New("Unknown peer: " + peerId)
+               state.Ctx.LogE("sp-start", SDS{"peer": peerId}, "unknown")
+               return errors.New("Unknown peer: " + peerId)
        }
        state.Node = node
        state.rxRate = node.RxRate
        state.txRate = node.TxRate
        state.onlineDeadline = node.OnlineDeadline
        state.maxOnlineTime = node.MaxOnlineTime
-       sds := SDS{"node": node.Id, "nice": strconv.Itoa(int(nice))}
+       sds := SDS{"node": node.Id, "nice": strconv.Itoa(int(state.Nice))}
 
-       if ctx.ensureRxDir(node.Id); err != nil {
-               return nil, err
+       if state.Ctx.ensureRxDir(node.Id); err != nil {
+               return err
        }
        var rxLock *os.File
        if xxOnly == "" || xxOnly == TRx {
-               rxLock, err = ctx.LockDir(node.Id, TRx)
+               rxLock, err = state.Ctx.LockDir(node.Id, TRx)
                if err != nil {
-                       return nil, err
+                       return err
                }
        }
        state.rxLock = rxLock
        var txLock *os.File
        if xxOnly == "" || xxOnly == TTx {
-               txLock, err = ctx.LockDir(node.Id, TTx)
+               txLock, err = state.Ctx.LockDir(node.Id, TTx)
                if err != nil {
-                       return nil, err
+                       return err
                }
        }
        state.txLock = txLock
 
        var infosPayloads [][]byte
        if xxOnly == "" || xxOnly == TTx {
-               infosPayloads = ctx.infosOur(node.Id, nice, &state.infosOurSeen)
+               infosPayloads = state.Ctx.infosOur(node.Id, state.Nice, &state.infosOurSeen)
        }
        var firstPayload []byte
        if len(infosPayloads) > 0 {
@@ -483,36 +466,36 @@ func (ctx *Ctx) StartR(conn ConnDeadlined, nice uint8, xxOnly TRxTx) (*SPState,
                firstPayload = append(firstPayload, SPHaltMarshalized...)
        }
 
-       ctx.LogD("sp-start", sds, "sending first message")
+       state.Ctx.LogD("sp-start", sds, "sending first message")
        buf, state.csTheir, state.csOur, err = state.hs.WriteMessage(nil, firstPayload)
        if err != nil {
                state.dirUnlock()
-               return nil, err
+               return err
        }
        conn.SetWriteDeadline(time.Now().Add(DefaultDeadline * time.Second))
        if err = state.WriteSP(conn, buf); err != nil {
-               ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
+               state.Ctx.LogE("sp-start", SdsAdd(sds, SDS{"err": err}), "")
                state.dirUnlock()
-               return nil, err
+               return err
        }
-       ctx.LogD("sp-start", sds, "starting workers")
+       state.Ctx.LogD("sp-start", sds, "starting workers")
        err = state.StartWorkers(conn, infosPayloads, payload)
        if err != nil {
                state.dirUnlock()
-               return nil, err
+               return err
        }
-       return &state, err
+       return err
 }
 
 func (state *SPState) StartWorkers(
        conn ConnDeadlined,
        infosPayloads [][]byte,
        payload []byte) error {
-       sds := SDS{"node": state.Node.Id, "nice": strconv.Itoa(int(state.nice))}
+       sds := SDS{"node": state.Node.Id, "nice": strconv.Itoa(int(state.Nice))}
        if len(infosPayloads) > 1 {
                go func() {
                        for _, payload := range infosPayloads[1:] {
-                               state.ctx.LogD(
+                               state.Ctx.LogD(
                                        "sp-work",
                                        SdsAdd(sds, SDS{"size": strconv.Itoa(len(payload))}),
                                        "queuing remaining payload",
@@ -521,20 +504,20 @@ func (state *SPState) StartWorkers(
                        }
                }()
        }
-       state.ctx.LogD(
+       state.Ctx.LogD(
                "sp-work",
                SdsAdd(sds, SDS{"size": strconv.Itoa(len(payload))}),
                "processing first payload",
        )
        replies, err := state.ProcessSP(payload)
        if err != nil {
-               state.ctx.LogE("sp-work", SdsAdd(sds, SDS{"err": err}), "")
+               state.Ctx.LogE("sp-work", SdsAdd(sds, SDS{"err": err}), "")
                return err
        }
 
        go func() {
                for _, reply := range replies {
-                       state.ctx.LogD(
+                       state.Ctx.LogD(
                                "sp-work",
                                SdsAdd(sds, SDS{"size": strconv.Itoa(len(reply))}),
                                "queuing reply",
@@ -543,15 +526,18 @@ func (state *SPState) StartWorkers(
                }
        }()
 
-       if state.xxOnly == "" || state.xxOnly == TTx {
+       if !state.listOnly && (state.xxOnly == "" || state.xxOnly == TTx) {
                go func() {
                        for range time.Tick(time.Second) {
-                               for _, payload := range state.ctx.infosOur(
+                               if state.NotAlive() {
+                                       return
+                               }
+                               for _, payload := range state.Ctx.infosOur(
                                        state.Node.Id,
-                                       state.nice,
+                                       state.Nice,
                                        &state.infosOurSeen,
                                ) {
-                                       state.ctx.LogD(
+                                       state.Ctx.LogD(
                                                "sp-work",
                                                SdsAdd(sds, SDS{"size": strconv.Itoa(len(payload))}),
                                                "queuing new info",
@@ -575,7 +561,7 @@ func (state *SPState) StartWorkers(
                        var payload []byte
                        select {
                        case payload = <-state.payloads:
-                               state.ctx.LogD(
+                               state.Ctx.LogD(
                                        "sp-xmit",
                                        SdsAdd(sds, SDS{"size": strconv.Itoa(len(payload))}),
                                        "got payload",
@@ -585,7 +571,7 @@ func (state *SPState) StartWorkers(
                        if payload == nil {
                                state.RLock()
                                if len(state.queueTheir) == 0 {
-                                       state.ctx.LogD("sp-xmit", sds, "file queue is empty")
+                                       state.Ctx.LogD("sp-xmit", sds, "file queue is empty")
                                        state.RUnlock()
                                        time.Sleep(100 * time.Millisecond)
                                        continue
@@ -602,38 +588,38 @@ func (state *SPState) StartWorkers(
                                        "hash": ToBase32(freq.Hash[:]),
                                        "size": strconv.FormatInt(int64(freq.Offset), 10),
                                })
-                               state.ctx.LogD("sp-file", sdsp, "queueing")
+                               state.Ctx.LogD("sp-file", sdsp, "queueing")
                                fd, err := os.Open(filepath.Join(
-                                       state.ctx.Spool,
+                                       state.Ctx.Spool,
                                        state.Node.Id.String(),
                                        string(TTx),
                                        ToBase32(freq.Hash[:]),
                                ))
                                if err != nil {
-                                       state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
+                                       state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
                                        break
                                }
                                fi, err := fd.Stat()
                                if err != nil {
-                                       state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
+                                       state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
                                        break
                                }
                                fullSize := uint64(fi.Size())
                                var buf []byte
                                if freq.Offset < fullSize {
-                                       state.ctx.LogD("sp-file", sdsp, "seeking")
-                                       if _, err = fd.Seek(int64(freq.Offset), 0); err != nil {
-                                               state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
+                                       state.Ctx.LogD("sp-file", sdsp, "seeking")
+                                       if _, err = fd.Seek(int64(freq.Offset), io.SeekStart); err != nil {
+                                               state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
                                                break
                                        }
                                        buf = make([]byte, MaxSPSize-SPHeadOverhead-SPFileOverhead)
                                        n, err := fd.Read(buf)
                                        if err != nil {
-                                               state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
+                                               state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
                                                break
                                        }
                                        buf = buf[:n]
-                                       state.ctx.LogD(
+                                       state.Ctx.LogD(
                                                "sp-file",
                                                SdsAdd(sdsp, SDS{"size": strconv.Itoa(n)}),
                                                "read",
@@ -648,11 +634,11 @@ func (state *SPState) StartWorkers(
                                ourSize := freq.Offset + uint64(len(buf))
                                sdsp["size"] = strconv.FormatInt(int64(ourSize), 10)
                                sdsp["fullsize"] = strconv.FormatInt(int64(fullSize), 10)
-                               state.ctx.LogP("sp-file", sdsp, "")
+                               state.Ctx.LogP("sp-file", sdsp, "")
                                state.Lock()
                                if len(state.queueTheir) > 0 && *state.queueTheir[0].freq.Hash == *freq.Hash {
                                        if ourSize == fullSize {
-                                               state.ctx.LogD("sp-file", sdsp, "finished")
+                                               state.Ctx.LogD("sp-file", sdsp, "finished")
                                                if len(state.queueTheir) > 1 {
                                                        state.queueTheir = state.queueTheir[1:]
                                                } else {
@@ -662,18 +648,18 @@ func (state *SPState) StartWorkers(
                                                state.queueTheir[0].freq.Offset += uint64(len(buf))
                                        }
                                } else {
-                                       state.ctx.LogD("sp-file", sdsp, "queue disappeared")
+                                       state.Ctx.LogD("sp-file", sdsp, "queue disappeared")
                                }
                                state.Unlock()
                        }
-                       state.ctx.LogD(
+                       state.Ctx.LogD(
                                "sp-xmit",
                                SdsAdd(sds, SDS{"size": strconv.Itoa(len(payload))}),
                                "sending",
                        )
                        conn.SetWriteDeadline(time.Now().Add(DefaultDeadline * time.Second))
                        if err := state.WriteSP(conn, state.csOur.Encrypt(nil, nil, payload)); err != nil {
-                               state.ctx.LogE("sp-xmit", SdsAdd(sds, SDS{"err": err}), "")
+                               state.Ctx.LogE("sp-xmit", SdsAdd(sds, SDS{"err": err}), "")
                                break
                        }
                }
@@ -689,7 +675,7 @@ func (state *SPState) StartWorkers(
                        if state.NotAlive() {
                                return
                        }
-                       state.ctx.LogD("sp-recv", sds, "waiting for payload")
+                       state.Ctx.LogD("sp-recv", sds, "waiting for payload")
                        conn.SetReadDeadline(time.Now().Add(DefaultDeadline * time.Second))
                        payload, err := state.ReadSP(conn)
                        if err != nil {
@@ -701,32 +687,32 @@ func (state *SPState) StartWorkers(
                                if unmarshalErr.ErrorCode == xdr.ErrIO {
                                        break
                                }
-                               state.ctx.LogE("sp-recv", SdsAdd(sds, SDS{"err": err}), "")
+                               state.Ctx.LogE("sp-recv", SdsAdd(sds, SDS{"err": err}), "")
                                break
                        }
-                       state.ctx.LogD(
+                       state.Ctx.LogD(
                                "sp-recv",
                                SdsAdd(sds, SDS{"size": strconv.Itoa(len(payload))}),
                                "got payload",
                        )
                        payload, err = state.csTheir.Decrypt(nil, nil, payload)
                        if err != nil {
-                               state.ctx.LogE("sp-recv", SdsAdd(sds, SDS{"err": err}), "")
+                               state.Ctx.LogE("sp-recv", SdsAdd(sds, SDS{"err": err}), "")
                                break
                        }
-                       state.ctx.LogD(
+                       state.Ctx.LogD(
                                "sp-recv",
                                SdsAdd(sds, SDS{"size": strconv.Itoa(len(payload))}),
                                "processing",
                        )
                        replies, err := state.ProcessSP(payload)
                        if err != nil {
-                               state.ctx.LogE("sp-recv", SdsAdd(sds, SDS{"err": err}), "")
+                               state.Ctx.LogE("sp-recv", SdsAdd(sds, SDS{"err": err}), "")
                                break
                        }
                        go func() {
                                for _, reply := range replies {
-                                       state.ctx.LogD(
+                                       state.Ctx.LogD(
                                                "sp-recv",
                                                SdsAdd(sds, SDS{"size": strconv.Itoa(len(reply))}),
                                                "queuing reply",
@@ -760,123 +746,129 @@ func (state *SPState) Wait() {
 }
 
 func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
-       sds := SDS{"node": state.Node.Id, "nice": strconv.Itoa(int(state.nice))}
+       sds := SDS{"node": state.Node.Id, "nice": strconv.Itoa(int(state.Nice))}
        r := bytes.NewReader(payload)
        var err error
        var replies [][]byte
        var infosGot bool
        for r.Len() > 0 {
-               state.ctx.LogD("sp-process", sds, "unmarshaling header")
+               state.Ctx.LogD("sp-process", sds, "unmarshaling header")
                var head SPHead
                if _, err = xdr.Unmarshal(r, &head); err != nil {
-                       state.ctx.LogE("sp-process", SdsAdd(sds, SDS{"err": err}), "")
+                       state.Ctx.LogE("sp-process", SdsAdd(sds, SDS{"err": err}), "")
                        return nil, err
                }
                switch head.Type {
                case SPTypeInfo:
                        infosGot = true
                        sdsp := SdsAdd(sds, SDS{"type": "info"})
-                       state.ctx.LogD("sp-process", sdsp, "unmarshaling packet")
+                       state.Ctx.LogD("sp-process", sdsp, "unmarshaling packet")
                        var info SPInfo
                        if _, err = xdr.Unmarshal(r, &info); err != nil {
-                               state.ctx.LogE("sp-process", SdsAdd(sdsp, SDS{"err": err}), "")
+                               state.Ctx.LogE("sp-process", SdsAdd(sdsp, SDS{"err": err}), "")
                                return nil, err
                        }
                        sdsp = SdsAdd(sds, SDS{
                                "hash": ToBase32(info.Hash[:]),
                                "size": strconv.FormatInt(int64(info.Size), 10),
+                               "nice": strconv.Itoa(int(info.Nice)),
                        })
-                       if info.Nice > state.nice {
-                               state.ctx.LogD("sp-process", sdsp, "too nice")
+                       if !state.listOnly && info.Nice > state.Nice {
+                               state.Ctx.LogD("sp-process", sdsp, "too nice")
                                continue
                        }
-                       state.ctx.LogD("sp-process", sdsp, "received")
-                       if state.xxOnly == TTx {
+                       state.Ctx.LogD("sp-process", sdsp, "received")
+                       if !state.listOnly && state.xxOnly == TTx {
                                continue
                        }
                        state.Lock()
                        state.infosTheir[*info.Hash] = &info
                        state.Unlock()
-                       state.ctx.LogD("sp-process", sdsp, "stating part")
+                       state.Ctx.LogD("sp-process", sdsp, "stating part")
                        pktPath := filepath.Join(
-                               state.ctx.Spool,
+                               state.Ctx.Spool,
                                state.Node.Id.String(),
                                string(TRx),
                                ToBase32(info.Hash[:]),
                        )
                        if _, err = os.Stat(pktPath); err == nil {
-                               state.ctx.LogD("sp-process", sdsp, "already done")
-                               replies = append(replies, MarshalSP(SPTypeDone, SPDone{info.Hash}))
+                               state.Ctx.LogI("sp-info", sdsp, "already done")
+                               if !state.listOnly {
+                                       replies = append(replies, MarshalSP(SPTypeDone, SPDone{info.Hash}))
+                               }
                                continue
                        }
                        if _, err = os.Stat(pktPath + SeenSuffix); err == nil {
-                               state.ctx.LogD("sp-process", sdsp, "already seen")
-                               replies = append(replies, MarshalSP(SPTypeDone, SPDone{info.Hash}))
+                               state.Ctx.LogI("sp-info", sdsp, "already seen")
+                               if !state.listOnly {
+                                       replies = append(replies, MarshalSP(SPTypeDone, SPDone{info.Hash}))
+                               }
                                continue
                        }
                        fi, err := os.Stat(pktPath + PartSuffix)
                        var offset int64
                        if err == nil {
                                offset = fi.Size()
-                               state.ctx.LogD(
-                                       "sp-process",
-                                       SdsAdd(sdsp, SDS{"offset": strconv.FormatInt(offset, 10)}),
-                                       "part exists",
-                               )
                        }
-                       replies = append(replies, MarshalSP(
-                               SPTypeFreq,
-                               SPFreq{info.Hash, uint64(offset)},
-                       ))
-               case SPTypeFile:
-                       state.ctx.LogD(
-                               "sp-process",
-                               SdsAdd(sds, SDS{"type": "file"}),
-                               "unmarshaling packet",
+                       if !state.Ctx.IsEnoughSpace(int64(info.Size) - offset) {
+                               state.Ctx.LogI("sp-info", sdsp, "not enough space")
+                               continue
+                       }
+                       state.Ctx.LogI(
+                               "sp-info",
+                               SdsAdd(sdsp, SDS{"offset": strconv.FormatInt(offset, 10)}),
+                               "",
                        )
+                       if !state.listOnly && (state.onlyPkts == nil || state.onlyPkts[*info.Hash]) {
+                               replies = append(replies, MarshalSP(
+                                       SPTypeFreq,
+                                       SPFreq{info.Hash, uint64(offset)},
+                               ))
+                       }
+               case SPTypeFile:
+                       sdsp := SdsAdd(sds, SDS{"type": "file"})
+                       state.Ctx.LogD("sp-process", sdsp, "unmarshaling packet")
                        var file SPFile
                        if _, err = xdr.Unmarshal(r, &file); err != nil {
-                               state.ctx.LogE("sp-process", SdsAdd(sds, SDS{
+                               state.Ctx.LogE("sp-process", SdsAdd(sds, SDS{
                                        "err":  err,
                                        "type": "file",
                                }), "")
                                return nil, err
                        }
-                       sdsp := SdsAdd(sds, SDS{
-                               "xx":   string(TRx),
-                               "hash": ToBase32(file.Hash[:]),
-                               "size": strconv.Itoa(len(file.Payload)),
-                       })
+                       sdsp["xx"] = string(TRx)
+                       sdsp["hash"] = ToBase32(file.Hash[:])
+                       sdsp["size"] = strconv.Itoa(len(file.Payload))
                        filePath := filepath.Join(
-                               state.ctx.Spool,
+                               state.Ctx.Spool,
                                state.Node.Id.String(),
                                string(TRx),
                                ToBase32(file.Hash[:]),
                        )
-                       state.ctx.LogD("sp-file", sdsp, "opening part")
+                       state.Ctx.LogD("sp-file", sdsp, "opening part")
                        fd, err := os.OpenFile(
                                filePath+PartSuffix,
                                os.O_RDWR|os.O_CREATE,
                                os.FileMode(0600),
                        )
                        if err != nil {
-                               state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
+                               state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
                                return nil, err
                        }
-                       state.ctx.LogD(
+                       state.Ctx.LogD(
                                "sp-file",
                                SdsAdd(sdsp, SDS{"offset": strconv.FormatInt(int64(file.Offset), 10)}),
                                "seeking",
                        )
-                       if _, err = fd.Seek(int64(file.Offset), 0); err != nil {
-                               state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
+                       if _, err = fd.Seek(int64(file.Offset), io.SeekStart); err != nil {
+                               state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
                                fd.Close()
                                return nil, err
                        }
-                       state.ctx.LogD("sp-file", sdsp, "writing")
+                       state.Ctx.LogD("sp-file", sdsp, "writing")
                        _, err = fd.Write(file.Payload)
                        if err != nil {
-                               state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
+                               state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
                                fd.Close()
                                return nil, err
                        }
@@ -884,7 +876,7 @@ func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
                        state.RLock()
                        sdsp["fullsize"] = strconv.FormatInt(int64(state.infosTheir[*file.Hash].Size), 10)
                        sdsp["size"] = strconv.FormatInt(int64(ourSize), 10)
-                       state.ctx.LogP("sp-file", sdsp, "")
+                       state.Ctx.LogP("sp-file", sdsp, "")
                        if state.infosTheir[*file.Hash].Size != ourSize {
                                state.RUnlock()
                                fd.Close()
@@ -895,21 +887,21 @@ func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
                        spWorkersGroup.Add(1)
                        go func() {
                                if err := fd.Sync(); err != nil {
-                                       state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "sync")
+                                       state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "sync")
                                        fd.Close()
                                        return
                                }
                                state.wg.Add(1)
                                defer state.wg.Done()
-                               fd.Seek(0, 0)
-                               state.ctx.LogD("sp-file", sdsp, "checking")
+                               fd.Seek(0, io.SeekStart)
+                               state.Ctx.LogD("sp-file", sdsp, "checking")
                                gut, err := Check(fd, file.Hash[:])
                                fd.Close()
                                if err != nil || !gut {
-                                       state.ctx.LogE("sp-file", sdsp, "checksum mismatch")
+                                       state.Ctx.LogE("sp-file", sdsp, "checksum mismatch")
                                        return
                                }
-                               state.ctx.LogI("sp-done", SdsAdd(sdsp, SDS{"xx": string(TRx)}), "")
+                               state.Ctx.LogI("sp-done", SdsAdd(sdsp, SDS{"xx": string(TRx)}), "")
                                os.Rename(filePath+PartSuffix, filePath)
                                state.Lock()
                                delete(state.infosTheir, *file.Hash)
@@ -920,72 +912,69 @@ func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
                                }()
                        }()
                case SPTypeDone:
-                       state.ctx.LogD(
-                               "sp-process",
-                               SdsAdd(sds, SDS{"type": "done"}),
-                               "unmarshaling packet",
-                       )
+                       sdsp := SdsAdd(sds, SDS{"type": "done"})
+                       state.Ctx.LogD("sp-process", sdsp, "unmarshaling packet")
                        var done SPDone
                        if _, err = xdr.Unmarshal(r, &done); err != nil {
-                               state.ctx.LogE("sp-process", SdsAdd(sds, SDS{
+                               state.Ctx.LogE("sp-process", SdsAdd(sds, SDS{
                                        "type": "done",
                                        "err":  err,
                                }), "")
                                return nil, err
                        }
-                       sdsp := SdsAdd(sds, SDS{"hash": ToBase32(done.Hash[:])})
-                       state.ctx.LogD("sp-done", sdsp, "removing")
+                       sdsp["hash"] = ToBase32(done.Hash[:])
+                       state.Ctx.LogD("sp-done", sdsp, "removing")
                        err := os.Remove(filepath.Join(
-                               state.ctx.Spool,
+                               state.Ctx.Spool,
                                state.Node.Id.String(),
                                string(TTx),
                                ToBase32(done.Hash[:]),
                        ))
+                       sdsp["xx"] = string(TTx)
                        if err == nil {
-                               state.ctx.LogI("sp-done", SdsAdd(sdsp, SDS{"xx": string(TTx)}), "")
+                               state.Ctx.LogI("sp-done", sdsp, "")
                        } else {
-                               state.ctx.LogE("sp-done", SdsAdd(sdsp, SDS{"xx": string(TTx)}), "")
+                               state.Ctx.LogE("sp-done", sdsp, "")
                        }
                case SPTypeFreq:
                        sdsp := SdsAdd(sds, SDS{"type": "freq"})
-                       state.ctx.LogD("sp-process", sdsp, "unmarshaling packet")
+                       state.Ctx.LogD("sp-process", sdsp, "unmarshaling packet")
                        var freq SPFreq
                        if _, err = xdr.Unmarshal(r, &freq); err != nil {
-                               state.ctx.LogE("sp-process", SdsAdd(sdsp, SDS{"err": err}), "")
+                               state.Ctx.LogE("sp-process", SdsAdd(sdsp, SDS{"err": err}), "")
                                return nil, err
                        }
-                       state.ctx.LogD("sp-process", SdsAdd(sdsp, SDS{
-                               "hash":   ToBase32(freq.Hash[:]),
-                               "offset": strconv.FormatInt(int64(freq.Offset), 10),
-                       }), "queueing")
+                       sdsp["hash"] = ToBase32(freq.Hash[:])
+                       sdsp["offset"] = strconv.FormatInt(int64(freq.Offset), 10)
+                       state.Ctx.LogD("sp-process", sdsp, "queueing")
                        nice, exists := state.infosOurSeen[*freq.Hash]
                        if exists {
-                               state.Lock()
-                               insertIdx := 0
-                               var freqWithNice *FreqWithNice
-                               for insertIdx, freqWithNice = range state.queueTheir {
-                                       if freqWithNice.nice > nice {
-                                               break
+                               if state.onlyPkts == nil || !state.onlyPkts[*freq.Hash] {
+                                       state.Lock()
+                                       insertIdx := 0
+                                       var freqWithNice *FreqWithNice
+                                       for insertIdx, freqWithNice = range state.queueTheir {
+                                               if freqWithNice.nice > nice {
+                                                       break
+                                               }
                                        }
+                                       state.queueTheir = append(state.queueTheir, nil)
+                                       copy(state.queueTheir[insertIdx+1:], state.queueTheir[insertIdx:])
+                                       state.queueTheir[insertIdx] = &FreqWithNice{&freq, nice}
+                                       state.Unlock()
+                               } else {
+                                       state.Ctx.LogD("sp-process", sdsp, "skipping")
                                }
-                               state.queueTheir = append(state.queueTheir, nil)
-                               copy(state.queueTheir[insertIdx+1:], state.queueTheir[insertIdx:])
-                               state.queueTheir[insertIdx] = &FreqWithNice{&freq, nice}
-                               state.Unlock()
                        } else {
-                               state.ctx.LogD("sp-process", SdsAdd(sdsp, SDS{
-                                       "hash":   ToBase32(freq.Hash[:]),
-                                       "offset": strconv.FormatInt(int64(freq.Offset), 10),
-                               }), "unknown")
+                               state.Ctx.LogD("sp-process", sdsp, "unknown")
                        }
                case SPTypeHalt:
-                       sdsp := SdsAdd(sds, SDS{"type": "halt"})
-                       state.ctx.LogD("sp-process", sdsp, "")
+                       state.Ctx.LogD("sp-process", SdsAdd(sds, SDS{"type": "halt"}), "")
                        state.Lock()
                        state.queueTheir = nil
                        state.Unlock()
                default:
-                       state.ctx.LogE(
+                       state.Ctx.LogE(
                                "sp-process",
                                SdsAdd(sds, SDS{"type": head.Type}),
                                "unknown",
@@ -1002,7 +991,7 @@ func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
                        size += info.Size
                }
                state.RUnlock()
-               state.ctx.LogI("sp-infos", SDS{
+               state.Ctx.LogI("sp-infos", SDS{
                        "xx":   string(TRx),
                        "node": state.Node.Id,
                        "pkts": strconv.Itoa(pkts),
index 14af6b95f6b020f75b229bb5206ef5dbfdac4b43..3d73333a2ed764c9dd1ba38f03767cbc4d6428d5 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
index fa49c169049c9c5882a852e5e99abc6a58834cff..1f717faddedc1c01f4528f8f6ab1e2188a710ec5 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -37,6 +37,7 @@ import (
        "github.com/davecgh/go-xdr/xdr2"
        "github.com/dustin/go-humanize"
        "golang.org/x/crypto/blake2b"
+       "golang.org/x/crypto/poly1305"
 )
 
 const (
@@ -55,7 +56,8 @@ func newNotification(fromTo *FromToYAML, subject string) io.Reader {
 func (ctx *Ctx) Toss(
        nodeId *NodeId,
        nice uint8,
-       dryRun, doSeen, noFile, noFreq, noExec, noTrns bool) bool {
+       dryRun, doSeen, noFile, noFreq, noExec, noTrns bool,
+) bool {
        isBad := false
        for job := range ctx.Jobs(nodeId, TRx) {
                pktName := filepath.Base(job.Fd.Name())
@@ -87,12 +89,18 @@ func (ctx *Ctx) Toss(
                var pkt Pkt
                var err error
                var pktSize int64
+               var pktSizeBlocks int64
                if _, err = xdr.Unmarshal(pipeR, &pkt); err != nil {
                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "unmarshal")
                        isBad = true
                        goto Closing
                }
-               pktSize = job.Size - PktEncOverhead - PktOverhead
+               pktSize = job.Size - PktEncOverhead - PktOverhead - PktSizeOverhead
+               pktSizeBlocks = pktSize / (EncBlkSize + poly1305.TagSize)
+               if pktSize%(EncBlkSize+poly1305.TagSize) != 0 {
+                       pktSize -= poly1305.TagSize
+               }
+               pktSize -= pktSizeBlocks * poly1305.TagSize
                sds["size"] = strconv.FormatInt(pktSize, 10)
                ctx.LogD("rx", sds, "taken")
                switch pkt.Type {
@@ -176,21 +184,31 @@ func (ctx *Ctx) Toss(
                        }
                        if !dryRun {
                                tmp, err := ioutil.TempFile(dir, "nncp-file")
-                               sds["tmp"] = tmp.Name()
-                               ctx.LogD("rx", sds, "created")
                                if err != nil {
                                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "mktemp")
                                        isBad = true
                                        goto Closing
                                }
+                               sds["tmp"] = tmp.Name()
+                               ctx.LogD("rx", sds, "created")
                                bufW := bufio.NewWriter(tmp)
                                if _, err = io.Copy(bufW, pipeR); err != nil {
                                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "copy")
                                        isBad = true
                                        goto Closing
                                }
-                               bufW.Flush()
-                               tmp.Sync()
+                               if err = bufW.Flush(); err != nil {
+                                       tmp.Close()
+                                       ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "copy")
+                                       isBad = true
+                                       goto Closing
+                               }
+                               if err = tmp.Sync(); err != nil {
+                                       tmp.Close()
+                                       ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "copy")
+                                       isBad = true
+                                       goto Closing
+                               }
                                tmp.Close()
                                dstPathOrig := filepath.Join(*incoming, dst)
                                dstPath := dstPathOrig
index 4d49d21857edbad5b6de3d18ec5058ed20b1127e..fb1a29fc829d088db88208445fde2a7419ff4ef8 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
index 0692ecaa66433128026799f3b80d609fc6a7d111..fcf1c83126123649526e54c39bbff7e22f93dcdd 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
@@ -34,6 +34,7 @@ import (
 
        "github.com/davecgh/go-xdr/xdr2"
        "golang.org/x/crypto/blake2b"
+       "golang.org/x/crypto/chacha20poly1305"
 )
 
 func (ctx *Ctx) Tx(
@@ -41,7 +42,8 @@ func (ctx *Ctx) Tx(
        pkt *Pkt,
        nice uint8,
        size, minSize int64,
-       src io.Reader) (*Node, error) {
+       src io.Reader,
+) (*Node, error) {
        tmp, err := ctx.NewTmpFileWHash()
        if err != nil {
                return nil, err
@@ -53,7 +55,11 @@ func (ctx *Ctx) Tx(
                lastNode = ctx.Neigh[*node.Via[i-1]]
                hops = append(hops, lastNode)
        }
-       padSize := minSize - size - int64(len(hops))*(PktOverhead+PktEncOverhead)
+       expectedSize := size
+       for i := 0; i < len(hops); i++ {
+               expectedSize = PktEncOverhead + PktSizeOverhead + sizeWithTags(PktOverhead+expectedSize)
+       }
+       padSize := minSize - expectedSize
        if padSize < 0 {
                padSize = 0
        }
@@ -69,12 +75,11 @@ func (ctx *Ctx) Tx(
                errs <- PktEncWrite(ctx.Self, hops[0], pkt, nice, size, padSize, src, dst)
                dst.Close()
        }(curSize, src, pipeW)
-       curSize += padSize
+       curSize = PktEncOverhead + PktSizeOverhead + sizeWithTags(PktOverhead+curSize) + padSize
 
        var pipeRPrev io.Reader
        for i := 1; i < len(hops); i++ {
                pktTrns, _ := NewPkt(PktTypeTrns, 0, hops[i-1].Id[:])
-               curSize += PktOverhead + PktEncOverhead
                pipeRPrev = pipeR
                pipeR, pipeW = io.Pipe()
                go func(node *Node, pkt *Pkt, size int64, src io.Reader, dst io.WriteCloser) {
@@ -86,6 +91,7 @@ func (ctx *Ctx) Tx(
                        errs <- PktEncWrite(ctx.Self, node, pkt, nice, size, 0, src, dst)
                        dst.Close()
                }(hops[i], pktTrns, curSize, pipeRPrev, pipeW)
+               curSize = PktEncOverhead + PktSizeOverhead + sizeWithTags(PktOverhead+curSize)
        }
        go func() {
                _, err := io.Copy(tmp.W, pipeR)
@@ -116,19 +122,30 @@ func prepareTxFile(srcPath string) (io.Reader, *os.File, int64, error) {
                }
                os.Remove(src.Name())
                tmpW := bufio.NewWriter(src)
-               tmpKey := new([32]byte)
+               tmpKey := make([]byte, chacha20poly1305.KeySize)
                if _, err = rand.Read(tmpKey[:]); err != nil {
                        return nil, nil, 0, err
                }
-               written, err := ae(tmpKey, bufio.NewReader(os.Stdin), tmpW)
+               aead, err := chacha20poly1305.New(tmpKey)
+               if err != nil {
+                       return nil, nil, 0, err
+               }
+               nonce := make([]byte, aead.NonceSize())
+               written, err := aeadProcess(aead, nonce, true, bufio.NewReader(os.Stdin), tmpW)
                if err != nil {
                        return nil, nil, 0, err
                }
                fileSize = int64(written)
-               tmpW.Flush()
-               src.Seek(0, 0)
+               if err = tmpW.Flush(); err != nil {
+                       return nil, nil, 0, err
+               }
+               src.Seek(0, io.SeekStart)
                r, w := io.Pipe()
-               go ae(tmpKey, bufio.NewReader(src), w)
+               go func() {
+                       if _, err := aeadProcess(aead, nonce, false, bufio.NewReader(src), w); err != nil {
+                               panic(err)
+                       }
+               }()
                reader = r
        } else {
                src, err = os.Open(srcPath)
@@ -190,7 +207,8 @@ func (ctx *Ctx) TxFileChunked(
        nice uint8,
        srcPath, dstPath string,
        minSize int64,
-       chunkSize int64) error {
+       chunkSize int64,
+) error {
        if dstPath == "" {
                if srcPath == "-" {
                        return errors.New("Must provide destination filename")
@@ -365,7 +383,8 @@ func (ctx *Ctx) TxExec(
        handle string,
        args []string,
        body []byte,
-       minSize int64) error {
+       minSize int64,
+) error {
        path := make([][]byte, 0, 1+len(args))
        path = append(path, []byte(handle))
        for _, arg := range args {
index 6ea7ef2cb7b9613c4e78fe7020f9c6420af794c7..4bfccfcc5fd25f44bde984e5e666167e2c18b399 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
diff --git a/src/cypherpunks.ru/nncp/vendor/cypherpunks.ru/balloon b/src/cypherpunks.ru/nncp/vendor/cypherpunks.ru/balloon
new file mode 160000 (submodule)
index 0000000..0e07700
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 0e07700b027907d44e0060fc3f75c4590478452b
diff --git a/src/cypherpunks.ru/nncp/vendor/github.com/dustin/go-humanize b/src/cypherpunks.ru/nncp/vendor/github.com/dustin/go-humanize
new file mode 160000 (submodule)
index 0000000..9f541cc
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 9f541cc9db5d55bce703bd99987c9d5cb8eea45e
diff --git a/src/cypherpunks.ru/nncp/vendor/golang.org/x/crypto b/src/cypherpunks.ru/nncp/vendor/golang.org/x/crypto
new file mode 160000 (submodule)
index 0000000..a29dc8f
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit a29dc8fdc73485234dbef99ebedb95d2eced08de
diff --git a/src/cypherpunks.ru/nncp/vendor/golang.org/x/net b/src/cypherpunks.ru/nncp/vendor/golang.org/x/net
new file mode 160000 (submodule)
index 0000000..4829fb1
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 4829fb13d2c62012c17688fa7f629f371014946d
diff --git a/src/cypherpunks.ru/nncp/vendor/golang.org/x/sys b/src/cypherpunks.ru/nncp/vendor/golang.org/x/sys
new file mode 160000 (submodule)
index 0000000..a129542
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit a129542de9ae0895210abff9c95d67a1f33cb93d
diff --git a/src/cypherpunks.ru/nncp/vendor/gopkg.in/check.v1 b/src/cypherpunks.ru/nncp/vendor/gopkg.in/check.v1
new file mode 160000 (submodule)
index 0000000..788fd78
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 788fd78401277ebd861206a03c884797c6ec5541
diff --git a/src/cypherpunks.ru/nncp/vendor/gopkg.in/yaml.v2 b/src/cypherpunks.ru/nncp/vendor/gopkg.in/yaml.v2
new file mode 160000 (submodule)
index 0000000..7b8349a
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 7b8349ac747c6a24702b762d2c4fd9266cf4f1d6
index f6f3e98f959697e855db8f200b5e0a89db8a80d4..0868b76062df06be010036efd33c7441bdb8af4c 100644 (file)
@@ -1,6 +1,6 @@
 /*
 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 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
diff --git a/src/github.com/dustin/go-humanize b/src/github.com/dustin/go-humanize
deleted file mode 160000 (submodule)
index 02af396..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 02af3965c54e8cacf948b97fef38925c4120652c
diff --git a/src/golang.org/x/crypto b/src/golang.org/x/crypto
deleted file mode 160000 (submodule)
index a49355c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit a49355c7e3f8fe157a85be2f77e6e269a0f89602
diff --git a/src/golang.org/x/net b/src/golang.org/x/net
deleted file mode 160000 (submodule)
index 32a936f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 32a936f46389aa10549d60bd7833e54b01685d09
diff --git a/src/golang.org/x/sys b/src/golang.org/x/sys
deleted file mode 160000 (submodule)
index 3c6ecd8..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 3c6ecd8f22c6f40fbeec94c000a069d7d87c7624
diff --git a/src/gopkg.in/check.v1 b/src/gopkg.in/check.v1
deleted file mode 160000 (submodule)
index 20d25e2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 20d25e2804050c1cd24a7eea1e7a6447dd0e74ec
diff --git a/src/gopkg.in/yaml.v2 b/src/gopkg.in/yaml.v2
deleted file mode 160000 (submodule)
index 5420a8b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5420a8b6744d3b0345ab293f6fcba19c978f1183