-[submodule "src/github.com/davecgh/go-xdr"]
- path = src/github.com/davecgh/go-xdr
+[submodule "src/cypherpunks.ru/nncp/vendor/github.com/davecgh/go-xdr"]
+ path = src/cypherpunks.ru/nncp/vendor/github.com/davecgh/go-xdr
url = https://github.com/davecgh/go-xdr.git
-[submodule "src/github.com/dustin/go-humanize"]
- path = src/github.com/dustin/go-humanize
+[submodule "src/cypherpunks.ru/nncp/vendor/github.com/dustin/go-humanize"]
+ path = src/cypherpunks.ru/nncp/vendor/github.com/dustin/go-humanize
url = https://github.com/dustin/go-humanize.git
-[submodule "src/github.com/flynn/noise"]
- path = src/github.com/flynn/noise
+[submodule "src/cypherpunks.ru/nncp/vendor/github.com/flynn/noise"]
+ path = src/cypherpunks.ru/nncp/vendor/github.com/flynn/noise
url = https://github.com/flynn/noise.git
-[submodule "src/gopkg.in/check.v1"]
- path = src/gopkg.in/check.v1
+[submodule "src/cypherpunks.ru/nncp/vendor/gopkg.in/check.v1"]
+ path = src/cypherpunks.ru/nncp/vendor/gopkg.in/check.v1
url = https://github.com/go-check/check.git
branch = v1
-[submodule "src/gopkg.in/yaml.v2"]
- path = src/gopkg.in/yaml.v2
+[submodule "src/cypherpunks.ru/nncp/vendor/gopkg.in/yaml.v2"]
+ path = src/cypherpunks.ru/nncp/vendor/gopkg.in/yaml.v2
url = https://github.com/go-yaml/yaml.git
branch = v2
-[submodule "src/golang.org/x/crypto"]
- path = src/golang.org/x/crypto
+[submodule "src/cypherpunks.ru/nncp/vendor/golang.org/x/crypto"]
+ path = src/cypherpunks.ru/nncp/vendor/golang.org/x/crypto
url = https://go.googlesource.com/crypto
-[submodule "src/golang.org/x/sys"]
- path = src/golang.org/x/sys
+[submodule "src/cypherpunks.ru/nncp/vendor/golang.org/x/sys"]
+ path = src/cypherpunks.ru/nncp/vendor/golang.org/x/sys
url = https://go.googlesource.com/sys
-[submodule "src/golang.org/x/net"]
- path = src/golang.org/x/net
+[submodule "src/cypherpunks.ru/nncp/vendor/golang.org/x/net"]
+ path = src/cypherpunks.ru/nncp/vendor/golang.org/x/net
url = https://go.googlesource.com/net
-[submodule "src/github.com/gorhill/cronexpr"]
- path = src/github.com/gorhill/cronexpr
+[submodule "src/cypherpunks.ru/nncp/vendor/github.com/gorhill/cronexpr"]
+ path = src/cypherpunks.ru/nncp/vendor/github.com/gorhill/cronexpr
url = https://github.com/gorhill/cronexpr.git
-[submodule "src/cypherpunks.ru/balloon"]
- path = src/cypherpunks.ru/balloon
+[submodule "src/cypherpunks.ru/nncp/vendor/cypherpunks.ru/balloon"]
+ path = src/cypherpunks.ru/nncp/vendor/cypherpunks.ru/balloon
url = git://git.cypherpunks.ru/balloon.git
-GOPATH != pwd
VERSION != cat VERSION
include common.mk
-GOPATH = $(shell pwd)
VERSION = $(shell cat VERSION)
include common.mk
PREFIX ?= /usr/local
+GO ?= go
SENDMAIL ?= /usr/sbin/sendmail
CFGPATH ?= $(PREFIX)/etc/nncp.yaml
-X cypherpunks.ru/nncp.DefaultSendmailPath=$(SENDMAIL) \
-X cypherpunks.ru/nncp.DefaultSpoolPath=$(SPOOLPATH) \
-X cypherpunks.ru/nncp.DefaultLogPath=$(LOGPATH)
+BUILDMOD ?= -mod=vendor
ALL = \
nncp-bundle \
nncp-toss \
nncp-xfer
-all: $(ALL)
-
-nncp-bundle:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-bundle
-
-nncp-call:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-call
-
-nncp-caller:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-caller
-
-nncp-cfgenc:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgenc
-
-nncp-cfgmin:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgmin
-
-nncp-cfgnew:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgnew
-
-nncp-check:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-check
-
-nncp-daemon:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-daemon
+SRC := $(PWD)/src/cypherpunks.ru/nncp
+BIN := $(PWD)/bin
+GOPATH ?= $(PWD)/gopath
-nncp-exec:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-exec
-
-nncp-file:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-file
-
-nncp-freq:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-freq
-
-nncp-log:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-log
-
-nncp-pkt:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-pkt
-
-nncp-reass:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-reass
-
-nncp-rm:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-rm
-
-nncp-stat:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-stat
+all: $(ALL)
-nncp-toss:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-toss
+$(BIN):
+ mkdir -p $(BIN)
-nncp-xfer:
- GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-xfer
+$(ALL): $(BIN)
+ cd $(SRC) ; GOPATH=$(GOPATH) $(GO) build $(BUILDMOD) -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/$@
+ mv $(SRC)/$@ $(BIN)
test:
- GOPATH=$(GOPATH) go test -failfast cypherpunks.ru/nncp/...
+ cd $(SRC) ; GOPATH=$(GOPATH) $(GO) test $(BUILDMOD) -failfast cypherpunks.ru/nncp/...
clean:
- rm -f $(ALL)
+ rm -rf bin
.PHONY: doc
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)
@node Об утилитах
@section Подробнее об утилитах NNCP
+@verbatiminclude pedro.txt
+
@strong{NNCP} (Node to Node copy) это набор утилит упрощающий безопасный
обмен файлами, почтой и командами в режиме сохранить-и-переслать.
@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|}.
[-onlinedeadline INT]
[-maxonlinetime INT]
[-rx|-tx]
+ [-list]
+ [-pkts PKT,PKT,...]
[-rxrate INT]
[-txrate INT]
NODE[:ADDR] [FORCEADDR]
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
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
@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
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
@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
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
@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}
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
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
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
% 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
@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
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
@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)
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 |
@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
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
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:
@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
<style type="text/css"><!--
body {
margin: auto;
- max-width: 800px;
+ width: 80em;
background-color: #AEBECE;
}
h1, h2, h3, h4 { text-align: center }
h1, h2, h3, h4, strong { color: #900090 }
pre { background-color: #CCCCCC }
+table, th, td { border: 1px solid black }
--></style>
* Медленная/дорогая связь для больших объёмов данных, плохой QoS: UsecaseQoSRU.
* Экстремальные наземные окружающие условия, нет связи: UsecaseNoLinkRU.
* Односторонняя широковещательная связь: UsecaseBroadcastRU.
+* Спутниковые каналы связи: UsecaseSatelliteLinksRU.
* Частные, изолированные MitM/Sybil-устойчивые сети: UsecaseF2FRU.
* Высоко защищённые изолированные компьютеры с воздушным зазором: UsecaseAirgapRU.
* Обход сетевой цензуры, здоровье: UsecaseCensorRU.
* Разведка, шпионаж, тайная агентура: UsecaseSpyRU.
+* Дешёвая ночная связь: UsecaseCallerRU.
@end menu
@node UsecaseMailRU
опцию:
@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
переотправлять широковещательные рассылки время от времени, повышая
шансы на то, что получатель примет их, регулярно слушая рассылку.
+@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-устойчивые сети
Эти ребята знают насколько небезопасен Интернет, несовместим с
понятием приватности. Им необходим быстрый сброс и забор данных. Нет
-возможности провести несколько итераций туда-обратно (round trip) --
+возможности провести несколько итераций приёмо-передач (round-trips) --
только сбросить данные, выстрелить и забыть. Опять же, это может быть
переносной накопитель и/или
@url{https://en.wikipedia.org/wiki/USB_dead_drop, USB тайник} (dead drop),
Общение узлов между собой происходит в, так называемой, @ref{Spool,
спул} области: директории содержащей только необработанные зашифрованные
-пакеты. После передачи пакета вы всё-равно не сможете его прочитать:
+пакеты. После передачи пакета вы всё равно не сможете его прочитать:
необходимо запустить другую фазу: @ref{nncp-toss, распаковку}, которая
использует ваши приватные криптографические ключи. То есть, даже если вы
потеряете свой компьютер, устройства хранения и тому прочее -- это не
файлов и почтовых сообщений) может и должна бы быть произведена на
отдельном компьютере (@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
* 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
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
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
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
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
cat > $tmp/includes <<EOF
golang.org/x/crypto/AUTHORS
-golang.org/x/crypto/CONTRIBUTORS
-golang.org/x/crypto/LICENSE
-golang.org/x/crypto/PATENTS
-golang.org/x/crypto/README.md
golang.org/x/crypto/blake2b
golang.org/x/crypto/blake2s
golang.org/x/crypto/chacha20poly1305
+golang.org/x/crypto/CONTRIBUTORS
golang.org/x/crypto/curve25519
golang.org/x/crypto/ed25519
+golang.org/x/crypto/go.mod
+golang.org/x/crypto/go.sum
golang.org/x/crypto/internal/chacha20
golang.org/x/crypto/internal/subtle
+golang.org/x/crypto/LICENSE
golang.org/x/crypto/nacl
+golang.org/x/crypto/PATENTS
golang.org/x/crypto/poly1305
+golang.org/x/crypto/README.md
golang.org/x/crypto/salsa20
golang.org/x/crypto/ssh/terminal
golang.org/x/net/AUTHORS
golang.org/x/net/CONTRIBUTORS
+golang.org/x/net/go.mod
+golang.org/x/net/go.sum
golang.org/x/net/LICENSE
+golang.org/x/net/netutil
golang.org/x/net/PATENTS
golang.org/x/net/README.md
-golang.org/x/net/netutil
golang.org/x/sys/AUTHORS
golang.org/x/sys/CONTRIBUTORS
+golang.org/x/sys/cpu
+golang.org/x/sys/go.mod
golang.org/x/sys/LICENSE
golang.org/x/sys/PATENTS
golang.org/x/sys/README.md
-golang.org/x/sys/cpu
golang.org/x/sys/unix
EOF
-tar cfCI - src $tmp/includes | tar xfC - $tmp
-rm -fr src/golang.org
-mv $tmp/golang.org src/
-rm -fr $tmp/golang.org $tmp/includes
+tar cfCI - $vendor $tmp/includes | tar xfC - $tmp
+rm -fr $vendor/golang.org
+mv $tmp/golang.org $vendor
+rm $tmp/includes
find src -name .travis.yml -delete
-rm -fr src/github.com/davecgh/go-xdr/xdr
-rm -fr src/github.com/gorhill/cronexpr/cronexpr src/github.com/gorhill/cronexpr/APLv2
+rm -fr $vendor/github.com/davecgh/go-xdr/xdr
+rm $vendor/github.com/gorhill/cronexpr/APLv2
rm -fr ports
rm makedist.sh
rm .gitmodules
cd ..
-tar cvf nncp-"$release".tar nncp-"$release"
-xz -9 nncp-"$release".tar
-gpg --detach-sign --sign --local-user 0x2B25868E75A1A953 nncp-"$release".tar.xz
-mv $tmp/nncp-"$release".tar.xz $tmp/nncp-"$release".tar.xz.sig $cur/doc/nncp.html/download
+tar cvf nncp-"$release".tar --uid=0 --gid=0 --numeric-owner nncp-"$release"
+xz -9v nncp-"$release".tar
+gpg --detach-sign --sign --local-user releases@nncpgo.org nncp-"$release".tar.xz
+mv -v $tmp/nncp-"$release".tar.xz $tmp/nncp-"$release".tar.xz.sig $cur/doc/nncp.html/download
tarball=$cur/doc/nncp.html/download/nncp-"$release".tar.xz
-size=$(( $(cat $tarball | wc -c) / 1024 ))
+size=$(( $(stat -f %z $tarball) / 1024 ))
hash=$(gpg --print-md SHA256 < $tarball)
+release_date=$(date "+%Y-%m-%d")
+
cat <<EOF
An entry for documentation:
-@item @ref{Release $release, $release} @tab $size KiB
+@item @ref{Release $release, $release} @tab $release_date @tab $size KiB
@tab @url{download/nncp-${release}.tar.xz, link} @url{download/nncp-${release}.tar.xz.sig, sign}
@tab @code{$hash}
EOF
# $FreeBSD: head/net/nncp/Makefile 471003 2018-05-27 20:24:00Z krion $
PORTNAME= nncp
-DISTVERSION= 3.3
+DISTVERSION= 3.4
CATEGORIES= net
MASTER_SITES= http://www.nncpgo.org/download/
NNCP (Node to Node copy) is a collection of utilities simplifying
-secure store-and-forward files and mail exchanging.
+secure store-and-forward files, mail and commands exchanging.
This utilities are intended to help build up small size (dozens of
nodes) ad-hoc friend-to-friend (F2F) statically routed darknet
+++ /dev/null
-golang.org/x/crypto/internal/chacha20
\ No newline at end of file
+++ /dev/null
-Subproject commit 92c161713a13cc78403468c09f1557eb7c4a3a4a
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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")
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{
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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
}
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 {
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Create/digest stream of NNCP encrypted packets
+// Create/digest stream of NNCP encrypted packets.
package main
import (
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
}
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)
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Call NNCP TCP daemon
+// Call NNCP TCP daemon.
package main
import (
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")
}
}
+ 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,
*txRate,
*onlineDeadline,
*maxOnlineTime,
+ *listOnly,
+ onlyPkts,
) {
os.Exit(1)
}
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Croned NNCP TCP daemon caller
+// Croned NNCP TCP daemon caller.
package main
import (
call.TxRate,
call.OnlineDeadline,
call.MaxOnlineTime,
+ false,
+ nil,
)
node.Lock()
node.Busy = false
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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")
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Verify NNCP Rx/Tx packets checksum
+// Verify NNCP Rx/Tx packets checksum.
package main
import (
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)
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// NNCP TCP daemon
+// NNCP TCP daemon.
package main
import (
}
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{
}, "")
} 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}, "")
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Send execution command via NNCP
+// Send execution command via NNCP.
package main
import (
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Send file via NNCP
+// Send file via NNCP.
package main
import (
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Send file request via NNCP
+// Send file request via NNCP.
package main
import (
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Read NNCP logs
+// Read NNCP logs.
package main
import (
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Parse raw NNCP packet
+// Parse raw NNCP packet.
package main
import (
"cypherpunks.ru/nncp"
"github.com/davecgh/go-xdr/xdr2"
- "golang.org/x/crypto/blake2b"
)
func usage() {
func main() {
var (
+ overheads = flag.Bool("overheads", false, "Print packet overheads")
dump = flag.Bool("dump", false, "Write decrypted/parsed payload to stdout")
decompress = flag.Bool("decompress", false, "Try to zlib decompress dumped data")
cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
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")
}
}
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 {
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Send file via NNCP
+// Reassembly chunked file.
package main
import (
}
}
}
- 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")
}
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
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{}{}
}
}
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 {
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Remove packet from the queue
+// Remove packet from the queue.
package main
import (
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Show queued NNCP Rx/Tx stats
+// Show queued NNCP Rx/Tx stats.
package main
import (
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Process inbound NNCP packets
+// Process inbound NNCP packets.
package main
import (
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Copy NNCP inbound and outbounds packets
+// Exchange NNCP inbound and outbounds packets with external directory.
package main
import (
}
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")
}
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
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()
)); 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")
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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
import (
"errors"
"io/ioutil"
+ "log"
"os"
"path/filepath"
+
+ "golang.org/x/sys/unix"
)
type Ctx struct {
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
+}
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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 (
)
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 {
PCost uint32
Salt *[32]byte
Blob []byte
- MAC *[blake2b.Size256]byte
}
func blake256() hash.Hash {
if _, err = rand.Read(salt[:]); err != nil {
return nil, err
}
- key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost)
- kdf, err := blake2b.NewXOF(32+64, key)
- if err != nil {
- return nil, err
- }
- if _, err = kdf.Write(MagicNNCPBv2[:]); err != nil {
- return nil, err
- }
- keyEnc := new([32]byte)
- if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
- return nil, err
- }
- keyAuth := make([]byte, 64)
- if _, err = io.ReadFull(kdf, keyAuth); err != nil {
- return nil, err
- }
- mac, err := blake2b.New256(keyAuth)
- if err != nil {
- return nil, err
- }
- chacha20.XORKeyStream(data, data, new([16]byte), keyEnc)
- if _, err = mac.Write(data); err != nil {
- return nil, err
- }
- macTag := new([blake2b.Size256]byte)
- mac.Sum(macTag[:0])
eblob := EBlob{
- Magic: MagicNNCPBv2,
+ Magic: MagicNNCPBv3,
SCost: uint32(sCost),
TCost: uint32(tCost),
PCost: uint32(pCost),
Salt: salt,
- Blob: data,
- MAC: macTag,
+ Blob: nil,
}
- var eblobRaw bytes.Buffer
- if _, err = xdr.Marshal(&eblobRaw, &eblob); err != nil {
+ var eblobBuf bytes.Buffer
+ if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
return nil, err
}
- return eblobRaw.Bytes(), nil
+ key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost)
+ aead, err := chacha20poly1305.New(key)
+ if err != nil {
+ return nil, err
+ }
+ buf := make([]byte, 0, len(data)+aead.Overhead())
+ buf = aead.Seal(buf, make([]byte, aead.NonceSize()), data, eblobBuf.Bytes())
+ eblob.Blob = buf
+ eblobBuf.Reset()
+ if _, err = xdr.Marshal(&eblobBuf, &eblob); err != nil {
+ return nil, err
+ }
+ return eblobBuf.Bytes(), nil
}
func DeEBlob(eblobRaw, password []byte) ([]byte, error) {
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(
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
}
--- /dev/null
+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
--- /dev/null
+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=
/*
NNCP -- Node to Node copy
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
}
if err, exists := sds["err"]; exists {
msg += ": " + err
+ } else {
+ msg += " " + rem
}
case "nncp-bundle":
switch sds["xx"] {
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":
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":
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
package nncp
import (
+ "io"
"os"
"path/filepath"
"strconv"
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,
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
func UsageHeader() string {
return VersionGet() + `
-Copyright (C) 2016-2017 Sergey Matveev
+Copyright (C) 2016-2019 Sergey Matveev
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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
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 {
if err != nil {
panic(err)
}
- PktOverhead = 8 + blake2b.Size256 + int64(n) + blake2b.Size256
+ PktOverhead = int64(n)
buf.Reset()
dummyId, err := NodeIdFromString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
panic(err)
}
pktEnc := PktEnc{
- Magic: MagicNNCPEv3,
+ Magic: MagicNNCPEv4,
Nice: 123,
Sender: dummyId,
Recipient: dummyId,
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(
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
return err
}
tbs := PktTbs{
- Magic: MagicNNCPEv3,
+ Magic: MagicNNCPEv4,
Nice: nice,
Sender: our.Id,
Recipient: their.Id,
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,
if err != nil {
return err
}
- if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil {
+ if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
return err
}
- keyEnc := new([32]byte)
- if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
+ key := make([]byte, chacha20poly1305.KeySize)
+ if _, err = io.ReadFull(kdf, key); err != nil {
return err
}
- keyAuth := make([]byte, 64)
- if _, err = io.ReadFull(kdf, keyAuth); err != nil {
- return err
- }
- mac, err := blake2b.New256(keyAuth)
+ aead, err := chacha20poly1305.New(key)
if err != nil {
return err
}
+ nonce := make([]byte, aead.NonceSize())
- sizeBuf := make([]byte, 8)
- binary.BigEndian.PutUint64(sizeBuf, uint64(size))
- chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc)
- if _, err = out.Write(sizeBuf); err != nil {
- return err
- }
- if _, err = mac.Write(sizeBuf); err != nil {
- return err
- }
- if _, err = out.Write(mac.Sum(nil)); err != nil {
+ fullSize := pktBuf.Len() + int(size)
+ sizeBuf := make([]byte, 8+aead.Overhead())
+ binary.BigEndian.PutUint64(sizeBuf, uint64(sizeWithTags(int64(fullSize))))
+ if _, err = out.Write(aead.Seal(sizeBuf[:0], nonce, sizeBuf[:8], nil)); err != nil {
return err
}
- if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
- return err
- }
- if _, err = io.ReadFull(kdf, keyAuth); err != nil {
- return err
- }
- mac, err = blake2b.New256(keyAuth)
- if err != nil {
- return err
- }
lr := io.LimitedReader{R: data, N: size}
mr := io.MultiReader(&pktBuf, &lr)
- mw := io.MultiWriter(out, mac)
- fullSize := pktBuf.Len() + int(size)
- written, err := ae(keyEnc, mr, mw)
+ written, err := aeadProcess(aead, nonce, true, mr, out)
if err != nil {
return err
}
if written != fullSize {
return io.ErrUnexpectedEOF
}
- if _, err = out.Write(mac.Sum(nil)); err != nil {
- return err
- }
if padSize > 0 {
- if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
+ if _, err = io.ReadFull(kdf, key); err != nil {
return err
}
- lr = io.LimitedReader{R: DevZero{}, N: padSize}
- written, err = ae(keyEnc, &lr, out)
+ kdf, err = blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
if err != nil {
return err
}
- if written != int(padSize) {
- return io.ErrUnexpectedEOF
+ if _, err = io.CopyN(out, kdf, padSize); err != nil {
+ return err
}
}
return nil
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,
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]
if err != nil {
return their, 0, err
}
- if _, err = kdf.Write(MagicNNCPEv3[:]); err != nil {
+ if _, err = kdf.Write(MagicNNCPEv4[:]); err != nil {
return their, 0, err
}
- keyEnc := new([32]byte)
- if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
- return their, 0, err
- }
- keyAuth := make([]byte, 64)
- if _, err = io.ReadFull(kdf, keyAuth); err != nil {
+ key := make([]byte, chacha20poly1305.KeySize)
+ if _, err = io.ReadFull(kdf, key); err != nil {
return their, 0, err
}
- mac, err := blake2b.New256(keyAuth)
+ aead, err := chacha20poly1305.New(key)
if err != nil {
return their, 0, err
}
+ nonce := make([]byte, aead.NonceSize())
- sizeBuf := make([]byte, 8)
+ sizeBuf := make([]byte, 8+aead.Overhead())
if _, err = io.ReadFull(data, sizeBuf); err != nil {
return their, 0, err
}
- if _, err = mac.Write(sizeBuf); err != nil {
- return their, 0, err
- }
- tag := make([]byte, blake2b.Size256)
- if _, err = io.ReadFull(data, tag); err != nil {
- return their, 0, err
- }
- if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
- return their, 0, errors.New("Unauthenticated size")
- }
- chacha20.XORKeyStream(sizeBuf, sizeBuf, new([16]byte), keyEnc)
- size := int64(binary.BigEndian.Uint64(sizeBuf))
-
- if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
- return their, size, err
- }
- if _, err = io.ReadFull(kdf, keyAuth); err != nil {
- return their, size, err
- }
- mac, err = blake2b.New256(keyAuth)
+ sizeBuf, err = aead.Open(sizeBuf[:0], nonce, sizeBuf, nil)
if err != nil {
return their, 0, err
}
+ size := int64(binary.BigEndian.Uint64(sizeBuf))
- fullSize := PktOverhead + size - 8 - 2*blake2b.Size256
- lr := io.LimitedReader{R: data, N: fullSize}
- tr := io.TeeReader(&lr, mac)
- written, err := ae(keyEnc, tr, out)
+ lr := io.LimitedReader{R: data, N: size}
+ written, err := aeadProcess(aead, nonce, false, &lr, out)
if err != nil {
return their, int64(written), err
}
- if written != int(fullSize) {
+ if written != int(size) {
return their, int64(written), io.ErrUnexpectedEOF
}
- if _, err = io.ReadFull(data, tag); err != nil {
- return their, size, err
- }
- if subtle.ConstantTimeCompare(mac.Sum(nil), tag) != 1 {
- return their, size, errors.New("Unauthenticated payload")
- }
return their, size, nil
}
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
if *node.Id != *node1.Id {
return false
}
- if sizeGot != int64(size) {
+ if sizeGot != sizeWithTags(PktOverhead+int64(size)) {
return false
}
var pktBuf bytes.Buffer
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
}
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
rxRate int
txRate int
isDead bool
+ listOnly bool
+ onlyPkts map[[32]byte]bool
sync.RWMutex
}
}
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 {
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 {
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 {
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",
}
}()
}
- 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",
}
}()
- 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",
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",
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
"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",
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 {
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
}
}
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 {
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",
}
func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
- sds := SDS{"node": state.Node.Id, "nice": strconv.Itoa(int(state.nice))}
+ sds := SDS{"node": state.Node.Id, "nice": strconv.Itoa(int(state.Nice))}
r := bytes.NewReader(payload)
var err error
var replies [][]byte
var infosGot bool
for r.Len() > 0 {
- state.ctx.LogD("sp-process", sds, "unmarshaling header")
+ state.Ctx.LogD("sp-process", sds, "unmarshaling header")
var head SPHead
if _, err = xdr.Unmarshal(r, &head); err != nil {
- state.ctx.LogE("sp-process", SdsAdd(sds, SDS{"err": err}), "")
+ state.Ctx.LogE("sp-process", SdsAdd(sds, SDS{"err": err}), "")
return nil, err
}
switch head.Type {
case SPTypeInfo:
infosGot = true
sdsp := SdsAdd(sds, SDS{"type": "info"})
- state.ctx.LogD("sp-process", sdsp, "unmarshaling packet")
+ state.Ctx.LogD("sp-process", sdsp, "unmarshaling packet")
var info SPInfo
if _, err = xdr.Unmarshal(r, &info); err != nil {
- state.ctx.LogE("sp-process", SdsAdd(sdsp, SDS{"err": err}), "")
+ state.Ctx.LogE("sp-process", SdsAdd(sdsp, SDS{"err": err}), "")
return nil, err
}
sdsp = SdsAdd(sds, SDS{
"hash": ToBase32(info.Hash[:]),
"size": strconv.FormatInt(int64(info.Size), 10),
+ "nice": strconv.Itoa(int(info.Nice)),
})
- if info.Nice > state.nice {
- state.ctx.LogD("sp-process", sdsp, "too nice")
+ if !state.listOnly && info.Nice > state.Nice {
+ state.Ctx.LogD("sp-process", sdsp, "too nice")
continue
}
- state.ctx.LogD("sp-process", sdsp, "received")
- if state.xxOnly == TTx {
+ state.Ctx.LogD("sp-process", sdsp, "received")
+ if !state.listOnly && state.xxOnly == TTx {
continue
}
state.Lock()
state.infosTheir[*info.Hash] = &info
state.Unlock()
- state.ctx.LogD("sp-process", sdsp, "stating part")
+ state.Ctx.LogD("sp-process", sdsp, "stating part")
pktPath := filepath.Join(
- state.ctx.Spool,
+ state.Ctx.Spool,
state.Node.Id.String(),
string(TRx),
ToBase32(info.Hash[:]),
)
if _, err = os.Stat(pktPath); err == nil {
- state.ctx.LogD("sp-process", sdsp, "already done")
- replies = append(replies, MarshalSP(SPTypeDone, SPDone{info.Hash}))
+ state.Ctx.LogI("sp-info", sdsp, "already done")
+ if !state.listOnly {
+ replies = append(replies, MarshalSP(SPTypeDone, SPDone{info.Hash}))
+ }
continue
}
if _, err = os.Stat(pktPath + SeenSuffix); err == nil {
- state.ctx.LogD("sp-process", sdsp, "already seen")
- replies = append(replies, MarshalSP(SPTypeDone, SPDone{info.Hash}))
+ state.Ctx.LogI("sp-info", sdsp, "already seen")
+ if !state.listOnly {
+ replies = append(replies, MarshalSP(SPTypeDone, SPDone{info.Hash}))
+ }
continue
}
fi, err := os.Stat(pktPath + PartSuffix)
var offset int64
if err == nil {
offset = fi.Size()
- state.ctx.LogD(
- "sp-process",
- SdsAdd(sdsp, SDS{"offset": strconv.FormatInt(offset, 10)}),
- "part exists",
- )
}
- replies = append(replies, MarshalSP(
- SPTypeFreq,
- SPFreq{info.Hash, uint64(offset)},
- ))
- case SPTypeFile:
- state.ctx.LogD(
- "sp-process",
- SdsAdd(sds, SDS{"type": "file"}),
- "unmarshaling packet",
+ if !state.Ctx.IsEnoughSpace(int64(info.Size) - offset) {
+ state.Ctx.LogI("sp-info", sdsp, "not enough space")
+ continue
+ }
+ state.Ctx.LogI(
+ "sp-info",
+ SdsAdd(sdsp, SDS{"offset": strconv.FormatInt(offset, 10)}),
+ "",
)
+ if !state.listOnly && (state.onlyPkts == nil || state.onlyPkts[*info.Hash]) {
+ replies = append(replies, MarshalSP(
+ SPTypeFreq,
+ SPFreq{info.Hash, uint64(offset)},
+ ))
+ }
+ case SPTypeFile:
+ sdsp := SdsAdd(sds, SDS{"type": "file"})
+ state.Ctx.LogD("sp-process", sdsp, "unmarshaling packet")
var file SPFile
if _, err = xdr.Unmarshal(r, &file); err != nil {
- state.ctx.LogE("sp-process", SdsAdd(sds, SDS{
+ state.Ctx.LogE("sp-process", SdsAdd(sds, SDS{
"err": err,
"type": "file",
}), "")
return nil, err
}
- sdsp := SdsAdd(sds, SDS{
- "xx": string(TRx),
- "hash": ToBase32(file.Hash[:]),
- "size": strconv.Itoa(len(file.Payload)),
- })
+ sdsp["xx"] = string(TRx)
+ sdsp["hash"] = ToBase32(file.Hash[:])
+ sdsp["size"] = strconv.Itoa(len(file.Payload))
filePath := filepath.Join(
- state.ctx.Spool,
+ state.Ctx.Spool,
state.Node.Id.String(),
string(TRx),
ToBase32(file.Hash[:]),
)
- state.ctx.LogD("sp-file", sdsp, "opening part")
+ state.Ctx.LogD("sp-file", sdsp, "opening part")
fd, err := os.OpenFile(
filePath+PartSuffix,
os.O_RDWR|os.O_CREATE,
os.FileMode(0600),
)
if err != nil {
- state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
+ state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
return nil, err
}
- state.ctx.LogD(
+ state.Ctx.LogD(
"sp-file",
SdsAdd(sdsp, SDS{"offset": strconv.FormatInt(int64(file.Offset), 10)}),
"seeking",
)
- if _, err = fd.Seek(int64(file.Offset), 0); err != nil {
- state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
+ if _, err = fd.Seek(int64(file.Offset), io.SeekStart); err != nil {
+ state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
fd.Close()
return nil, err
}
- state.ctx.LogD("sp-file", sdsp, "writing")
+ state.Ctx.LogD("sp-file", sdsp, "writing")
_, err = fd.Write(file.Payload)
if err != nil {
- state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
+ state.Ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "")
fd.Close()
return nil, err
}
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()
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)
}()
}()
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",
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),
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
"github.com/davecgh/go-xdr/xdr2"
"github.com/dustin/go-humanize"
"golang.org/x/crypto/blake2b"
+ "golang.org/x/crypto/poly1305"
)
const (
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())
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 {
}
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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
"github.com/davecgh/go-xdr/xdr2"
"golang.org/x/crypto/blake2b"
+ "golang.org/x/crypto/chacha20poly1305"
)
func (ctx *Ctx) Tx(
pkt *Pkt,
nice uint8,
size, minSize int64,
- src io.Reader) (*Node, error) {
+ src io.Reader,
+) (*Node, error) {
tmp, err := ctx.NewTmpFileWHash()
if err != nil {
return nil, err
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
}
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) {
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)
}
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)
nice uint8,
srcPath, dstPath string,
minSize int64,
- chunkSize int64) error {
+ chunkSize int64,
+) error {
if dstPath == "" {
if srcPath == "-" {
return errors.New("Must provide destination filename")
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 {
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
--- /dev/null
+Subproject commit 0e07700b027907d44e0060fc3f75c4590478452b
--- /dev/null
+Subproject commit 9f541cc9db5d55bce703bd99987c9d5cb8eea45e
--- /dev/null
+Subproject commit a29dc8fdc73485234dbef99ebedb95d2eced08de
--- /dev/null
+Subproject commit 4829fb13d2c62012c17688fa7f629f371014946d
--- /dev/null
+Subproject commit a129542de9ae0895210abff9c95d67a1f33cb93d
--- /dev/null
+Subproject commit 788fd78401277ebd861206a03c884797c6ec5541
--- /dev/null
+Subproject commit 7b8349ac747c6a24702b762d2c4fd9266cf4f1d6
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2018 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
+++ /dev/null
-Subproject commit 02af3965c54e8cacf948b97fef38925c4120652c
+++ /dev/null
-Subproject commit a49355c7e3f8fe157a85be2f77e6e269a0f89602
+++ /dev/null
-Subproject commit 32a936f46389aa10549d60bd7833e54b01685d09
+++ /dev/null
-Subproject commit 3c6ecd8f22c6f40fbeec94c000a069d7d87c7624
+++ /dev/null
-Subproject commit 20d25e2804050c1cd24a7eea1e7a6447dd0e74ec
+++ /dev/null
-Subproject commit 5420a8b6744d3b0345ab293f6fcba19c978f1183