BSDmakefile | 1 - GNUmakefile | 1 - VERSION | 2 +- common.mk | 69 +++++++++++------------------------------------------ doc/about.ru.texi | 2 ++ doc/cmds.texi | 41 +++++++++++++++++++++++++---------------- doc/download.texi | 45 ++++++++++++++++++++++++--------------------- doc/eblob.texi | 29 +++++++++++++---------------- doc/index.texi | 2 +- doc/integrity.texi | 1 - doc/news.ru.texi | 28 ++++++++++++++++++++++++++++ doc/news.texi | 27 +++++++++++++++++++++++++++ doc/pkt.texi | 47 ++++++++++++++++++++++------------------------- doc/style.css | 3 ++- doc/usecases.ru.texi | 84 +++++++++++++++++++++++++++++++++++++++++++++++++---- doc/usecases.texi | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++--- makedist.sh | 65 +++++++++++++++++++++++++++++++---------------------- ports/nncp/Makefile | 2 +- ports/nncp/pkg-descr | 2 +- src/cypherpunks.ru/nncp/call.go | 31 ++++++++++++++++++------------- src/cypherpunks.ru/nncp/cfg.go | 12 ++++++------ src/cypherpunks.ru/nncp/check.go | 2 +- src/cypherpunks.ru/nncp/chunked.go | 2 +- src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go | 10 ++++++---- src/cypherpunks.ru/nncp/cmd/nncp-call/main.go | 47 +++++++++++++++++++++++++++++++++-------------- src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go | 6 ++++-- src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go | 4 ++-- src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go | 2 +- src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go | 2 +- src/cypherpunks.ru/nncp/cmd/nncp-check/main.go | 8 +++++--- src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go | 13 ++++++++----- src/cypherpunks.ru/nncp/cmd/nncp-exec/main.go | 4 ++-- src/cypherpunks.ru/nncp/cmd/nncp-file/main.go | 4 ++-- src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go | 4 ++-- src/cypherpunks.ru/nncp/cmd/nncp-log/main.go | 4 ++-- src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go | 20 +++++++++++++++----- src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go | 26 +++++++++++++++++--------- src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go | 4 ++-- src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go | 4 ++-- src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go | 4 ++-- src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go | 35 ++++++++++++++++++++++------------- src/cypherpunks.ru/nncp/ctx.go | 13 ++++++++++++- src/cypherpunks.ru/nncp/eblob.go | 93 ++++++++++++++++++++--------------------------------- src/cypherpunks.ru/nncp/go.mod | 15 +++++++++++++++ src/cypherpunks.ru/nncp/go.sum | 25 +++++++++++++++++++++++++ src/cypherpunks.ru/nncp/humanizer.go | 29 ++++++++++++++++++++++++++++- src/cypherpunks.ru/nncp/jobs.go | 7 ++++--- src/cypherpunks.ru/nncp/lockdir.go | 2 +- src/cypherpunks.ru/nncp/log.go | 2 +- src/cypherpunks.ru/nncp/nncp.go | 4 ++-- src/cypherpunks.ru/nncp/node.go | 2 +- src/cypherpunks.ru/nncp/pkt.go | 198 ++++++++++++++++++++++------------------------------- src/cypherpunks.ru/nncp/pkt_test.go | 4 ++-- src/cypherpunks.ru/nncp/sp.go | 413 ++++++++++++++++++++++++++--------------------------- src/cypherpunks.ru/nncp/tmp.go | 2 +- src/cypherpunks.ru/nncp/toss.go | 32 +++++++++++++++++++++++++------- src/cypherpunks.ru/nncp/toss_test.go | 2 +- src/cypherpunks.ru/nncp/tx.go | 43 +++++++++++++++++++++++++++++++------------ src/cypherpunks.ru/nncp/tx_test.go | 2 +- src/cypherpunks.ru/nncp/via.go | 2 +- diff --git a/BSDmakefile b/BSDmakefile index 148fb82f8f709ad6be51726f7ce6424f6cdd78de884166fd80ee43c752a4cc00..c9cd663e8c3f32f851b25f1b5d9df2abd244b965e99d83b74d4c6129b5728d16 100644 --- a/BSDmakefile +++ b/BSDmakefile @@ -1,4 +1,3 @@ -GOPATH != pwd VERSION != cat VERSION include common.mk diff --git a/GNUmakefile b/GNUmakefile index aa473484fbdce0ba0f0a563adfcf34bbc8883a56f4f1f0a0ef036fb6fe106229..4c73fbf6b76de756957722c0bd77ccd3c74a681ea13fe3c081491614d169bcbb 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,4 +1,3 @@ -GOPATH = $(shell pwd) VERSION = $(shell cat VERSION) include common.mk diff --git a/VERSION b/VERSION index 201baede28f0e6e68178dea6552eff49010d545c454f0a8100f412f4e5507892..dddd2b6fe8e64351ebd48d13898cd77b3dd21803c72f8a028b265ed6c316055f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.4 +4.0 diff --git a/common.mk b/common.mk index d78edabb252d4afc25db08db07b4c3cbcddc8e955f1141ad5daaf420e34a1055..32bcbe138dc2fd48c2400a24d01ff3ee6be4ed2d21ce70e68626cc87036c8782 100644 --- 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 @@ -X cypherpunks.ru/nncp.DefaultCfgPath=$(CFGPATH) \ -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 @@ nncp-stat \ nncp-toss \ nncp-xfer -all: $(ALL) +SRC := $(PWD)/src/cypherpunks.ru/nncp +BIN := $(PWD)/bin +GOPATH ?= $(PWD)/gopath -nncp-bundle: - GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-bundle +all: $(ALL) -nncp-call: - GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-call +$(BIN): + mkdir -p $(BIN) -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 - -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 - -nncp-toss: - GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-toss - -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 @@ $(MAKE) -C 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) diff --git a/doc/about.ru.texi b/doc/about.ru.texi index ef87089883323f791c508c45ad00f4675333372449ce45406e8d6b6b38d55b35..a0f8b7089ab5c9fab059716d12828afcee766b1486c1ccfcedf696392b633ddf 100644 --- a/doc/about.ru.texi +++ b/doc/about.ru.texi @@ -1,6 +1,8 @@ @node Об утилитах @section Подробнее об утилитах NNCP +@verbatiminclude pedro.txt + @strong{NNCP} (Node to Node copy) это набор утилит упрощающий безопасный обмен файлами, почтой и командами в режиме сохранить-и-переслать. diff --git a/doc/cmds.texi b/doc/cmds.texi index faab2ef4574c7145a4f3855f229d20089a5ebb3c818f2f43bc784a2caf2b240e..a60938daf1dbb12342ebe18ffdd108f2160cd58d62440c23b8e6b59817a3f17a 100644 --- a/doc/cmds.texi +++ b/doc/cmds.texi @@ -20,8 +20,6 @@ Set desired outgoing packet @ref{Niceness, niceness level}. @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 @@ % nncp-call [options] [-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 @@ 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 @@ @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 @@ -381,6 +387,8 @@ And with the @option{-dump} option it will give you the actual payload (the whole file, mail message, and so on). @option{-decompress} option 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 @@ @node nncp-stat @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 @@ @ref{nncp-bundle}, @ref{nncp-daemon} and @ref{nncp-call} commands skip 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 diff --git a/doc/download.texi b/doc/download.texi index a52d1b4d2d8e1210fa8781f976f3aabd8fed9a5a35d7e462e15d3283f176fca9..715c00e7dca689ef8e9377c31d8668dddf5839145882d5b47c0201a6eee3b079 100644 --- a/doc/download.texi +++ b/doc/download.texi @@ -20,79 +20,82 @@ @item @code{golang.org/x/net} @tab BSD 3-Clause @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} diff --git a/doc/eblob.texi b/doc/eblob.texi index 7c4ae400d39b4ea4f17981e6d3cb1a79ff542bc17c2c33af4dc199adbf16f1ea..077e14f4d2376dfe398fcf4ead21a569a73ffcd25352a005597121d46d9d22b8 100644 --- a/doc/eblob.texi +++ b/doc/eblob.texi @@ -32,16 +32,16 @@ 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 @@ 32 bytes, fixed length opaque data @tab 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 diff --git a/doc/index.texi b/doc/index.texi index c0ff5e7a24c85180ea80236ded89c0b4dfae8a6907017a1ba48f0d2b6aaf57e1..a5ba6e1a7a85eb8d4b2fbea63c2ef60555749af7c893f1b6f4b31a25d01573a6 100644 --- a/doc/index.texi +++ b/doc/index.texi @@ -6,7 +6,7 @@ @copying 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 diff --git a/doc/integrity.texi b/doc/integrity.texi index 409df9651861c4cc7aed1362fc09698e26b0f995bb22f556b29d95ad3910559f..371677601807eaf0ae233077e21479857b27a6469c1a0f77d5df76835993ef4d 100644 --- a/doc/integrity.texi +++ b/doc/integrity.texi @@ -23,7 +23,6 @@ @verbatim % 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 diff --git a/doc/news.ru.texi b/doc/news.ru.texi index 6f820e37fbe6962f5a999c61d538bde400e06f6ad1e5418e7eda35df48c70259..61e6fa17a6970c7e0a72181c567c1601eea00d0865483375bf5a3a2cb3f28119 100644 --- a/doc/news.ru.texi +++ b/doc/news.ru.texi @@ -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 diff --git a/doc/news.texi b/doc/news.texi index 925a396dc99cb37c16fca90ff142397f5397e84d1ce00f122c123b07bd1a21f0..0088aa0e00a8b1550849d880c9434b1a24d7ceb4a9917943730050dae96c6c1c 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -3,6 +3,33 @@ @unnumbered News 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 diff --git a/doc/pkt.texi b/doc/pkt.texi index f269ab8634205703b90496932652fa652d7a2e5fb5d803465004c3d187977ccf..a25082ef8c80556be8a0df29b2cb192b73fe3fa662582d42a374891b3dc55a67 100644 --- a/doc/pkt.texi +++ b/doc/pkt.texi @@ -28,7 +28,7 @@ @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 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 @@ 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 @@ @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 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 @@ @end multitable 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 @@ unsigned hyper integer @tab 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 @@ private ephemeral one @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 diff --git a/doc/style.css b/doc/style.css index 228b076e684e52cfa41bd3d7376be26fa55bf0cba471ad7c589778c876947ddf..7cfb55f8ad0828d46db2d8ab86aec767541141dedbdb0a821ba846b195309e85 100644 --- a/doc/style.css +++ b/doc/style.css @@ -1,10 +1,11 @@ diff --git a/doc/usecases.ru.texi b/doc/usecases.ru.texi index c4797772fad78185aa01545dfe5b64a056f6bf3b382e87903b7e3f69c752c8a3..8bdf213c97c13f7cf76a754c983e595a880b2982b0a33242dd9baa196c9b268e 100644 --- a/doc/usecases.ru.texi +++ b/doc/usecases.ru.texi @@ -8,10 +8,12 @@ * Ненадёжный/дорогой канал связи: UsecaseUnreliableRU. * Медленная/дорогая связь для больших объёмов данных, плохой QoS: UsecaseQoSRU. * Экстремальные наземные окружающие условия, нет связи: UsecaseNoLinkRU. * Односторонняя широковещательная связь: UsecaseBroadcastRU. +* Спутниковые каналы связи: UsecaseSatelliteLinksRU. * Частные, изолированные MitM/Sybil-устойчивые сети: UsecaseF2FRU. * Высоко защищённые изолированные компьютеры с воздушным зазором: UsecaseAirgapRU. * Обход сетевой цензуры, здоровье: UsecaseCensorRU. * Разведка, шпионаж, тайная агентура: UsecaseSpyRU. +* Дешёвая ночная связь: UsecaseCallerRU. @end menu @node UsecaseMailRU @@ -104,9 +106,9 @@ раньше или позднее остальных. Почти все команды имеют соответствующую опцию: @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 @@ Встроенная возможность определять дубляжи пакетов позволит вам переотправлять широковещательные рассылки время от времени, повышая шансы на то, что получатель примет их, регулярно слушая рассылку. +@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 @@ @subsection Разведка, шпионаж, тайная агентура Эти ребята знают насколько небезопасен Интернет, несовместим с понятием приватности. Им необходим быстрый сброс и забор данных. Нет -возможности провести несколько итераций туда-обратно (round trip) -- +возможности провести несколько итераций приёмо-передач (round-trips) -- только сбросить данные, выстрелить и забыть. Опять же, это может быть переносной накопитель и/или @url{https://en.wikipedia.org/wiki/USB_dead_drop, USB тайник} (dead drop), @@ -316,7 +346,7 @@ видно. Общение узлов между собой происходит в, так называемой, @ref{Spool, спул} области: директории содержащей только необработанные зашифрованные -пакеты. После передачи пакета вы всё-равно не сможете его прочитать: +пакеты. После передачи пакета вы всё равно не сможете его прочитать: необходимо запустить другую фазу: @ref{nncp-toss, распаковку}, которая использует ваши приватные криптографические ключи. То есть, даже если вы потеряете свой компьютер, устройства хранения и тому прочее -- это не @@ -326,3 +356,47 @@ Распаковка (чтение этих зашифрованных пакетов с извлечением переданных файлов и почтовых сообщений) может и должна бы быть произведена на отдельном компьютере (@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 diff --git a/doc/usecases.texi b/doc/usecases.texi index 186240bc1de144d674b5670b801cc676294e5c8d7c6b8c7934b06c11e849809f..39c444cc04b23e821393dc60eb51080bce564a8c40f1166268d5ae4c9dea35d8 100644 --- a/doc/usecases.texi +++ b/doc/usecases.texi @@ -10,10 +10,12 @@ * Unreliable/expensive communication link: UsecaseUnreliable. * 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 diff --git a/makedist.sh b/makedist.sh index 2989fd3d1b283cc5a441596bda4e5ddbc630c743fda58f1b174a6e89bcd997db..f548b3bb659ed8c2cc8e16821b2e338720e56c2352542bdd6175be33a9f139b0 100755 --- a/makedist.sh +++ b/makedist.sh @@ -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 < +Copyright (C) 2016-2019 Sergey Matveev 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 @@ addrs []string, 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 @@ ctx.LogD("call", SdsAdd(sds, SDS{"err": err}), "dialing") 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{ diff --git a/src/cypherpunks.ru/nncp/cfg.go b/src/cypherpunks.ru/nncp/cfg.go index dd81ceae4caa057e7cd7d1deeeb9652407c7eb8822232d28ba2e96300432cb70..c2005d8f13cc0e1f695f16c95c9c6021e7fb54366975c55e7dcc335921ea98fe 100644 --- a/src/cypherpunks.ru/nncp/cfg.go +++ b/src/cypherpunks.ru/nncp/cfg.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ default: 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 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 { diff --git a/src/cypherpunks.ru/nncp/check.go b/src/cypherpunks.ru/nncp/check.go index a2d1b6e942a5aebac3a84f5948901e74acbb3697af03626bd7b4ada7bb83a9a2..6eae1d08364aaf7f6f70a4dadc13aa3ff9ead37d50f6eb5f925792e4f033464b 100644 --- a/src/cypherpunks.ru/nncp/check.go +++ b/src/cypherpunks.ru/nncp/check.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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/chunked.go b/src/cypherpunks.ru/nncp/chunked.go index c6f8190b9d99a922469c102c1fcc06795304c06dd16d36a6309f5fdde9033f51..3f293ec6157c6ec92f6ee0f3e7cc8b47a80e2044db2869c47ae7ec92d6f76aa0 100644 --- a/src/cypherpunks.ru/nncp/chunked.go +++ b/src/cypherpunks.ru/nncp/chunked.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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/cmd/nncp-bundle/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go index 0e524d15619d6b66b4e327e7651f3cba2664a9f5b866d0aee8726debf5705578..dcc86c2bade2d0651f95f2f169cff92001468ae5e3044cb4e88422c92075bce9 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Create/digest stream of NNCP encrypted packets +// Create/digest stream of NNCP encrypted packets. package main import ( @@ -236,7 +236,7 @@ if _, err = xdr.Unmarshal(bytes.NewReader(pktEncBuf), &pktEnc); err != nil { 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 @@ } 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) diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go index 37524822be7cf60658c46d1ab9c99b76bd207d148cdbf3226b2bc7fa02915e01..af15fae979463e0b6b5bd80a75f9ed70b72f3cf1d87c3f1344b45cc8bb5d1867 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Call NNCP TCP daemon +// Call NNCP TCP daemon. package main import ( @@ -39,18 +39,20 @@ } 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 @@ addrs = append(addrs, addr) } } + 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 @@ *rxRate, *txRate, *onlineDeadline, *maxOnlineTime, + *listOnly, + onlyPkts, ) { os.Exit(1) } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go index 47a73070666d3789fb41c229c7dc768a0222ccae9a8cdc96847a7d6bc15abd3c..7b5fee13453e13a0e81d9a720f75461530e3795b3d31bff28a4a2732960adc68 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Croned NNCP TCP daemon caller +// Croned NNCP TCP daemon caller. package main import ( @@ -132,6 +132,8 @@ call.RxRate, call.TxRate, call.OnlineDeadline, call.MaxOnlineTime, + false, + nil, ) node.Lock() node.Busy = false diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go index fa4dacad8f72688c744c519b3087b062d4320068492b2cedcd91167db38fb347..b2813035dce350b9748957487870c644f8aeb8ee1ee75fce92e0c474664189e5 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ var eblob nncp.EBlob 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") diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go index 845f2218f2c401e37110d4ea8822a05c68f794beeafd17713799ccdd7cde4dd3..ea3fbd6e45716519c27bd9f47f8a940e8214a3825558296eed0e9b2d5dc0af98 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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/cmd/nncp-cfgnew/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go index 7db9060f298d504c5a5382264521c5b6b55e3b3f0d3e837f955fd0988337da36..24156f793d5f93967de471b984c9c6351635d175f292912929a93a93fd61ab77 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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/cmd/nncp-check/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-check/main.go index 29c1ff4ea74a102236d7a817fd338726bb3554f48818846f93017893e34b2ede..e03f4c339cf16ff7ce96db4266062557fc9dc39a9e0d4eae3acf55c1cf6df308 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-check/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-check/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Verify NNCP Rx/Tx packets checksum +// Verify NNCP Rx/Tx packets checksum. package main import ( @@ -75,7 +75,9 @@ for nodeId, node := range ctx.Neigh { 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) diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go index 710fa233dec13b57ceda7b85e711d46bb7109556f46fc83ab7478fcc10219453..fe8068705730bf1d7d5f541dad29834cdd12d4e4ed2a18661a5c55c88123a138 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// NNCP TCP daemon +// NNCP TCP daemon. package main import ( @@ -61,8 +61,11 @@ return ic.w.SetWriteDeadline(t) } 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 @@ "txspeed": strconv.FormatInt(state.TxSpeed, 10), }, "") } 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}, "") diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-exec/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-exec/main.go index de1c06bad18f8d576e7fb6fa0d7c67d14aed0fbfa621f9748338dbed63047355..633c190ca7b25f2df4e417895728fe71ba2041f15f287d056d132bc763d03b22 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-exec/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-exec/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Send execution command via NNCP +// Send execution command via NNCP. package main import ( diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go index b554dd1e35d3041b2b00fd953e7f44bbce3c71aac72e795dcb365f241c0e7e0c..d3ecc69d88bb3494cfb37e5f1c4397e047108341329a16a822e0d3287e06bccb 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Send file via NNCP +// Send file via NNCP. package main import ( diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go index 4d9e64fc46318141363ebd63175670de1db3a76ff543934bb7624202a141921c..ca311ab4f2ead1d2146bc0dade8fc22b7abdabf4778b0b830f95fe78876160f7 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Send file request via NNCP +// Send file request via NNCP. package main import ( diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-log/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-log/main.go index b099be28957182276d7f7966169fe21731e22537e6eb38cd4f4a637ed11210eb..3f73bf778cf3e236dae424a1ac61d1aa442f90a2eae5df111d197721360cdcd3 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-log/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-log/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Read NNCP logs +// Read NNCP logs. package main import ( diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go index 3d9a8c89cd60e0a046900d2a8aeadbc6e32e74d7a3b1a462e22125c157316b15..abb04be7c8f374e9e1430cd9f5ea8876d93a25198e1ab5bd946ef4a5dfce2e15 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Parse raw NNCP packet +// Parse raw NNCP packet. package main import ( @@ -31,7 +31,6 @@ "os" "cypherpunks.ru/nncp" "github.com/davecgh/go-xdr/xdr2" - "golang.org/x/crypto/blake2b" ) func usage() { @@ -44,6 +43,7 @@ } 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 @@ fmt.Println(nncp.VersionGet()) 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 @@ return } 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 { diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go index a3bdda7e04865333c9a12e17791f58ecaac6b6d8c23b0aa66f0d81558e6ae027..636da54533599055d7262ccc7d8afc816e87904035b3bb1544e1460c3b90274c 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Send file via NNCP +// Reassembly chunked file. package main import ( @@ -203,9 +203,13 @@ hasErrors = true } } } - 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 @@ os.Exit(1) } 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 @@ for _, metaPath := range findMetas(ctx, *node.Incoming) { 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 @@ if nodeOnly.Incoming == nil { 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 { diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go index 226613f85aadf10244f4e933c4fca2aa3b07f2bc53c261ca3615458d1cff9262..708f9ca5478e7d922ec1f1bc19ea2b91a6f167bac2d26c58380bf8dbc5a54e85 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Remove packet from the queue +// Remove packet from the queue. package main import ( diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go index 89bf5899272e878e1d55c1c5ff3fe91fe26ed3f7ddd9c3303701ac957230c25e..41fd3a100631378d0ece93b2cb8840d6b7f4fe0adbcb142456d451b619038dd9 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Show queued NNCP Rx/Tx stats +// Show queued NNCP Rx/Tx stats. package main import ( diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go index c57062ebf4f34615a0114d44b3cef2b54ef8cd20f9f6eacdffb377f199f00a48..33fca24c1e2ffebcfdc023af85120037882df9e60101d245e6c8471975a09252 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Process inbound NNCP packets +// Process inbound NNCP packets. package main import ( diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go index 3b017bb5b768062294164329d14ca4b71536c1f33c284a8f2d00d0d7982e962c..3ee59490dd70875cdd5625d105a01a57fe2ee9bab12157f25f0c04b2bcfa63bc 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 . */ -// Copy NNCP inbound and outbounds packets +// Exchange NNCP inbound and outbounds packets with external directory. package main import ( @@ -162,6 +162,7 @@ continue } 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 @@ continue } 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 @@ ctx.LogD("nncp-xfer", sds, "too nice") 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 @@ string(nncp.TRx), )); 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 @@ tmp.Close() 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 diff --git a/src/cypherpunks.ru/nncp/ctx.go b/src/cypherpunks.ru/nncp/ctx.go index fae4d3f1ead908398593b6e7db1ab133590d395a8ca0d67deb3a513a1597b9a7..0c292545c690e9e6f1d7afdd808990fd62808e4ae23db5b3eba2a1090e32ddbc 100644 --- a/src/cypherpunks.ru/nncp/ctx.go +++ b/src/cypherpunks.ru/nncp/ctx.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ import ( "errors" "io/ioutil" + "log" "os" "path/filepath" + + "golang.org/x/sys/unix" ) type Ctx struct { @@ -103,3 +106,11 @@ ctx.Quiet = quiet 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 +} diff --git a/src/cypherpunks.ru/nncp/eblob.go b/src/cypherpunks.ru/nncp/eblob.go index 5ba76923636b60d80aa2bcf65506fdcc9ea95bc0c7cfd87f23bdd215b851712c..6b63645804df4a8e1fa4f8dadfa5442f3ce6d179a5ebab16a6341bd92c2e4bf4 100644 --- a/src/cypherpunks.ru/nncp/eblob.go +++ b/src/cypherpunks.ru/nncp/eblob.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ 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 @@ DefaultP = 2 ) 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 @@ TCost uint32 PCost uint32 Salt *[32]byte Blob []byte - MAC *[blake2b.Size256]byte } func blake256() hash.Hash { @@ -69,46 +65,31 @@ var err 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 + eblob := EBlob{ + Magic: MagicNNCPBv3, + SCost: uint32(sCost), + TCost: uint32(tCost), + PCost: uint32(pCost), + Salt: salt, + Blob: nil, } - if _, err = kdf.Write(MagicNNCPBv2[:]); err != nil { + var eblobBuf bytes.Buffer + if _, err = xdr.Marshal(&eblobBuf, &eblob); 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) + key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost) + aead, err := chacha20poly1305.New(key) if err != nil { return nil, err } - chacha20.XORKeyStream(data, data, new([16]byte), keyEnc) - if _, err = mac.Write(data); err != nil { + 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 } - macTag := new([blake2b.Size256]byte) - mac.Sum(macTag[:0]) - eblob := EBlob{ - Magic: MagicNNCPBv2, - SCost: uint32(sCost), - TCost: uint32(tCost), - PCost: uint32(pCost), - Salt: salt, - Blob: data, - MAC: macTag, - } - var eblobRaw bytes.Buffer - if _, err = xdr.Marshal(&eblobRaw, &eblob); err != nil { - return nil, err - } - return eblobRaw.Bytes(), nil + return eblobBuf.Bytes(), nil } func DeEBlob(eblobRaw, password []byte) ([]byte, error) { @@ -117,7 +98,7 @@ var err 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 @@ int(eblob.SCost), 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 index 0000000000000000000000000000000000000000..c39492e9c56f686e78dced827a11c860fb82381fb1b53e22a6cfcf5098d32cbb --- /dev/null +++ b/src/cypherpunks.ru/nncp/go.mod @@ -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 index 0000000000000000000000000000000000000000..3cf147169a2e914e6bb192b9698d013a54af600d5d565930627874061402e188 --- /dev/null +++ b/src/cypherpunks.ru/nncp/go.sum @@ -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= diff --git a/src/cypherpunks.ru/nncp/humanizer.go b/src/cypherpunks.ru/nncp/humanizer.go index d6f41e3f9a6e6e80ec107f9d52e0723857ffe4685836c8a32e6812bfac87ed86..e78569fdf25ffda78552df33b81dc2aea9b770a3ff9cd0c4cde9ececf27408f2 100644 --- a/src/cypherpunks.ru/nncp/humanizer.go +++ b/src/cypherpunks.ru/nncp/humanizer.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ msg += fmt.Sprintf(" (%s)", size) } if err, exists := sds["err"]; exists { msg += ": " + err + } else { + msg += " " + rem } case "nncp-bundle": switch sds["xx"] { @@ -196,6 +198,29 @@ nodeS, 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 @@ default: 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": diff --git a/src/cypherpunks.ru/nncp/jobs.go b/src/cypherpunks.ru/nncp/jobs.go index 3313bfc7e350fce557d69b7d272ebd6c1891b51f8f729d0db8e0b45f0a4612a1..4d3ddd39b0525841e3bac6901f8cbfdd61c2ed0af2eaf78613fc37e176f56a2c 100644 --- a/src/cypherpunks.ru/nncp/jobs.go +++ b/src/cypherpunks.ru/nncp/jobs.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ package nncp import ( + "io" "os" "path/filepath" "strconv" @@ -64,11 +65,11 @@ if err != nil { 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, diff --git a/src/cypherpunks.ru/nncp/lockdir.go b/src/cypherpunks.ru/nncp/lockdir.go index ec21c44ca509a09b52dd1093602da6feb87d1d172e3e190d2853539ad94d056f..7158dfcceba3627b341aecdbcc36daebfcf35c6e648379459683e539143a8bf9 100644 --- a/src/cypherpunks.ru/nncp/lockdir.go +++ b/src/cypherpunks.ru/nncp/lockdir.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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/log.go b/src/cypherpunks.ru/nncp/log.go index c3b431b216fe0ee1e6ea5790d486cd04ff3c1f7aede29cef8899ada6ec2e93bc..42a2228a7288bff2a432d1a595878d936e46bca4941f8ffcc18e0b99e6e96bd9 100644 --- a/src/cypherpunks.ru/nncp/log.go +++ b/src/cypherpunks.ru/nncp/log.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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/nncp.go b/src/cypherpunks.ru/nncp/nncp.go index 25e51e3d5a622616c4af1817bd35867709bf46b16b3aedfff522a7a79e7a01b1..1e6edfa286b3ea0de506c99f5034e2c8c30335d648d481486ed8770267c3abf1 100644 --- a/src/cypherpunks.ru/nncp/nncp.go +++ b/src/cypherpunks.ru/nncp/nncp.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 UsageHeader() string { return VersionGet() + ` -Copyright (C) 2016-2017 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. diff --git a/src/cypherpunks.ru/nncp/node.go b/src/cypherpunks.ru/nncp/node.go index 1fa20ab8fdbe0812fb6a1ec23e369ef256a2b7c950730e71ff465b99cc5a671c..5b5417253dd2dc7667bb2abce8aa8f7bb8f484725679915b72b86f97eb43a857 100644 --- a/src/cypherpunks.ru/nncp/node.go +++ b/src/cypherpunks.ru/nncp/node.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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/pkt.go b/src/cypherpunks.ru/nncp/pkt.go index 7c057ab98fed5d90775b8640c0f2693645b47e6dd8c90a18505a5153c9f24999..4085a5856a8553d0de50834500cee6181c40bd01b26b09a923006e49d30e3868 100644 --- a/src/cypherpunks.ru/nncp/pkt.go +++ b/src/cypherpunks.ru/nncp/pkt.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ ) 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 @@ n, err := xdr.Marshal(&buf, pkt) 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 @@ if err != nil { panic(err) } pktEnc := PktEnc{ - Magic: MagicNNCPEv3, + Magic: MagicNNCPEv4, Nice: 123, Sender: dummyId, Recipient: dummyId, @@ -132,42 +134,60 @@ copy(pkt.Path[:], path) 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 @@ pkt *Pkt, 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 @@ if _, err := xdr.Marshal(&pktBuf, pkt); err != nil { return err } tbs := PktTbs{ - Magic: MagicNNCPEv3, + Magic: MagicNNCPEv4, Nice: nice, Sender: our.Id, Recipient: their.Id, @@ -200,7 +221,7 @@ } 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 @@ kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:]) 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 { - return 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 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 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 @@ kdf, err := blake2b.NewXOF(KDFXOFSize, sharedKey[:]) 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 { + key := make([]byte, chacha20poly1305.KeySize) + if _, err = io.ReadFull(kdf, key); err != nil { return their, 0, err } - keyAuth := make([]byte, 64) - if _, err = io.ReadFull(kdf, keyAuth); 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 { + sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, nil) + if 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) - if err != nil { - return their, 0, err - } - - 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 } diff --git a/src/cypherpunks.ru/nncp/pkt_test.go b/src/cypherpunks.ru/nncp/pkt_test.go index aedb7d10b117f01654e127a6f45558b09da1398b05cafdf8fedc65d48664d5a9..f006b07f6f72e9594f264c34c91c28d036edb6e4af63d0bc94a787a012d60161 100644 --- a/src/cypherpunks.ru/nncp/pkt_test.go +++ b/src/cypherpunks.ru/nncp/pkt_test.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ } if *node.Id != *node1.Id { return false } - if sizeGot != int64(size) { + if sizeGot != sizeWithTags(PktOverhead+int64(size)) { return false } var pktBuf bytes.Buffer diff --git a/src/cypherpunks.ru/nncp/sp.go b/src/cypherpunks.ru/nncp/sp.go index d92302b2875b2641f3ee5dd5ef6bb10712a485e9c9704015657d23fd54b8ff16..43941c7678b24832d081168625933eb07c1ab6e13bdb1e497bbe5f5eb0a58a21 100644 --- a/src/cypherpunks.ru/nncp/sp.go +++ b/src/cypherpunks.ru/nncp/sp.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ return outbounds } 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 @@ xxOnly TRxTx rxRate int txRate int isDead bool + listOnly bool + onlyPkts map[[32]byte]bool sync.RWMutex } @@ -213,8 +215,8 @@ uint(now.Sub(state.TxLastSeen).Seconds()) >= state.onlineDeadline } 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 @@ } 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 @@ var payload []byte 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 @@ for i := 0; i < (MaxSPSize-len(firstPayload))/SPHeadOverhead; i++ { 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 @@ state.payloads <- payload } }() } - 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 @@ state.payloads <- reply } }() - 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 @@ } 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 @@ } 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 @@ "xx": string(TTx), "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 @@ }) 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 @@ } else { 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 @@ for { 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 @@ } 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) 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", - ) + } + 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)}, + )) } - replies = append(replies, MarshalSP( - SPTypeFreq, - SPFreq{info.Hash, uint64(offset)}, - )) case SPTypeFile: - state.ctx.LogD( - "sp-process", - SdsAdd(sds, SDS{"type": "file"}), - "unmarshaling packet", - ) + 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 @@ ourSize := uint64(file.Offset) + uint64(len(file.Payload)) 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 @@ spWorkersGroup.Wait() 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 @@ state.payloads <- MarshalSP(SPTypeDone, SPDone{file.Hash}) }() }() 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 @@ pkts++ 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), diff --git a/src/cypherpunks.ru/nncp/tmp.go b/src/cypherpunks.ru/nncp/tmp.go index 4d6f0623603b85cdf955ba1666bc40bf437edc7a22bad46db66f22ee483983e6..14325c1d01b283166af762161479e39528e27287b967f69efcc931f396316714 100644 --- a/src/cypherpunks.ru/nncp/tmp.go +++ b/src/cypherpunks.ru/nncp/tmp.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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/toss.go b/src/cypherpunks.ru/nncp/toss.go index 0465c1ec56f55857a6f9be5d32e2ed529e183d64a11f5933a4602b41c0ed65e5..a8664d437621f851a69b6d5032e7907490d26455a533cd723e1451e711b82265 100644 --- a/src/cypherpunks.ru/nncp/toss.go +++ b/src/cypherpunks.ru/nncp/toss.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ "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 (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 @@ }(job) 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 @@ goto Closing } 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 diff --git a/src/cypherpunks.ru/nncp/toss_test.go b/src/cypherpunks.ru/nncp/toss_test.go index cb763838a67fd4bbc6cde49f2ec73e06fb606fb5299ad204de4020d6a60c53ff..5a64475c4f0e9eae9154f0275d73981bd313e5d8dd7b365113a1163004c3526b 100644 --- a/src/cypherpunks.ru/nncp/toss_test.go +++ b/src/cypherpunks.ru/nncp/toss_test.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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/tx.go b/src/cypherpunks.ru/nncp/tx.go index d3535b296e0a79498e0e8e5059832829314c741b1d5d1a4ba22a797345d3af23..9296a40a0cd64175c93e58ffca64c513388a08850b1bd66673ccd79f1f2f7c1b 100644 --- a/src/cypherpunks.ru/nncp/tx.go +++ b/src/cypherpunks.ru/nncp/tx.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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 @@ "strings" "github.com/davecgh/go-xdr/xdr2" "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/chacha20poly1305" ) func (ctx *Ctx) Tx( @@ -41,7 +42,8 @@ node *Node, 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 @@ for i := len(node.Via); i > 0; i-- { 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 @@ }, "wrote") 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 @@ }, "trns wrote") 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 @@ return nil, nil, 0, err } 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 @@ node *Node, 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 @@ nice, replyNice uint8, 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 { diff --git a/src/cypherpunks.ru/nncp/tx_test.go b/src/cypherpunks.ru/nncp/tx_test.go index 55e7a4eba6cc1498d87924ca6b3b1ee0be601a3cf627953bd83785216d390c97..035838f903707994fbe5a74d7ad0213100ae65363c503cf84c04a55f0e328d72 100644 --- a/src/cypherpunks.ru/nncp/tx_test.go +++ b/src/cypherpunks.ru/nncp/tx_test.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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/via.go b/src/cypherpunks.ru/nncp/via.go index cae9e32a767fddee26b070fb363827bbf7d22663898536f4be674785629ecf55..ca80c96bbbb1151c1df566f5a0594d596287583ac1e8558237a75a9cf17d939a 100644 --- a/src/cypherpunks.ru/nncp/via.go +++ b/src/cypherpunks.ru/nncp/via.go @@ -1,6 +1,6 @@ /* NNCP -- Node to Node copy, utilities for store-and-forward data exchange -Copyright (C) 2016-2018 Sergey Matveev +Copyright (C) 2016-2019 Sergey Matveev 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