From: Sergey Matveev Date: Sun, 30 Apr 2017 13:56:58 +0000 (+0300) Subject: Merge branch 'develop' X-Git-Tag: 0.7^0 X-Git-Url: http://www.git.cypherpunks.ru/?a=commitdiff_plain;h=093f249044a62ce4d988542c7267caf1da5d0968;hp=f6c2ddf3d60982518e6ff54af57e7d7107cc9ed1;p=nncp.git Merge branch 'develop' --- diff --git a/.gitmodules b/.gitmodules index ea445fb..ae68d8e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -30,3 +30,6 @@ [submodule "src/github.com/gorhill/cronexpr"] path = src/github.com/gorhill/cronexpr url = https://github.com/gorhill/cronexpr.git +[submodule "src/cypherpunks.ru/balloon"] + path = src/cypherpunks.ru/balloon + url = git://git.cypherpunks.ru/balloon.git diff --git a/README b/README index 5641e37..be8ac6b 100644 --- a/README +++ b/README @@ -24,6 +24,6 @@ patches to nncp-devel mailing list: https://lists.cypherpunks.ru/pipermail/nncp-devel/ Development Git source code repository currently is located here: -http://git.cypherpunks.ru/cgit.cgi/nncp.git/ +https://git.cypherpunks.ru/cgit.cgi/nncp.git/ For further information please read either doc/nncp.info or doc/nncp.texi. diff --git a/README.RU b/README.RU new file mode 100644 index 0000000..466ca10 --- /dev/null +++ b/README.RU @@ -0,0 +1,35 @@ +NNCP (Node to Node copy) это набор утилит упрощающий безопасный обмен +файлами и почтой в режиме сохранить-и-переслать. + +Эти утилиты предназначены помочь с построением одноранговых сетей +небольшого размера (дюжины узлов), в режиме друг-к-другу (F2F) со +статической маршрутизацией для безопасной надёжной передачи файлов, +запросов на передачу файлов и Интернет почты по принципу +выстрелил-и-забыл. Все пакеты проверяются на целостность, шифруются по +принципу точка-точка (E2EE), аутентифицируются известными публичными +ключами участников. Луковичное (onion) шифрование применяется ко всем +ретранслируемым пакетам. Каждый узел выступает одновременно в роли +клиента и сервера, может использовать как push, так и poll модель +поведения. + +Поддержка из коробки offline флоппинета, тайников для сброса информации +(dead drop) и компьютеров с "воздушным зазором" (air-gap). Но также +существует и online TCP демон с полнодуплексной возобновляемой передачей +данных. + +NNCP это свободное программное обеспечением: условия распространения +находятся в файле COPYING. Оно должно работать на всех POSIX-совместимых +системах. Лёгкая интеграция с существующими SMTP серверами. Единственный +конфигурационный YAML файл. + +Домашняя страница: http://www.nncpgo.org/ + +Пожалуйста все вопросы касающиеся использования NNCP, отчёты об ошибках +и патчи отправляйте в nncp-devel почтовую рассылку: +https://lists.cypherpunks.ru/pipermail/nncp-devel/ + +Исходный код для разработчика находится в Git репозитории: +https://git.cypherpunks.ru/cgit.cgi/nncp.git/ + +Для дополнительной информации пожалуйста читайте или doc/nncp.info +или doc/nncp.texi. diff --git a/THANKS b/THANKS deleted file mode 100644 index 939764c..0000000 --- a/THANKS +++ /dev/null @@ -1,5 +0,0 @@ -There are people deserving to be thanked for helping this project: - -* Shawn K. Quinn for his descriptive - instructions about building NNCP under Ubuntu GNU/Linux distributions - and bug reports diff --git a/VERSION b/VERSION index 5a2a580..eb49d7c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6 +0.7 diff --git a/common.mk b/common.mk index e949d12..327f8df 100644 --- a/common.mk +++ b/common.mk @@ -19,15 +19,17 @@ LDFLAGS = \ ALL = \ nncp-call \ nncp-caller \ + nncp-cfgenc \ + nncp-cfgmin \ + nncp-cfgnew \ nncp-check \ nncp-daemon \ nncp-file \ nncp-freq \ nncp-log \ nncp-mail \ - nncp-mincfg \ - nncp-newcfg \ nncp-pkt \ + nncp-reass \ nncp-rm \ nncp-stat \ nncp-toss \ @@ -41,6 +43,15 @@ 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 @@ -59,15 +70,12 @@ nncp-log: nncp-mail: GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-mail -nncp-mincfg: - GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-mincfg - -nncp-newcfg: - GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-newcfg - 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 @@ -99,7 +107,7 @@ install: all doc cp -f doc/nncp.info $(INFODIR) chmod 644 $(INFODIR)/nncp.info mkdir -p $(DOCDIR) - cp -f -L AUTHORS NEWS README THANKS $(DOCDIR) + cp -f -L AUTHORS NEWS NEWS.RU README README.RU THANKS $(DOCDIR) chmod 644 $(DOCDIR)/* install-strip: install diff --git a/doc/about.ru.texi b/doc/about.ru.texi new file mode 100644 index 0000000..2478ea0 --- /dev/null +++ b/doc/about.ru.texi @@ -0,0 +1,44 @@ +@node Об утилитах +@section Подробнее об утилитах NNCP + +NNCP (Node to Node copy) это набор утилит упрощающий безопасный обмен +файлами и почтой в режиме сохранить-и-переслать. + +Эти утилиты предназначены помочь с построением однораговых сетей +небольшого размера (дюжины узлов), в режиме +@url{https://ru.wikipedia.org/wiki/Friend-to-friend, друг-к-другу} (F2F) +со статической маршрутизацией для безопасной надёжной передачи файлов, +запросов на передачу файлов и Интернет почты по принципу +выстрелил-и-забыл. Все пакеты проверяются на целостность, шифруются по +принципу @url{https://en.wikipedia.org/wiki/End-to-end_encryption, +точка-точка}, аутентифицируются известными публичными ключами +участников. +@url{https://ru.wikipedia.org/wiki/%D0%9B%D1%83%D0%BA%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F, Луковичное} +(onion) шифрование применяется ко всем ретранслируемым пакетам. Каждый +узел выступает одновременно в роли клиента и сервера, может использовать +как push, так и poll модель поведения. + +Поддержка из коробки offline +@url{https://ru.wikipedia.org/wiki/%D0%A4%D0%BB%D0%BE%D0%BF%D0%BF%D0%B8%D0%BD%D0%B5%D1%82, +флоппинета}, +@url{https://ru.wikipedia.org/wiki/%D0%A2%D0%B0%D0%B9%D0%BD%D0%B8%D0%BA, +тайников} для сброса информации (dead drop) и компьютеров с +@url{https://ru.wikipedia.org/wiki/%D0%92%D0%BE%D0%B7%D0%B4%D1%83%D1%88%D0%BD%D1%8B%D0%B9_%D0%B7%D0%B0%D0%B7%D0%BE%D1%80_(%D1%81%D0%B5%D1%82%D0%B8_%D0%BF%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D1%87%D0%B8_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85), +воздушным зазором} (air-gap). Но также существует и online TCP демон с +полнодуплексной возобновляемой передачей данных. + +Узнайте о возможных @ref{Сценарии использования, сценариях использования}! + +NNCP это @url{https://www.gnu.org/philosophy/pragmatic.ru.html, +копилефт} @url{https://www.gnu.org/philosophy/free-sw.ru.html, свободное +программное обеспечение}: лицензировано под условиями +@url{https://www.gnu.org/licenses/gpl-3.0.ru.html, GNU GPLv3+}. Оно +должно работать на всех @url{https://ru.wikipedia.org/wiki/POSIX, +POSIX}-совместимых системах. Лёгкая интеграция с существующими +@url{https://ru.wikipedia.org/wiki/SMTP, SMTP} серверами. Единственный +конфигурационный @url{http://yaml.org/, YAML} файл. + +Зачем создавать ещё одно решение с принципом сохранить-и-переслать когда +уже существуют UUCP, FTN и даже SMTP? Посмотрите @ref{Сравнение, сравнение}! +Простота, криптографическая безопасность, совместимость с флоппинетом и +лёгкая интеграция с существующими SMTP серверами -- вот и причины. diff --git a/doc/about.texi b/doc/about.texi new file mode 100644 index 0000000..eb306ad --- /dev/null +++ b/doc/about.texi @@ -0,0 +1,37 @@ +NNCP (Node to Node copy) is a collection of utilities simplifying +secure store-and-forward files and mail exchanging. + +See also this page @ref{Об утилитах, on russian}. + +This utilities are intended to help build up small size (dozens of +nodes) ad-hoc @url{https://en.wikipedia.org/wiki/Friend-to-friend, +friend-to-friend} (F2F) statically routed +@url{https://en.wikipedia.org/wiki/Darknet, darknet} networks for +fire-and-forget secure reliable files, file requests and Internet mail +transmission. All packets are integrity checked, +@url{https://en.wikipedia.org/wiki/End-to-end_encryption, end-to-end} +encrypted, explicitly authenticated by known participants public keys. +@url{https://en.wikipedia.org/wiki/Onion_routing, Onion encryption} is +applied to relayed packets. Each node acts both as a client and server, +can use push and poll behaviour model. + +Out-of-box offline @url{https://en.wikipedia.org/wiki/Sneakernet, +sneakernet/floppynet}, @url{https://en.wikipedia.org/wiki/Dead_drop, +dead drops} and @url{https://en.wikipedia.org/wiki/Air_gap_(networking), +air-gapped} computers support. But online TCP daemon with full-duplex +resumable data transmission exists. + +Look for possible @ref{Use cases, use cases}! + +NNCP is @url{https://www.gnu.org/philosophy/pragmatic.html, copylefted} +@url{https://www.gnu.org/philosophy/free-sw.html, free software} +licenced under @url{https://www.gnu.org/licenses/gpl-3.0.html, GNU GPLv3+}. +It should work on all @url{https://en.wikipedia.org/wiki/POSIX, +POSIX}-compatible systems. Easy integration with existing +@url{https://en.wikipedia.org/wiki/SMTP, SMTP} servers. Single +@url{http://yaml.org/, YAML} configuration file. + +Why create yet another store-and-forward solution when UUCP, FTN and +even SMTP exists? Look in @ref{Comparison, comparison} section! +Simplicity, cryptographic security, sneakernet compatibility and easy +integration with current SMTP servers are the reasons. diff --git a/doc/call.texi b/doc/call.texi index 509da59..f65a2f8 100644 --- a/doc/call.texi +++ b/doc/call.texi @@ -8,16 +8,16 @@ Example list of call structures: @verbatim calls: - - cron: "*/1 * * * 0-4" + cron: "*/1 * * * MON-FRI" onlinedeadline: 3600 nice: 64 - - cron: "30 * * * 5-6" + cron: "30 * * * SAT,SUN" onlinedeadline: 1800 maxonlinetime: 1750 nice: 64 - - cron: "0 * * * 5-6" + cron: "0 * * * SAT,SUN" xx: rx addr: lan @end verbatim diff --git a/doc/cfg.texi b/doc/cfg.texi index 653c801..1a37f10 100644 --- a/doc/cfg.texi +++ b/doc/cfg.texi @@ -49,6 +49,8 @@ neigh: signpub: GTGXG...IE3OA sendmail: [/usr/sbin/sendmail] freq: /home/bob/pub + freqchunked: 1024 + freqminsize: 2048 via: [alice] @end verbatim @@ -99,6 +101,14 @@ omitted to forbid file uploading on that node. Full path to directory from where file requests will queue files for transmission. May be omitted to forbid freqing from that node. +@item freqchunked +If set, then enable @ref{Chunked, chunked} file transmission during +freqing. This is the desired chunk size in KiBs. + +@item freqminsize +If set, then apply @ref{OptMinSize, -minsize} option during file +transmission. + @item via An array of node identifiers that will be used as a relay to that node. For example @verb{|[foo,bar]|} means that packet can reach current node diff --git a/doc/chunked.texi b/doc/chunked.texi new file mode 100644 index 0000000..1682cc3 --- /dev/null +++ b/doc/chunked.texi @@ -0,0 +1,45 @@ +@node Chunked +@unnumbered Chunked files + +There is ability to transfer huge files with splitting them into smaller +chunks. Each chunk is treated like a separate file, producing separate +outbound packet unrelated with other ones. + +This is useful when your removable storage device has smaller capacity +than huge file's size. You can transfer those chunks on different +storage devices, and/or at different time, reassembling the whole packet +on the destination node. + +Splitting is done with @ref{nncp-file, nncp-file -chunked} command and +reassembling with @ref{nncp-reass} command. + +Chunked @file{FILE} produces @file{FILE.nncp.meta}, +@file{FILE.nncp.chunk0}, @file{FILE.nncp.chunk1}, ... files. All +@file{.nncp.chunkXXX} can be concatenated together to produce original +@file{FILE}. + +@file{.nncp.meta} contains information about file/chunk +size and their hash checksums. This is +@url{https://tools.ietf.org/html/rfc4506, XDR}-encoded structure: + +@verbatim ++------------------------------+---------------------+ +| MAGIC | FILESIZE | CHUNKSIZE | HASH0 | HASH1 | ... | ++------------------------------+---------------------+ +@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 M 0x00 0x00 0x01|} +@item File size @tab + unsigned hyper integer @tab + Whole reassembled file's size +@item Chunk size @tab + unsigned hyper integer @tab + Size of each chunk (except for the last one, that could be smaller) +@item Checksums @tab + variable length array of 32 byte fixed length opaque data @tab + BLAKE2b-256 checksum of each chunk +@end multitable diff --git a/doc/cmds.texi b/doc/cmds.texi index 4fa1045..039fc2d 100644 --- a/doc/cmds.texi +++ b/doc/cmds.texi @@ -5,14 +5,16 @@ Nearly all commands have the following common options: @table @option @item -cfg - Path to configuration file. May be overrided by @env{NNCPCFG} - environment variable. + Path to configuration file. May be overridden by @env{NNCPCFG} + environment variable. If file file is an encrypted @ref{EBlob, + eblob}, then ask for passphrase to decrypt it first. @item -debug Print debug messages. Normally this option should not be used. @item -minsize - Minimal required resulting packet size. For example if you send 2 - KiB file and set @option{-minsize 4096}, then resulting packet will - be 4 KiB (containing file itself and some junk). + @anchor{OptMinSize} + Minimal required resulting packet size, in KiBs. For example if you + send 2 KiB file and set @option{-minsize 4}, then resulting packet + will be 4 KiB (containing file itself and some junk). @item -nice Set desired outgoing packet @ref{Niceness, niceness level}. 1-255 values are allowed. @@ -81,6 +83,72 @@ file is renamed from @file{.part} one and when you rerun @command{nncp-call} again, remote node will receive completion notification. +@node nncp-cfgenc +@section nncp-cfgenc + +@verbatim +% nncp-cfgmin [options] [-s INT] [-t INT] [-p INT] cfg.yaml > cfg.yaml.eblob +% nncp-cfgmin [options] -d cfg.yaml.eblob > cfg.yaml +@end verbatim + +This command allows you to encrypt provided @file{cfg.yaml} file with +the passphrase, producing @ref{EBlob, eblob}, to safely keep your +configuration file with private keys. This utility was written for users +who do not want (or can not) to use either @url{https://gnupg.org/, +GnuPG} or similar tools. That @file{eblob} file can be used directly in +@option{-cfg} option of nearly all commands. + +@option{-s}, @option{-t}, @option{-p} are used to tune @file{eblob}'s +password strengthening function. Space memory cost (@option{-s}), +specified in number of BLAKE2b-256 blocks (32 bytes), tells how many +memory must be used for hashing -- bigger values are better, but slower. +Time cost (@option{-t}) tells how many rounds/iterations must be +performed -- bigger is better, but slower. Number of parallel jobs +(@option{-p}) tells how many computation processes will be run: this is +the same as running that number of independent hashers and then joining +their result together. + +When invoked for encryption, passphrase is entered manually twice. When +invoked for decryption (@option{-d} option), it is asked once and exits +if passphrase can not decrypt @file{eblob}. + +@option{-dump} options parses @file{eblob} and prints parameters used +during its creation. For example: +@verbatim +% nncp-cfgenc -dump /usr/local/etc/nncp.yaml.eblob +Strengthening function: Balloon with BLAKE2b-256 +Memory space cost: 1048576 bytes +Number of rounds: 16 +Number of parallel jobs: 2 +Blob size: 2494 +@end verbatim + +@node nncp-cfgmin +@section nncp-cfgmin + +@verbatim +% nncp-cfgmin [options] > stripped.yaml +@end verbatim + +Print out stripped configuration version: only path to @ref{Spool, +spool}, path to log file, neighbours public keys are stayed. This is +useful mainly for usage with @ref{nncp-xfer} that has to know only +neighbours, without private keys involving. + +@node nncp-cfgnew +@section nncp-cfgnew + +@verbatim +% nncp-cfgnew [options] > new.yaml +@end verbatim + +Generate new node configuration: private keys, example configuration +file and print it to stdout. You must use this command when you setup +the new node. + +Pay attention that private keys generation consumes an entropy from your +operating system. + @node nncp-check @section nncp-check @@ -90,7 +158,7 @@ notification. Perform @ref{Spool, spool} directory integrity check. Read all files that has Base32-encoded filenames and compare it with recalculated -BLAKE2b hash output of their contents. This supplementary command is +BLAKE2b hash output of their contents. That supplementary command is not used often in practice, if ever. @node nncp-daemon @@ -113,7 +181,7 @@ bind to and listen. @section nncp-file @verbatim -% nncp-file [options] SRC NODE:[DST] +% nncp-file [options] [-chunked INT] SRC NODE:[DST] @end verbatim Send @file{SRC} file to remote @option{NODE}. @file{DST} specifies @@ -125,6 +193,24 @@ This command queues file in @ref{Spool, spool} directory immediately (through the temporary file of course) -- so pay attention that sending 2 GiB file will create 2 GiB outbound encrypted packet. +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://www.schneier.com/academic/twofish/, Twofish} algorithm, 256 +bit random key, zero IV, in +@url{https://en.wikipedia.org/wiki/Counter_mode#Counter_.28CTR.29, CTR} +mode. + +If @option{-chunked} is specified, then source file will be split +@ref{Chunked, on chunks}. @option{INT} is the desired chunk size in +KiBs. This mode is more CPU hungry. Pay attention that chunk is saved in +spool directory immediately and it is not deleted if any error occurs. +@option{-minsize} option is applied per each chunk. + If @ref{CfgNotify, notification} is enabled on the remote side for file transmissions, then it will sent simple letter after successful file receiving. @@ -166,32 +252,6 @@ side will execute specified @ref{CfgSendmail, sendmail} command with @option{USER}s appended as a command line argument and feed decompressed mail body to that command's stdin. -@node nncp-mincfg -@section nncp-mincfg - -@verbatim -% nncp-mincfg [options] > stripped.yaml -@end verbatim - -Print out stripped configuration version: only path to @ref{Spool, -spool}, path to log file, neighbours public keys are stayed. This is -useful mainly for usage with @ref{nncp-xfer} that has to know only -neighbours, without private keys involving. - -@node nncp-newcfg -@section nncp-newcfg - -@verbatim -% nncp-newcfg [options] > new.yaml -@end verbatim - -Generate new node configuration: private keys, example configuration -file and print it to stdout. You must use this command when you setup -the new node. - -Pay attention that private keys generation consumes an entropy from your -operating system. - @node nncp-pkt @section nncp-pkt @@ -230,6 +290,74 @@ And with the @option{-dump} option it will give you the actual payload tries to zlib-decompress the data from plain packet (useful for mail packets). +@node nncp-reass +@section nncp-reass + +@verbatim +% nncp-reass [options] [-dryrun] [-keep] [-dump] [-stdout] FILE.nncp.meta +% nncp-reass [options] [-dryrun] [-keep] {-all | -node NODE} +@end verbatim + +Reassemble @ref{Chunked, chunked file} after @ref{nncp-toss, tossing}. + +When called with @option{FILE} option, this command will reassemble only +it. When called with @option{-node} option, this command will try to +reassemble all @file{.nncp.meta} files found in @option{NODE}'s +@ref{CfgIncoming, incoming} directory. When called with @option{-all} +option, then cycle through all known nodes to do the same. + +Reassembling process does the following: + +@enumerate +@item Parses @ref{Chunked, @file{.nncp.meta}} file. +@item Checks existence and size of every @file{.nncp.chunkXXX}. +@item Verifies integrity of every chunk. +@item Concatenates all chunks, simultaneously removing them from filesystem. +@end enumerate + +That process reads the whole data twice. Be sure to have free disk +space for at least one chunk. Decrypted chunk files as a rule are saved +in pseudo-random order, so removing them during reassembly process will +likely lead to filesystem fragmentation. Reassembly process on +filesystems with deduplication capability should be rather lightweight. + +If @option{-dryrun} option is specified, then only existence and +integrity checking are performed. + +If @option{-keep} option is specified, then no +@file{.nncp.meta}/@file{.nncp.chunkXXX} files are deleted during +reassembly process. + +@option{-stdout} option outputs reassembled file to stdout, instead of +saving to temporary file with renaming after. This could be useful for +reassembling on separate filesystem to lower fragmentation effect, +and/or separate storage device for higher performance. + +@option{-dump} option prints meta-file contents in human-friendly form. +It is useful mainly for debugging purposes. For example: +@verbatim +Original filename: testfile +File size: 3.8 MiB (3987795 bytes) +Chunk size: 1.0 MiB (1048576 bytes) +Number of chunks: 4 +Checksums: + 0: eac60d819edf40b8ecdacd0b9a5a8c62de2d15eef3c8ca719eafa0be9b894017 + 1: 013a07e659f2e353d0e4339c3375c96c7fffaa2fa00875635f440bbc4631052a + 2: f4f883975a663f2252328707a30e71b2678f933b2f3103db8475b03293e4316e + 3: 0e9e229501bf0ca42d4aa07393d19406d40b179f3922a3986ef12b41019b45a3 +@end verbatim + +@node nncp-rm +@section nncp-rm + +@verbatim +% nncp-rm [options] NODE PKT +@end verbatim + +Remove specified packet (Base32 name) in @option{NODE}'s queues. This +command is useful when you want to remove the packet that is failing to +be processed. + @node nncp-stat @section nncp-stat @@ -265,7 +393,7 @@ running this command as a daemon. @section nncp-xfer @verbatim -% nncp-xfer [options] [-force] [-keep] [-rx|-tx] DIR +% nncp-xfer [options] [-mkdir] [-keep] [-rx|-tx] DIR @end verbatim Search for directory in @file{DIR} containing inbound packets for us and @@ -273,7 +401,7 @@ move them to local @ref{Spool, spool} directory. Also search for known neighbours directories and move locally queued outbound packets to them. This command is used for offline packets transmission. -If @option{-force} option is specified, then outbound neighbour(s) +If @option{-mkdir} option is specified, then outbound neighbour(s) directories will be created. This is useful for the first time usage, when storage device does not have any directories tree. @@ -283,20 +411,9 @@ remove them. @option{-rx} option tells only to move inbound packets addressed to us. @option{-tx} option tells exactly the opposite: move only outbound packets. -@ref{nncp-mincfg} could be useful for creating stripped minimalistic +@ref{nncp-cfgmin} could be useful for creating stripped minimalistic configuration file version without any private keys. @file{DIR} directory has the following structure: @file{RECIPIENT/SENDER/PACKET}, where @file{RECIPIENT} is Base32 encoded destination node, @file{SENDER} is Base32 encoded sender node. - -@node nncp-rm -@section nncp-rm - -@verbatim -% nncp-rm [options] NODE PKT -@end verbatim - -Remove specified packet (Base32 name) in @option{NODE}'s queues. This -command is useful when you want to remove the packet that is failing to -be processed. diff --git a/doc/comparison.ru.texi b/doc/comparison.ru.texi new file mode 100644 index 0000000..a4d101b --- /dev/null +++ b/doc/comparison.ru.texi @@ -0,0 +1,119 @@ +@node Сравнение +@section Сравнение с существующими решениями + +Это сравнение @url{https://ru.wikipedia.org/wiki/Uucp, UUCP} +(Unix to Unix copy), @url{https://ru.wikipedia.org/wiki/FTN, FTN} +(@url{https://ru.wikipedia.org/wiki/%D0%A4%D0%B8%D0%B4%D0%BE, FidoNet}) +и @url{https://ru.wikipedia.org/wiki/SMTP, SMTP} (так как это тоже +сохранить-и-переслать режим). + +@multitable @columnfractions 0.40 0.15 0.15 0.15 0.15 +@headitem @tab UUCP @tab FTN @tab NNCP @tab SMTP + +@item Простота настройки @tab Средне @tab Сложно @tab Легко @tab Сложно +@item Передача почты @tab @strong{Да} @tab @strong{Да} @tab @strong{Да} @tab @strong{Да} +@item Передача новостей @tab @strong{Да} @tab @strong{Да} @tab Нет @tab Нет +@item Передача файлов @tab @strong{Да} @tab @strong{Да} @tab @strong{Да} @tab Нет +@item Разбиение файлов на части @tab Нет @tab @strong{Да} @tab @strong{Да} @tab Нет +@item Удалённое исполнение команд @tab @strong{Да} @tab Нет @tab Нет @tab Нет +@item Возобновляемое скачивание @tab @strong{Да} @tab @strong{Да} @tab @strong{Да} @tab Нет +@item Приоритезация пакетов @tab @strong{Да} @tab Нет @tab @strong{Да} @tab Нет +@item Сжатие почты @tab Нет @tab @strong{Да} @tab @strong{Да} @tab Нет +@item Интеграция с SMTP @tab @strong{Да} @tab Нет @tab @strong{Да} @tab N/A +@item Push/poll модель @tab @strong{Обе} @tab @strong{Обе} @tab @strong{Обе} @tab Push +@item DTN @tab @strong{Да} @tab @strong{Да} @tab @strong{Да} @tab Нет +@item Предполагаемый размер сети @tab Дюжины @tab Глобально @tab Дюжины @tab Глобально +@item Маршрутизация @tab Ручное/статичное @tab Федеративное @tab Ручное/статичное @tab Федеративное +@item Поддержка телефонной сети @tab @strong{Да} @tab @strong{Да} @tab Возможно @tab Нет +@item Анонимные участники @tab @strong{Да} @tab Нет @tab Нет @tab @strong{Да} +@item Аутентификация участников @tab PAP @tab PAP/CHAP @tab публичный ключ @tab Нет +@item Шифрование пакетов @tab Нет @tab Нет @tab @strong{Да} @tab Нет +@item Приватность метаданных @tab Нет @tab Нет @tab @strong{Да} @tab Нет +@item Проверка целостности пакетов @tab Нет @tab Нет @tab @strong{Да} @tab Нет +@item Дружелюбность к флоппинету @tab Нет @tab Нет @tab @strong{Да} @tab Нет + +@end multitable + +@table @strong + +@item Простота установки + UUCP относительно легко настраивается несколькими строчками в + нескольких конфигурационных файлах. Но вы вынуждены добавить + дополнительный уровень шифрования и аутентификации для безопасного + обмена данными. + + FTN сложен в настройке, потому-что это совершенно другой мир + программного обеспечения, по-сравнению с Unix-ом. Даже редактор + почты будет какой-нибудь GoldEd, а не обычный почтовый клиент. Более + того, из коробки не предоставляется никакого шифрования и сильной + аутентификации. + + NNCP требует редактирование единственного YAML @ref{Configuration, + конфигурационного файла}. + +@item Передача новостей + SMTP ничего не знает о новостях, NNTP и тому подобному. NNCP тоже не + знает, потому-что на текущий день они уже мало используются. + +@item Передача файлов + SMTP может передавать файлы только в Base64 кодировке -- это очень + не эффективно. + +@item Разбиение файлов на части + FTN программы могут автоматически разбивать огромные файлы на + меньшие части, чтобы собрать их воедино на целевом узле. NNCP тоже + @ref{Chunked, поддерживает} эту возможность, особенно важную когда + дело касается переносных устройств хранения небольшого объёма. + +@item Приоритезация пакетов + UUCP и NNCP сначала будут отправлять пакеты с высоким приоритетом + ("grade" в терминологии UUCP). Ваша почта пройдёт, даже если при + этом в очереди на отправку будут гигабайты файлов. + +@item Интеграция с SMTP + Почтовые серверы типа @url{http://www.postfix.org/, Postfix} + предоставляют документацию и примеры конфигурации для использования + с UUCP. @url{http://www.exim.org/, Exim} и + @url{http://www.sendmail.com/sm/open_source/, Sendmail} тоже + относительно легко могут быть интегрированы с ним. Для использования + с NNCP, просто замените UUCP команды на аналогичные NNCP. + +@item Push/poll модель + С SMTP, вы вынуждены ждать в online режиме когда удалённые участники + отправят вам сообщение. Существуют расширения протокола позволяющие + делать poll-модель взаимодействия, но они не везде доступны и + используются. Очень важно быть независимым от заданной модели + поведения и обмениваться данными с теми возможностями которые у вас + имеются. + +@item @url{https://ru.wikipedia.org/wiki/DTN, DTN} (сеть устойчивая к разрывам) + SMTP удалит сообщения которые не могут быть доставлены в течении + длительного времени (несколько дней). Другие решения толерантны к + длительным задержкам. + +@item Маршрутизация + UUCP и NNCP ничего не знают о маршрутизации. Вы явно должны сообщать + через какие и к каким узлам нужно посылать пакет. + +@item Поддержка телефонной сети + UUCP и FidoNet всегда из коробки поддерживали работу с модемами. + Только много лет позже они получили возможность работы поверх + TCP/IP соединений. SMTP работает только поверх TCP/IP. NNCP на + данный момент имеет только TCP демон, но ничего не мешает + использовать другой 8-бит online транспорт. + +@item Анонимные участники + NNCP и FTN являются только друг-к-другу (F2F) сетью. Это очень + безопасно и предотвращает многие возможные атаки + человека-по-середине (MitM) и + @url{https://en.wikipedia.org/wiki/Sybil_attack, Sybil}. + +@item Дружелюбность к флоппинету + Никто, кроме NNCP, не поддерживает штатный обмен данными через + переносные устройства хранения типа флеш накопителей, CD-ROM-ов, + лент и жёстких дисков. Это можно сэмулировать для большинства FTN + программного обеспечения, путём ручного копирования файлов в + входящие/исходящие директории. Но UUCP и SMTP требуют ещё больше + ручной работы для этого. + +@end table diff --git a/doc/comparison.texi b/doc/comparison.texi index ed3bc48..e8edddd 100644 --- a/doc/comparison.texi +++ b/doc/comparison.texi @@ -7,19 +7,20 @@ FidoNet} Technology Networks) and @url{https://en.wikipedia.org/wiki/SMTP, SMTP} (because it is also store-and-forward solution). @multitable @columnfractions 0.40 0.15 0.15 0.15 0.15 -@headitem @tab UUCP @tab FTN @tab NNCP @tab SMTP +@headitem @tab UUCP @tab FTN @tab NNCP @tab SMTP @item Ease of setup @tab Medium @tab Hard @tab Easy @tab Hard @item Mail transmission @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @item News transmission @tab @strong{Yes} @tab @strong{Yes} @tab No @tab No @item File transmission @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @tab No +@item Chunked files @tab No @tab @strong{Yes} @tab @strong{Yes} @tab No @item Remote command execution @tab @strong{Yes} @tab No @tab No @tab No @item Resumable downloads @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @tab No @item Packets prioritizing @tab @strong{Yes} @tab No @tab @strong{Yes} @tab No @item Mail compression @tab No @tab @strong{Yes} @tab @strong{Yes} @tab No @item SMTP integration @tab @strong{Yes} @tab No @tab @strong{Yes} @tab N/A @item Push/poll @tab @strong{Both} @tab @strong{Both} @tab @strong{Both} @tab Push -@item Delay tolerant @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @tab No +@item DTN @tab @strong{Yes} @tab @strong{Yes} @tab @strong{Yes} @tab No @item Intended network size @tab Dozens @tab Global @tab Dozens @tab Global @item Routing @tab Manual/static @tab Federated @tab Manual/static @tab Federated @item PSTN support @tab @strong{Yes} @tab @strong{Yes} @tab Possible @tab No @@ -44,7 +45,8 @@ FidoNet} Technology Networks) and @url{https://en.wikipedia.org/wiki/SMTP, SMTP} like GoldEd, not an ordinary email client. Moreover, there is no out-of-box encryption and strong authentication involved. - NNCP requires single YAML file editing and nothing more. + NNCP requires editing of single YAML @ref{Configuration, + configuration file}. @item News transmission SMTP does not know anything about news, NNTP and so forth. Neither @@ -54,6 +56,12 @@ FidoNet} Technology Networks) and @url{https://en.wikipedia.org/wiki/SMTP, SMTP} SMTP could transfer files only Base64-encoding them -- this is very inefficient. +@item Chunked files + FTN software can automatically split huge files on smaller chunks, + to reassemble it on the destination node. NNCP also supports + @ref{Chunked, that feature}, especially important when dealing with + small capacity removable storage devices. + @item Packets prioritizing UUCP and NNCP will push higher priority ("grade" in UUCP terminology) packets first. You mail will pass, even when many @@ -74,11 +82,9 @@ FidoNet} Technology Networks) and @url{https://en.wikipedia.org/wiki/SMTP, SMTP} to be independent from specified model and be able to exchange the data with possibility you have. -@item Delay tolerant +@item @url{https://en.wikipedia.org/wiki/Delay-tolerant_networking, DTN} (delay tolerant networking) SMTP will drop messages that can not be delivered for a long time - (several days). Others are - @url{https://en.wikipedia.org/wiki/Delay-tolerant_networking, - tolerant} for the long delays. + (several days). Others are tolerant for the long delays. @item Routing UUCP and NNCP does not known nothing about routing. You have to @@ -93,13 +99,14 @@ FidoNet} Technology Networks) and @url{https://en.wikipedia.org/wiki/SMTP, SMTP} @item Anonymous peers NNCP and FTN are friend-to-friend networks exclusively. This is very - secure and mitigates many possible man-in-the-middle attacks. + secure and mitigates many possible man-in-the-middle (MitM) and + @url{https://en.wikipedia.org/wiki/Sybil_attack, Sybil} attacks. @item Sneakernet friendliness No one, except NNCP, supports data exchanging via removable storages - likes flash drives, CD-ROMs, tapes and hard drives. It can be - emulated for many FTN software, by manually copying files in its - inbound/outbound directories. But UUCP and SMTP software requires - more manual work to do so. + likes flash drives, CD-ROMs, tapes and hard drives out-of-box. It + can be emulated for many FTN software, by manually copying files in + its inbound/outbound directories. But UUCP and SMTP software + requires more manual work to do so. @end table diff --git a/doc/download.texi b/doc/download.texi index ed8614a..0c683d5 100644 --- a/doc/download.texi +++ b/doc/download.texi @@ -9,11 +9,12 @@ Tarballs include all necessary required libraries: @multitable @columnfractions .50 .50 @headitem Library @tab Licence +@item @code{cypherpunks.ru/balloon} @tab GNU GPLv3+ @item @code{github.com/dustin/go-humanize} @tab MIT @item @code{github.com/flynn/noise} @tab BSD 3-Clause @item @code{github.com/go-check/check} @tab BSD 2-Clause @item @code{github.com/go-yaml/yaml} @tab Apache License 2.0 and MIT -@item @code{github.com/gorhill/cronexpr} @tab GPLv3 +@item @code{github.com/gorhill/cronexpr} @tab GNU GPLv3 @item @code{github.com/minio/blake2b-simd} @tab Apache License 2.0 @item @code{golang.org/x/crypto} @tab BSD 3-Clause @item @code{golang.org/x/net} @tab BSD 3-Clause @@ -23,6 +24,10 @@ Tarballs include all necessary required libraries: @multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} @headitem Version @tab Size @tab Tarball @tab SHA256 checksum +@item @ref{Release 0.6, 0.6} @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 @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} diff --git a/doc/eblob.texi b/doc/eblob.texi new file mode 100644 index 0000000..baaa718 --- /dev/null +++ b/doc/eblob.texi @@ -0,0 +1,67 @@ +@node EBlob +@unnumbered EBlob format + +Eblob is an encrypted blob (binary large object, in the terms of +databases), holding any kind of symmetrically encrypted data with the +passphrase used to derive the key. It is used to secure configuration +files, holding valuable private keys, allowing them to be transferred +safely everywhere. + +In fact it uses two factors for securing the data: + +@itemize +@item @strong{salt}, that is kept inside @file{eblob}, something @emph{you have} +@item @strong{passphrase}, that is kept inside the head, something @emph{you know} +@end itemize + +Whole security depends on the passphrase itself. Pay attention that this +is @strong{not} the password. Password is a short string of high entropy +(highly random) characters, but passphrase is (very) long string of +low-entropy characters. Low-entropy text is much more easier to +remember, and its length provides pretty enough entropy as a result. + +Password strengthening function is applied to that passphrase to +mitigate brute-force and dictionary attacks on it. Here, +@url{https://crypto.stanford.edu/balloon/, Balloon} memory-hard password +hashing function is used, together with BLAKE2b-256 hash. It has proven +memory-hardness properties, very easy to implement, resistant to cache +attacks and seems more secure than Argon2 +(@url{https://password-hashing.net/, Password Hashing Competition} +winner). + +Eblob is an @url{https://tools.ietf.org/html/rfc4506, XDR}-encoded structure: + +@verbatim ++-------+------------------+------------+ +| MAGIC | S | T | P | SALT | BLOB | MAC | ++-------+------------------+------------+ +@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 0x01|} +@item S, T, P @tab + unsigned integer @tab + Space cost, time cost and parallel jobs number +@item Salt @tab + 32 bytes, fixed length opaque data @tab + Randomly generated salt +@item Blob @tab + variable length opaque data @tab + Encrypted data itself +@item MAC @tab + 32 bytes, fixed length opaque data @tab + BLAKE2b-256 MAC of encrypted blob +@end multitable + +Blob's encryption is done using +@url{https://www.schneier.com/academic/twofish/, Twofish} algorithm with +256-bit key in +@url{https://en.wikipedia.org/wiki/Counter_mode#Counter_.28CTR.29, CTR} +mode of operation with zero initialization vector. +@code{balloon(BLAKE2b-256, S, T, P, salt, password)} gives the main key, +that is fed to @url{https://en.wikipedia.org/wiki/HKDF, +HKDF}-BLAKE2b-256 KDF. Actual encryption key for Twofish and +authentication key for MAC are derived from that KDF. diff --git a/doc/index.texi b/doc/index.texi index ceadf1c..87c008e 100644 --- a/doc/index.texi +++ b/doc/index.texi @@ -20,39 +20,7 @@ A copy of the license is included in the section entitled "Copying conditions". @node Top @top NNCP -NNCP (Node to Node copy) is a collection of utilities simplifying -secure store-and-forward files and mail exchanging. - -This utilities are intended to help build up small size (dozens of -nodes) ad-hoc @url{https://en.wikipedia.org/wiki/Friend-to-friend, -friend-to-friend} (F2F) statically routed -@url{https://en.wikipedia.org/wiki/Darknet, darknet} networks for -fire-and-forget secure reliable files, file requests and Internet mail -transmission. All packets are integrity checked, -@url{https://en.wikipedia.org/wiki/End-to-end_encryption, end-to-end} -encrypted, explicitly authenticated by known participants public keys. -@url{https://en.wikipedia.org/wiki/Onion_routing, Onion encryption} is -applied to relayed packets. Each node acts both as a client and server, -can use push and poll behaviour model. - -Out-of-box offline @url{https://en.wikipedia.org/wiki/Sneakernet, -sneakernet/floppynet}, @url{https://en.wikipedia.org/wiki/Dead_drop, -dead drops} and @url{https://en.wikipedia.org/wiki/Air_gap_(networking), -air-gapped} computers support. But online TCP daemon with full-duplex -resumable data transmission exists. - -NNCP is @url{https://www.gnu.org/philosophy/pragmatic.html, copylefted} -@url{https://www.gnu.org/philosophy/free-sw.html, free software} -licenced under @url{https://www.gnu.org/licenses/gpl-3.0.html, GPLv3+}. -It should work on all @url{https://en.wikipedia.org/wiki/POSIX, -POSIX}-compatible systems. Easy integration with existing -@url{https://en.wikipedia.org/wiki/SMTP, SMTP} servers. Single -@url{http://yaml.org/, YAML} configuration file. - -Why create yet another store-and-forward solution when UUCP, FTN and -even SMTP exists? Look in @ref{Comparison, comparison} section! -Simplicity, cryptographic security, sneakernet compatibility and easy -integration with current SMTP servers are the reasons. +@include about.texi @center Interested? @ref{Tarballs, @strong{Download it}}! @@ -61,16 +29,19 @@ integration with current SMTP servers are the reasons. * Use cases:: * Workflow:: * News:: +* Информация на русском: Русский. * Installation:: * Configuration:: * Call configuration: Call. * Integration with Postfix: Postfix. * Commands:: * Niceness:: +* Chunked files: Chunked. * Spool directory: Spool. * Log format: Log. * Packet format: Packet. * Sync protocol: Sync. +* EBlob format: EBlob. * Thanks:: * Contacts and feedback: Contacts. * Copying conditions: Copying. @@ -80,16 +51,19 @@ integration with current SMTP servers are the reasons. @include usecases.texi @include workflow.texi @include news.texi +@include russian.texi @include install.texi @include cfg.texi @include call.texi @include postfix.texi @include cmds.texi @include niceness.texi +@include chunked.texi @include spool.texi @include log.texi @include pkt.texi @include sp.texi +@include eblob.texi @include thanks.texi @include contacts.texi diff --git a/doc/install.texi b/doc/install.texi index 9dec382..eb85846 100644 --- a/doc/install.texi +++ b/doc/install.texi @@ -1,8 +1,7 @@ @node Installation @unnumbered Installation -NNCP should run on any POSIX-compatible operating system with file -systems supporting directory and filename's length up to 57 characters. +NNCP should run on any POSIX-compatible operating system. NNCP is written on @url{https://golang.org/, Go} programming language and you have to install Go compiler (1.7+ version is highly diff --git a/doc/news.ru.texi b/doc/news.ru.texi new file mode 100644 index 0000000..2f6bc2e --- /dev/null +++ b/doc/news.ru.texi @@ -0,0 +1,93 @@ +@node Новости +@section Новости + +@node Релиз 0.7 +@subsection Релиз 0.7 +@itemize +@item +Возможность предоставлять данные для @command{nncp-file} через +стандартный ввод, используя временный зашифрованный файл для этого. + +@item +Появилась возможность передачи файлов разбитых на части, с сопутствующей +@command{nncp-reass} командой и @option{freqchunked} опцией +конфигурационного файла. Полезно для передачи больших файлов через +маленькие устройства хранения. + +@item +@option{freqminsize} опция конфигурационного файла, аналогичная +@option{-minsize}. + +@item +Опция @option{-force} команды @command{nncp-xfer} переименована в +@option{-mkdir} для ясности. + +@item +Опция @option{-minsize} задётся в KiB, а не байтах, для удобства. + +@item +Команда @command{nncp-newcfg} переименована в @command{nncp-cfgnew}, +а @command{nncp-mincfg} в @command{nncp-cfgmin}, для того чтобы они +имели общий префикс и были сгруппированы для удобства. + +@item Появилась команда @command{nncp-cfgenc}, позволяющая +шифровать/дешифровать конфигурационный файл, чтобы безопасно его хранить +без использования OpenPGP или других подобных инструментов. + +@item +Обновлены зависимые криптографические библиотеки. +@end itemize + +@node Релиз 0.6 +@subsection Релиз 0.6 +@itemize +@item Появилась небольшая команда @command{nncp-rm}. +@item Обновлены зависимые криптографические библиотеки. +@end itemize + +@node Релиз 0.5 +@subsection Релиз 0.5 +@itemize +@item Тривиальное небольшое исправление в значениях приоритетов +по-умолчанию в @command{nncp-file} и @command{nncp-freq} командах. +@end itemize + +@node Релиз 0.4 +@subsection Релиз 0.4 +@itemize +@item Небольшое исправление в @command{nncp-call}, @command{nncp-caller}, +@command{nncp-daemon}: иногда они могли падать с segmentation fault +ошибкой (данные не терялись). +@item @command{nncp-newnode} переименована в @command{nncp-newcfg} -- +это короче и удобнее для использования. +@item Появилась команда @command{nncp-mincfg}: вспомогательная утилита +позволяющая создать минималистичный урезанный конфигурационный файл без +приватных ключей, что полезно во время использования @command{nncp-xfer}. +@end itemize + +@node Релиз 0.3 +@subsection Релиз 0.3 +Исправлена совместимость с Go 1.6. + +@node Релиз 0.2 +@subsection Релиз 0.2 +@itemize +@item @strong{Несовместимое} изменение формата пакета (магическое число +тоже изменено): поле размера пакета шифруется и не посылается в открытом +виде. +@item @option{-minsize} опция даёт возможность автоматически дополнять +исходящие пакеты до указанного минимального размера. +@item @command{nncp-daemon} и +@command{nncp-call}/@command{nncp-caller} всегда в фоне проверяют появление +исходящих @emph{tx} пакетов пока подключены. Удалённая сторона сразу же +оповещается об этом. +@item @option{-onlinedeadline} опция даёт возможность выставления +timeout-а на неактивность в online соединении, когда оно должно быть +отключено. Она может быть использована для сохранения соединения на +долгое время. +@item @option{-maxonlinetime} опция даёт возможность указания +максимального возможного времени жизни соединения. +@item Появилась @command{nncp-caller} команда: клиент TCP-демона +работающий по cron-у. +@item @command{nncp-pkt} команда может разжимать данные. +@end itemize diff --git a/doc/news.texi b/doc/news.texi index ec43ffc..7bd287c 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -1,11 +1,51 @@ @node News @unnumbered News +See also this page @ref{Новости, on russian}. + +@node Release 0.7 +@section Release 0.7 +@itemize +@item +Ability to feed @command{nncp-file} from stdin, that uses an encrypted +temporary file for that. + +@item +Chunked files transmission appeared with corresponding +@command{nncp-reass} command and @option{freqchunked} configuration file +entry. Useful for transferring big files over small storage devices. + +@item +@option{freqminsize} configuration file option, analogue to +@option{-minsize} one. + +@item +@command{nncp-xfer}'s @option{-force} option is renamed to +@option{-mkdir} for clarity. + +@item +@option{-minsize} option is specified in KiBs, not bytes, for +convenience. + +@item +@command{nncp-newcfg} command is renamed to @command{nncp-cfgnew}, +and @command{nncp-mincfg} to @command{nncp-cfgmin} -- now they have +common prefix and are grouped together for convenience. + +@item +@command{nncp-cfgenc} command appeared, allowing configuration file +encryption/decryption, for keeping it safe without any either OpenPGP or +similar tools usage. + +@item +Cryptographic libraries (dependencies) are updated. +@end itemize + @node Release 0.6 @section Release 0.6 @itemize @item Small @command{nncp-rm} command appeared. -@item Cryptographic libraries (dependecies) are updated. +@item Cryptographic libraries (dependencies) are updated. @end itemize @node Release 0.5 @@ -24,7 +64,7 @@ lost). @item @command{nncp-newnode} renamed to @command{nncp-newcfg} -- it is shorter and more convenient to use. @item @command{nncp-mincfg} command appeared: helper allowing to create -minimalistic stripped down configuration files without private keys, +minimalistic stripped down configuration file without private keys, that is useful during @command{nncp-xfer} usage. @end itemize diff --git a/doc/pkt.texi b/doc/pkt.texi index ef6bc0a..4549505 100644 --- a/doc/pkt.texi +++ b/doc/pkt.texi @@ -2,8 +2,7 @@ @unnumbered Packet format All packets are -@url{https://en.wikipedia.org/wiki/External_Data_Representation, -XDR}-encoded structures. +@url{https://tools.ietf.org/html/rfc4506, XDR}-encoded structures. @menu * Plain packet: Plain. @@ -26,7 +25,7 @@ drive. @end verbatim @multitable @columnfractions 0.2 0.3 0.5 -@headitem @tab XDR type @tab Value +@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|} @@ -81,7 +80,7 @@ Each encrypted packet has the following header: @end verbatim @multitable @columnfractions 0.2 0.3 0.5 -@headitem @tab XDR type @tab Value +@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 0x01|} @@ -120,7 +119,7 @@ BLAKE2b-256} MAC is appended to the ciphertext. After the headers comes an encrypted payload size and MAC of that size. @multitable @columnfractions 0.2 0.3 0.5 -@headitem @tab XDR type @tab Value +@headitem @tab XDR type @tab Value @item Size @tab unsigned hyper integer @tab Payload size. diff --git a/doc/russian.texi b/doc/russian.texi new file mode 100644 index 0000000..a377ea3 --- /dev/null +++ b/doc/russian.texi @@ -0,0 +1,14 @@ +@node Русский +@unnumbered Информация на русском + +@menu +* Подробнее об утилитах NNCP: Об утилитах. +* Сравнение с существующими решениями: Сравнение. +* Сценарии использования:: +* Новости:: +@end menu + +@include about.ru.texi +@include comparison.ru.texi +@include usecases.ru.texi +@include news.ru.texi diff --git a/doc/sources.texi b/doc/sources.texi index a259df0..83c2b4a 100644 --- a/doc/sources.texi +++ b/doc/sources.texi @@ -7,7 +7,7 @@ libraries source code. Because of that, it is recommended for porters to use @ref{Tarballs, tarballs} instead. You can obtain it by cloning @url{http://git-scm.com/, Git} -@url{http://git.cypherpunks.ru/cgit.cgi/nncp.git/log/, repository} +@url{https://git.cypherpunks.ru/cgit.cgi/nncp.git/log/, repository} and fetching dependent libraries source code as git submodules: @verbatim @@ -16,5 +16,3 @@ and fetching dependent libraries source code as git submodules: % git checkout develop % git submodule update --init @end verbatim - -Github.com mirror exists: @url{https://github.com/stargrave/nncp}. diff --git a/doc/sp.texi b/doc/sp.texi index dca38c3..919cca1 100644 --- a/doc/sp.texi +++ b/doc/sp.texi @@ -15,7 +15,7 @@ unacceptable performance degradation. SP works on top of @url{http://noiseprotocol.org/noise.html#interactive-patterns, @code{Noise_IK_25519_ChaChaPoly_BLAKE2b}} protocol. Each Noise packet -are sent inside XDR envelope: +is sent inside an @url{https://tools.ietf.org/html/rfc4506, XDR} envelope: @verbatim +-----------------+ @@ -24,7 +24,7 @@ are sent inside XDR envelope: @end verbatim @multitable @columnfractions 0.2 0.3 0.5 -@headitem @tab XDR type @tab Value +@headitem @tab XDR type @tab Value @item Magic number @tab 8-byte, fixed length opaque data @tab @verb{|N N C P S 0x00 0x00 0x01|} @@ -68,7 +68,7 @@ just an unsigned integer telling what body structure follows. +------+--------------------+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 - @headitem @tab XDR type @tab Value + @headitem @tab XDR type @tab Value @item Niceness @tab unsigned integer @tab 1-255, file niceness level @@ -89,7 +89,7 @@ just an unsigned integer telling what body structure follows. +------+---------------+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 - @headitem @tab XDR type @tab Value + @headitem @tab XDR type @tab Value @item Hash @tab 32-byte, fixed length opaque data @tab Unique file identifier, its checksum @@ -106,7 +106,7 @@ just an unsigned integer telling what body structure follows. +------+-------------------------+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 - @headitem @tab XDR type @tab Value + @headitem @tab XDR type @tab Value @item Hash @tab 32-byte, fixed length opaque data @tab Unique file identifier, its checksum @@ -126,7 +126,7 @@ just an unsigned integer telling what body structure follows. +------+------+ @end verbatim @multitable @columnfractions 0.2 0.3 0.5 - @headitem @tab XDR type @tab Value + @headitem @tab XDR type @tab Value @item Hash @tab 32-byte, fixed length opaque data @tab Unique file identifier, its checksum @@ -134,7 +134,7 @@ just an unsigned integer telling what body structure follows. @end table -Typical peers behaviour is following: +Typical peer's behaviour is following: @enumerate @item Perform Noise-IK handshake. @@ -163,7 +163,7 @@ delete @file{.part} suffix from file's name and send @emph{DONE} packet. @item When @emph{HALT} packet received, empty file sending queue. @item @emph{FILE} sending is performed only if no other outgoing packets are queued. -@item Each second node check are there any new @emph{tx} packets +@item Each second, node checks: are there any new @emph{tx} packets appeared and queues corresponding @emph{INFO} packets. @item If no packets are sent and received during @ref{CfgOnlineDeadline, onlinedeadline} duration, then close the connection. There is no diff --git a/doc/usecases.ru.texi b/doc/usecases.ru.texi new file mode 100644 index 0000000..19885c7 --- /dev/null +++ b/doc/usecases.ru.texi @@ -0,0 +1,298 @@ +@node Сценарии использования +@section Сценарии использования + +@menu +* Доступность почтового сервера время от времени: UsecaseMailRU. +* Легковесная и быстрая замена POP3/IMAP4: UsecasePOPRU. +* Ненадёжный/дорогой канал связи: UsecaseUnreliableRU. +* Медленная/дорогая связь для больших объёмов данных, плохой QoS: UsecaseQoSRU. +* Экстремальные наземные окружающие условия, нет связи: UsecaseNoLinkRU. +* Частные, изолированные MitM/Sybil-устойчивые сети: UsecaseF2FRU. +* Высоко защищённые изолированные компьютеры с воздушным зазором: UsecaseAirgapRU. +* Обход сетевой цензуры, здоровье: UsecaseCensorRU. +* Разведка, шпионаж, тайная агентура: UsecaseSpyRU. +@end menu + +@node UsecaseMailRU +@subsection Доступность почтового сервера время от времени + +Представьте, что у вас есть собственный @url{http://www.postfix.org/, +Postfix} SMTP сервер подключённый к Интернету. Но вы читаете и пишете +почтовые сообщения на своём ноутбуке, который подключается к нему лишь +время от времени. Как опустошить очередь из ожидающих сообщений когда +ноутбук подключён? + +Одна из возможностей это войти на сервер и сделать что-то типа +@command{postqueue -f}, но по-умолчанию у вас есть только несколько дней +на это, плюс отправитель будет получать уведомления о том, что его +сообщение всё ещё не доставлено. Кроме того, вы должны использовать +безопасный канал связи (SSH, VPN, итд). + +Другая возможность это использовать POP3/IMAP4 сервер, но это слишком +переусложнённо и громоздко для такой простой задачи. Не вариант. +@url{https://ru.wikipedia.org/wiki/KISS_(%D0%BF%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF), +KISS}! + +Просто скажите вашим обоим Postfix-ам (на сервере и ноутбуке) отправлять +сообщения через NNCP (@ref{nncp-mail}) на заданный узел. Это делается +аналогично тому как с UUCP, и описано в +@url{http://www.postfix.org/UUCP_README.html, документации Postfix}. + +Читайте @ref{Postfix, здесь} для более подробной информации. Вся почта +будет сохранятся в NNCP @ref{Spool, спуле}, который после обмена данных +и распаковки вызовет локальный @command{sendmail} для доставки почты, +как-будто это произошло на этой же машине. + +@node UsecasePOPRU +@subsection Легковесная и быстрая замена POP3/IMAP4 + +@ref{nncp-daemon} может быть соединён с @ref{nncp-caller} длительное +время -- он создаёт TCP соединение на многие часы. Когда SMTP сервер +получает письмо, то вызывает @ref{nncp-mail} для создания исходящего +зашифрованного пакета. Демон ежесекундно проверяет исходящую директорию +и сразу же посылает оповещение о недоставленных пакетах противоположной +стороне, которая сразу же их может скачать. + +Всего несколько дюжин байт оповещают о входящих пакетах, дюжины байт +начинающие доставку этих пакетов. Почтовые пакеты сжимаются (POP3 и +IMAP4, как правило, нет). У вас легковесный, сжатый, надёжный канал +связи с низкими задержками для почты, с сильным шифрованием и +двусторонней аутентификацией! + +@node UsecaseUnreliableRU +@subsection Ненадёжный/дорогой канал связи + +Представьте, что у вас медленный модем/радио/спутниковый канал связи, +который часто обрывается и вызывает timeout у TCP. Не все HTTP серверы +поддерживают возобновляемые скачивания. SMTP вообще не поддерживает +продолжение оборванного приёма и тяжёлые сообщения становится очень +проблематично получить. Более того, каждый обрыв может приводить к +отсылке данных с самого начала, что не всегда по карману. + +Просто отправьте вашу @ref{nncp-mail, почту} и @ref{nncp-file, файлы} +через NNCP. Вы сможете использовать или offline методы доставки -- +читайте о них в следующем разделе, либо использовать поставляемый NNCP +@ref{nncp-daemon, TCP демон}. + +Команды: + +@verbatim +% nncp-file file_i_want_to_send bob: +% nncp-file another_file bob:movie.avi +@end verbatim + +добавят в очередь отправки два файла для узла @emph{bob}. +Выстрелил-и-забыл! Теперь это работа демона (или offline передачи) +доставить частями эти файлы до удалённой системы когда она будет +доступна. + +@node UsecaseQoSRU +@subsection Медленная/дорогая связь для больших объёмов данных, плохой QoS + +Представьте, что относительно дешёвый 2 TiB переносной жёсткий диск вы +отдаёте кому-нибудь утром каждый день (и забираете назад вечером). Это +равносильно 185 мегабитному качественному однонаправленному каналу +связи. Как насчёт большего количества и бОльших жёстких дисков? Этот +метод обмена данными называется +@url{https://ru.wikipedia.org/wiki/%D0%A4%D0%BB%D0%BE%D0%BF%D0%BF%D0%B8%D0%BD%D0%B5%D1%82, +флоппинет}. + +NNCP поддерживает @ref{Niceness, приоритезацию трафика}: каждый пакет +имеет уровень "приятности", который гарантирует что он будет обработан +раньше или позднее остальных. Почти все команды имеют соответствующую +опцию: + +@verbatim +% nncp-file -nice 32 myfile node:dst +% nncp-xfer -nice 192 /mnt/shared +% nncp-call -nice 224 bob +[...] +@end verbatim + +Огромные файлы могут быть разбиты на маленькие @ref{Chunked, части}, +давая возможность передачи, по сути, любых объёмов используя накопители +небольших размеров. + +@node UsecaseNoLinkRU +@subsection Экстремальные наземные окружающие условия, нет связи + +Это, в некотором роде, вариант очень медленного канала связи. Offline +методы доставки -- единственный выбор. Просто отправьте, файлы как было +показано в предыдущем разделе, но используйте переносные накопители для +передачи пакетов другим узлам. + +Представьте, что вы послали два файла узлу @emph{bob}. Вставьте USB +устройство хранения, подмонтируйте и запустите @ref{nncp-xfer}: + +@verbatim +% nncp-xfer -node bob /media/usbstick +@end verbatim + +чтобы скопировать все исходящие пакеты относящиеся к @emph{bob}. +Используйте @option{-mkdir} опцию чтобы создать все необходимые +директории на накопителе, если их нет (например когда запускаемся первый +раз). + +Если вы используете один и тот же накопитель для передачи данных и к +@emph{bob} и к @emph{alice}, то тогда просто не указывайте +@option{-node} опцию, чтобы скопировать все доступные исходящие пакеты. + +@verbatim +% nncp-xfer /media/usbstick +@end verbatim + +Размонтируйте и передайте накопитель Бобу и Алисе. Когда они вставят +накопитель в свои компьютеры, то выполнят точно такую же команду: + +@verbatim +% nncp-xfer /media/usbstick +@end verbatim + +чтобы найти все пакеты относящиеся к их узлу и локально скопируют для +дальнейшей обработки. @command{nncp-xfer} это единственная команда +используемая с переносными устройствами хранения. + +@node UsecaseF2FRU +@subsection Частные, изолированные MitM/Sybil-устойчивые сети + +Все Интернет соединения могут быть прослушаны и сфальсифицированы. Вы +@strong{вынуждены} использовать шифрование и аутентификацию для +безопасности. Но очень сложно обезопасить метаданные, которые утекают +при каждой online сессии. Когда вы запускаете свой новый сверкающий +программный сервер, то имейте в виду, что может существовать огромное +количество поддельных узлов пытающихся произвести +@url{https://en.wikipedia.org/wiki/Sybil_attack, Sybil атаку}. Открытые +узел-к-узлу (peer-to-peer) сети опасны. + +Наиболее популярный криптографический протокол в Интернете это +@url{https://ru.wikipedia.org/wiki/TLS, TLS}, который крайне сложно +правильно реализовать и сконфигурировать для двусторонней аутентификации +собеседников. Не все конфигурации TLS обладают свойством +@url{https://ru.wikipedia.org/wiki/Perfect_forward_secrecy, совершенной +прямой секретности} -- все ранее перехваченные пакеты могут быть +прочтены если приватные ключи скомпрометированы. + +Друг-к-другу (friend-to-friend) сети, "тёмные сети" (darknet) могут +нивелировать возможные риски связанные с поддельными и фиктивными +узлами. Хотя они и сложнее в поддержке и требуют больше затрат на +построение. + +@ref{nncp-daemon, TCP демон} NNCP использует +@url{http://noiseprotocol.org/, Noise-IK} протокол для двусторонней +аутентификации узлов и предоставляет эффективный (оба участника могут +отослать полезную нагрузку сразу же в самом первом пакете) безопасный +транспорт с свойством совершенной прямой секретности. + +@verbatim +% nncp-daemon -bind [::]:5400 +@end verbatim +запустит TCP демон, который будет слушать входящие соединения на всех +интерфейсах. + +@verbatim +% nncp-call bob +@end verbatim +попытается подключиться к известному TCP-адресу узла @emph{bob} (взятого +из конфигурационного файла), послать все связанные с ним исходящие +пакеты и получить от него. Все прерванные передачи будут автоматически +возобновлены. + +@node UsecaseAirgapRU +@subsection Высокозащищённые изолированные компьютеры с воздушным зазором + +Если вы сильно беспокоитесь о безопасности, то компьютер с +@url{https://ru.wikipedia.org/wiki/%D0%92%D0%BE%D0%B7%D0%B4%D1%83%D1%88%D0%BD%D1%8B%D0%B9_%D0%B7%D0%B0%D0%B7%D0%BE%D1%80_(%D1%81%D0%B5%D1%82%D0%B8_%D0%BF%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D1%87%D0%B8_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85), +воздушным зазором} может будет вашим единственным позволительным +выбором. Компьютер без каких-либо модемов, проводных и беспроводных +сетей. Очевидно, что единственная возможность обмениваться почтой и +файлами -- использовать физически переносимые устройства хранения типа +CD-ROM, жёстких дисков, лент и USB накопителей (худший вариант, из-за +сложности подобных устройств). + +Предполагаем что у вас есть ещё один собственный узел, стоящий "до" +безопасного, который делает базовые проверки полученных накопителей, +возможно перезаписывая данные с USB/жёстких дисков на CD-RW. + +NNCP из коробки поддерживает ретрансляцию пакетов. + +@verbatim +neigh: + bob: + [...] + addrs: + lan: [fe80::5400%igb0]:5400 + bob-airgap: + [...] + via: [bob] +@end verbatim + +Такой @ref{Configuration, конфигурационный файл} говорит что у нас есть +два известных соседа: @emph{bob} и @emph{bob-airgap}. @emph{bob} +доступен через online соединение, используя @emph{lan} адрес. +@emph{bob-airgap} доступен путём посылки промежуточного ретранслируемого +пакета через узел @emph{bob}. + +Любая команда типа @command{nncp-file myfile bob-airgap:} автоматически +создаст инкапсулированный пакет: один непосредственно для целевой точки, +а другой несущий его для промежуточного узла. + +Имейте в виду, что узел-ретранслятор ничего не знает о внутреннем +пакете, кроме его полного размера и приоритета. Все промежуточные пакеты +тоже зашифрованы: используя хорошо известную технологию +@url{https://ru.wikipedia.org/wiki/%D0%9B%D1%83%D0%BA%D0%BE%D0%B2%D0%B0%D1%8F_%D0%BC%D0%B0%D1%80%D1%88%D1%80%D1%83%D1%82%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F, +луковой маршрутизации}. @emph{bob} не может прочитать пакеты +@emph{bob-airgap}. + +@node UsecaseCensorRU +@subsection Обход сетевой цензуры, здоровье + +Это тоже подвид плохого канала связи. Некоторые правительства склонны к +запрету @strong{любого} вида личного (приватного) общения между людьми, +разрешая только доставку развлекательного контента и доступ к популярным +социальным сетям (которые уже вовсю наводнены рекламой, локально +исполняемым @url{https://www.gnu.org/philosophy/free-sw.ru.html, +проприетарным} JavaScript кодом (для слежкой за действиями пользователя, +сбором данных), бесстыдно и бессовестно эксплуатируя базовые потребности +человека в общении). + +Это их естественное желание. Но никто вас не заставляет насильно +подчиняться огромным корпорациям типа Apple, Google или Microsoft. Ваш +выбор это создавать изолированные друг-к-другу сети с кучами безобидного +контента и приватными сообщениями. Только хищники тихо наблюдают за +своими жертвами в мире млекопитающих -- слежка и чувство что вы жертва, +сделавшая что-то плохое, вредит вашему здоровью. + +@node UsecaseSpyRU +@subsection Разведка, шпионаж, тайная агентура + +Эти ребята знают насколько небезопасен Интернет, несовместим с +понятием приватности. Им необходим быстрый сброс и забор данных. Нет +возможности провести несколько итераций туда-обратно (round trip) -- +только сбросить данные, выстрелить и забыть. Опять же, это может быть +переносной накопитель и/или +@url{https://en.wikipedia.org/wiki/USB_dead_drop, USB тайник} (dead drop), +@url{https://en.wikipedia.org/wiki/PirateBox, PirateBox}ы, +@url{https://en.wikipedia.org/wiki/Short-range_agent_communications, +связь малой дальности (SRAC)}. Короткоживущие сети малой дальности типа +Bluetooth и WiFi могут быть и довольно быстрыми, позволяя быстро +"выстреливать" порциями исходящих пакетов. + +Очень важное свойство -- компрометация этих тайников или накопителей не +должна быть ни фатальна, ни даже опасна. Пакеты посылаемые через сети +или обмениваемые через устройства -- @ref{Encrypted, зашифрованы} по +принципу точка-точка (но, к сожалению, без совершенной прямой +секретности). Никаких имён файлов, получателей почтовых сообщений не +видно. + +Общение узлов между собой происходит в, так называемой, @ref{Spool, +спул} области: директории содержащей только необработанные зашифрованные +пакеты. После передачи пакета вы всё-равно не сможете его прочитать: +необходимо запустить другую фазу: @ref{nncp-toss, распаковку}, которая +использует ваши приватные криптографические ключи. То есть, даже если вы +потеряете свой компьютер, устройства хранения и тому прочее -- это не +так плохо, потому-что вы не носите с собой приватные ключи (ведь так?), +вы не "распаковываете" эти пакеты сразу же на том же самом устройстве. +Распаковка (чтение этих зашифрованных пакетов с извлечением переданных +файлов и почтовых сообщений) может и должна бы быть произведена на +отдельном компьютере (@ref{nncp-cfgmin} команда может помочь с созданием +конфигурационного файла без приватных ключей для этой цели). diff --git a/doc/usecases.texi b/doc/usecases.texi index d45eeb4..5f8fdcb 100644 --- a/doc/usecases.texi +++ b/doc/usecases.texi @@ -7,9 +7,9 @@ * Unreliable/expensive communication link: UsecaseUnreliable. * Slow/expensive link for high-volume data, bad QoS: UsecaseQoS. * Extreme terrestrial environments, no link: UsecaseNoLink. -* Private, isolated MitM-resistant networks: UsecaseF2F. +* Private, isolated MitM/Sybil-resistant networks: UsecaseF2F. * Highly secure isolated air-gap computers: UsecaseAirgap. -* Network censorship bypassing: UsecaseCensor. +* Network censorship bypassing, health: UsecaseCensor. * Reconnaissance, spying, intelligence, covert agents: UsecaseSpy. @end menu @@ -27,7 +27,8 @@ receive notification emails that his messages still are not delivered yet. Also you must have secure link (SSH, VPN, etc). Another possibility is to use POP3/IMAP4 servers, but this is too -overcomplicated and bloated for the simple task. Not an option. KISS! +overcomplicated and bloated for the simple task. Not an option. +@url{https://en.wikipedia.org/wiki/KISS_principle, KISS}! Just tell both of your Postfixes (on the server and notebook) to drop email as a mail via NNCP (@ref{nncp-mail}) to specified node. This is @@ -76,9 +77,9 @@ The command: % nncp-file another_file bob:movie.avi @end verbatim -will queue two files for sending to @code{emph} node. Fire and forget! -Now this is daemon's job (or offline transfer) to send this file part by -part to remote system when it is available. +will queue two files for sending to @emph{bob} node. Fire and forget! +Now this is daemon's job (or offline transfer) to send this files part +by part to remote system when it is available. @node UsecaseQoS @section Slow/expensive link for high-volume data, bad QoS @@ -101,6 +102,10 @@ later than the other ones. Nearly all commands has corresponding option: [...] @end verbatim +Huge files could be split on smaller @ref{Chunked, chunks}, giving +possibility to transfer virtually any volumes using small capacity +storages. + @node UsecaseNoLink @section Extreme terrestrial environments, no link @@ -115,20 +120,19 @@ device, mount it and run @ref{nncp-xfer}: % nncp-xfer -node bob /media/usbstick @end verbatim -to copy all outbound packets related to @emph{bob}'s node. Use -@option{-force} option to forcefully create related directory on USB -storage if they are missing (for example when running for the first -time). +to copy all outbound packets related to @emph{bob}. Use @option{-mkdir} +option to create related directory on USB storage if they are missing +(for example when running for the first time). If you use single storage device to transfer data both to @emph{bob} and -@emph{alice}, then just omit @option{-node} option to copy all existing -outgoing packets to that storage device. +@emph{alice}, then just omit @option{-node} option to copy all available +outgoing packets. @verbatim % nncp-xfer /media/usbstick @end verbatim -Unmount it and transfer somehow to Bob and Alice. When they will insert +Unmount it and transfer storage to Bob and Alice. When they will insert it in their computers, they will use exactly the same command: @verbatim @@ -136,11 +140,11 @@ it in their computers, they will use exactly the same command: @end verbatim to find all packets related to their node and copy them locally for -further processing. nncp-xfer is the only command used with removable -devices. +further processing. @command{nncp-xfer} is the only command used with +removable devices. @node UsecaseF2F -@section Private, isolated MitM-resistant networks +@section Private, isolated MitM/Sybil-resistant networks All Internet connections can be eavesdropped and forged. You @strong{have to} to use encryption and authentication for securing them. @@ -152,7 +156,7 @@ peer-to-peer networking is dangerous thing to do. The most popular cryptographic protocol in Internet is @url{https://en.wikipedia.org/wiki/Transport_Layer_Security, TLS} that -is very hard to implement right and hard to configure for mutual +is very hard to implement correctly and hard to configure for mutual participants authentication. Not all TLS configurations and related protocols provide @url{https://en.wikipedia.org/wiki/Forward_secrecy, forward secrecy} property -- all previously intercepted packets could be @@ -220,18 +224,20 @@ automatically create an encapsulated packet: one for the destination endpoint, and other carrying it for intermediate relaying node. Pay attention that relaying node knows nothing about the packet inside, -but just its size and priority. Transition packets are encrypted too. -@emph{bob} can not read @emph{bob-airgap}'s packets. +but just its size and priority. Transition packets are encrypted too: +using well-known @url{https://en.wikipedia.org/wiki/Onion_routing, onion +routing} technology. @emph{bob} can not read @emph{bob-airgap}'s packets. @node UsecaseCensor -@section Network censorship bypassing +@section Network censorship bypassing, health This is some kind of bad link too. Some governments tend to forbid @strong{any} kind of private communication between people, allowing only entertainment content delivering and popular social networks access (that are already bloated with advertisements, locally executed -proprietary JavaScript code (for spying on user activities, collect data -on them), shamelessly exploiting the very basic human need of communication). +@url{https://www.gnu.org/philosophy/free-sw.html, proprietary} +JavaScript code (for spying on user activities, collect data on them), +shamelessly exploiting the very basic human need of communication). This is their natural wish. But nobody forces you to obey huge corporations like Apple, Google or Microsoft. It is your choice to @@ -246,9 +252,9 @@ feeling that you are the victim that has already done something wrong. Those guys know how Internet is a dangerous place incompatible with privacy. They require quick, fast dropping and picking of data. No possibility of many round-trips -- just drop the data, fire-and-forget. -It could be either removable media again, or -@url{https://en.wikipedia.org/wiki/USB_dead_drop, USB dead drops}, or -@url{https://en.wikipedia.org/wiki/PirateBox, PirateBox}es, or +It could be either removable media again and/or +@url{https://en.wikipedia.org/wiki/USB_dead_drop, USB dead drops}, +@url{https://en.wikipedia.org/wiki/PirateBox, PirateBox}es, @url{https://en.wikipedia.org/wiki/Short-range_agent_communications, SRAC}. Short lived short range networks like Bluetooth and WiFi can also be pretty fast, allowing to quickly fire chunks of queued packets. @@ -259,7 +265,7 @@ the network and exchanged via those devices are end-to-end @ref{Encrypted, encrypted} (but unfortunately lacking forward secrecy). No filenames, mail recipients are seen. -All communications are done with so-called @ref{Spool, spool} area: +All node communications are done with so-called @ref{Spool, spool} area: directory containing only those unprocessed encrypted packets. After packet transfer you still can not read any of them: you have to run another stage: @ref{nncp-toss, tossing}, that involves your private @@ -268,4 +274,5 @@ and so on -- it is not so bad, because you are not carrying private keys with it (don't you?), you do not "toss" those packets immediately on the same device. Tossing (reading those encrypted packets and extracting transferred files and mail messages) could and should be done on a -separate computer. +separate computer (@ref{nncp-cfgmin} command could help creating +configuration file without private keys for that purpose). diff --git a/doc/workflow.texi b/doc/workflow.texi index 5ec2c73..137e7f9 100644 --- a/doc/workflow.texi +++ b/doc/workflow.texi @@ -5,7 +5,7 @@ NNCP consists of several utilities. As a rule you will have the following workflow: @enumerate -@item Run @ref{nncp-newcfg} on each node to create an initial +@item Run @ref{nncp-cfgnew} on each node to create an initial @ref{Configuration, configuration} file. @item Tune it up and set at least @ref{Spool, spool} and log paths. @item Share your public keys and reachability addressees with your diff --git a/makedist.sh b/makedist.sh index f6a8f0e..a68f436 100755 --- a/makedist.sh +++ b/makedist.sh @@ -39,6 +39,7 @@ golang.org/x/crypto/hkdf golang.org/x/crypto/nacl golang.org/x/crypto/poly1305 golang.org/x/crypto/salsa20 +golang.org/x/crypto/ssh/terminal golang.org/x/crypto/twofish golang.org/x/net/AUTHORS golang.org/x/net/CONTRIBUTORS @@ -71,8 +72,8 @@ You can obtain releases source code prepared tarballs on @url{http://www.nncpgo.org/}. EOF make -C doc -./news_and_install.sh -rm -r doc/.well-known doc/nncp.html/.well-known news_and_install.sh +./supplementary_files.sh +rm -r doc/.well-known doc/nncp.html/.well-known supplementary_files.sh find . -name .git -type d | xargs rm -fr find . -name .gitignore -delete @@ -97,7 +98,7 @@ EOF cd $cur cat <8 ------------------------ + +Основные усовершенствования в этом релизе: + +$(git cat-file -p $release | sed -n '6,/^.*BEGIN/p' | sed '$d') + +------------------------ >8 ------------------------ + +Домашняя страница NNCP: http://www.nncpgo.org/ +Коротко об утилитах: http://www.nncpgo.org/Ob-utilitakh.html + +Исходный код и его подпись для этой версии находятся здесь: + + http://www.nncpgo.org/download/nncp-${release}.tar.xz ($size KiB) + http://www.nncpgo.org/download/nncp-${release}.tar.xz.sig + +SHA256 хэш: $hash +Идентификатор GPG ключа: 0x2B25868E75A1A953 NNCP releases +Отпечаток: 92C2 F0AE FE73 208E 46BF F3DE 2B25 868E 75A1 A953 + +Пожалуйста, все вопросы касающиеся использования NNCP, отчёты об ошибках +и патчи отправляйте в nncp-devel почтовую рассылку: +https://lists.cypherpunks.ru/pipermail/nncp-devel/ +EOF diff --git a/news_and_install.sh b/news_and_install.sh deleted file mode 100755 index 8129fc7..0000000 --- a/news_and_install.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -texi=`mktemp` -cat > $texi < $texi < diff --git a/ports/nncp/files/nncp.newsyslog.conf.in b/ports/nncp/files/nncp.newsyslog.conf.sample.in similarity index 100% rename from ports/nncp/files/nncp.newsyslog.conf.in rename to ports/nncp/files/nncp.newsyslog.conf.sample.in diff --git a/ports/nncp/files/pkg-deinstall.in b/ports/nncp/files/pkg-deinstall.in index af0ca73..0c9f4f1 100644 --- a/ports/nncp/files/pkg-deinstall.in +++ b/ports/nncp/files/pkg-deinstall.in @@ -1,13 +1,9 @@ #!/bin/sh -if [ "$2" != "POST-DEINSTALL" ]; then - exit 0 -fi +[ "$2" = "POST-DEINSTALL" ] || exit 0 if [ -e %%PREFIX%%/etc/nncp.yaml ]; then echo "%%PREFIX%%/etc/nncp.yaml with your private keys is not removed!" fi -if [ -e /var/spool/nncp ]; then - echo "/var/spool/nncp is not removed!" -fi +exec rmdir /var/spool/nncp diff --git a/ports/nncp/files/pkg-install.in b/ports/nncp/files/pkg-install.in new file mode 100644 index 0000000..ff6cd87 --- /dev/null +++ b/ports/nncp/files/pkg-install.in @@ -0,0 +1,5 @@ +#!/bin/sh + +[ "$2" = "POST-INSTALL" ] || exit 0 + +[ -e /var/spool/nncp ] || exec mkdir -p /var/spool/nncp diff --git a/src/cypherpunks.ru/balloon b/src/cypherpunks.ru/balloon new file mode 160000 index 0000000..2be0740 --- /dev/null +++ b/src/cypherpunks.ru/balloon @@ -0,0 +1 @@ +Subproject commit 2be074075c635f95406490655039988c8e3633d8 diff --git a/src/cypherpunks.ru/nncp/cfg.go b/src/cypherpunks.ru/nncp/cfg.go index f89c106..3400ed8 100644 --- a/src/cypherpunks.ru/nncp/cfg.go +++ b/src/cypherpunks.ru/nncp/cfg.go @@ -19,12 +19,15 @@ along with this program. If not, see . package nncp import ( + "bytes" "errors" + "log" "os" "path" "github.com/gorhill/cronexpr" "golang.org/x/crypto/ed25519" + "golang.org/x/crypto/ssh/terminal" "gopkg.in/yaml.v2" ) @@ -40,15 +43,17 @@ var ( ) type NodeYAML struct { - Id string - ExchPub string - SignPub string - NoisePub *string `noisepub,omitempty` - Sendmail []string `sendmail,omitempty` - Incoming *string `incoming,omitempty` - Freq *string `freq,omitempty` - Via []string `via,omitempty` - Calls []CallYAML `calls,omitempty` + Id string + ExchPub string + SignPub string + NoisePub *string `noisepub,omitempty` + Sendmail []string `sendmail,omitempty` + Incoming *string `incoming,omitempty` + Freq *string `freq,omitempty` + FreqChunked *uint64 `freqchunked,omitempty` + FreqMinSize *uint64 `freqminsize,omitempty` + Via []string `via,omitempty` + Calls []CallYAML `calls,omitempty` Addrs map[string]string `addrs,omitempty` @@ -144,6 +149,17 @@ func NewNode(name string, yml NodeYAML) (*Node, error) { } freq = &fr } + var freqChunked int64 + if yml.FreqChunked != nil { + if *yml.FreqChunked == 0 { + return nil, errors.New("freqchunked value must be greater than zero") + } + freqChunked = int64(*yml.FreqChunked) * 1024 + } + var freqMinSize int64 + if yml.FreqMinSize != nil { + freqMinSize = int64(*yml.FreqMinSize) * 1024 + } defOnlineDeadline := uint(DefaultDeadline) if yml.OnlineDeadline != nil { @@ -218,6 +234,8 @@ func NewNode(name string, yml NodeYAML) (*Node, error) { Sendmail: yml.Sendmail, Incoming: incoming, Freq: freq, + FreqChunked: freqChunked, + FreqMinSize: freqMinSize, Calls: calls, Addrs: yml.Addrs, OnlineDeadline: defOnlineDeadline, @@ -319,9 +337,21 @@ func (nodeOur *NodeOur) ToYAML() string { } func CfgParse(data []byte) (*Ctx, error) { + var err error + if bytes.Compare(data[:8], MagicNNCPBv1[:]) == 0 { + os.Stderr.WriteString("Passphrase:") + password, err := terminal.ReadPassword(0) + if err != nil { + log.Fatalln(err) + } + os.Stderr.WriteString("\n") + data, err = DeEBlob(data, password) + if err != nil { + return nil, err + } + } var cfgYAML CfgYAML - err := yaml.Unmarshal(data, &cfgYAML) - if err != nil { + if err = yaml.Unmarshal(data, &cfgYAML); err != nil { return nil, err } if _, exists := cfgYAML.Neigh["self"]; !exists { diff --git a/src/cypherpunks.ru/nncp/chunked.go b/src/cypherpunks.ru/nncp/chunked.go new file mode 100644 index 0000000..61564c4 --- /dev/null +++ b/src/cypherpunks.ru/nncp/chunked.go @@ -0,0 +1,33 @@ +/* +NNCP -- Node to Node copy, utilities for store-and-forward data exchange +Copyright (C) 2016-2017 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package nncp + +var ( + MagicNNCPMv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'M', 0, 0, 1} + + ChunkedSuffixMeta = ".nncp.meta" + ChunkedSuffixPart = ".nncp.chunk" +) + +type ChunkedMeta struct { + Magic [8]byte + FileSize uint64 + ChunkSize uint64 + Checksums [][32]byte +} diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go new file mode 100644 index 0000000..9376d33 --- /dev/null +++ b/src/cypherpunks.ru/nncp/cmd/nncp-cfgenc/main.go @@ -0,0 +1,126 @@ +/* +NNCP -- Node to Node copy, utilities for store-and-forward data exchange +Copyright (C) 2016-2017 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +// NNCP configuration file encrypter/decrypter. +package main + +import ( + "bytes" + "errors" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + + "cypherpunks.ru/nncp" + "github.com/davecgh/go-xdr/xdr2" + "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/ssh/terminal" +) + +func usage() { + fmt.Fprintf(os.Stderr, nncp.UsageHeader()) + fmt.Fprintln(os.Stderr, "nncp-cfgenc -- encrypt/decrypt configuration file\n") + fmt.Fprintf(os.Stderr, "Usage: %s [options] cfg.yaml > cfg.yaml.eblob\n", os.Args[0]) + fmt.Fprintf(os.Stderr, " %s [options] -d cfg.yaml.eblob > cfg.yaml\n", os.Args[0]) + fmt.Fprintf(os.Stderr, " %s [options] -dump cfg.yaml.eblob\n", os.Args[0]) + fmt.Fprintln(os.Stderr, "Options:") + flag.PrintDefaults() +} + +func main() { + var ( + decrypt = flag.Bool("d", false, "Decrypt the file") + dump = flag.Bool("dump", false, "Print human-readable eblob information") + sOpt = flag.Int("s", nncp.DefaultS, "Balloon space cost, in 32 bytes chunks") + tOpt = flag.Int("t", nncp.DefaultT, "Balloon time cost, number of rounds") + pOpt = flag.Int("p", nncp.DefaultP, "Balloon number of parallel jobs") + version = flag.Bool("version", false, "Print version information") + warranty = flag.Bool("warranty", false, "Print warranty information") + ) + flag.Usage = usage + flag.Parse() + if *warranty { + fmt.Println(nncp.Warranty) + return + } + if *version { + fmt.Println(nncp.VersionGet()) + return + } + + if flag.NArg() != 1 { + usage() + os.Exit(1) + } + + data, err := ioutil.ReadFile(flag.Arg(0)) + if err != nil { + log.Fatalln("Can not read data:", err) + } + if *dump { + var eblob nncp.EBlob + if _, err := xdr.Unmarshal(bytes.NewReader(data), &eblob); err != nil { + log.Fatalln(err) + } + if eblob.Magic != nncp.MagicNNCPBv1 { + log.Fatalln(errors.New("Unknown eblob type")) + } + fmt.Println("Strengthening function: Balloon with BLAKE2b-256") + fmt.Printf("Memory space cost: %d bytes\n", eblob.SCost*blake2b.Size256) + fmt.Printf("Number of rounds: %d\n", eblob.TCost) + fmt.Printf("Number of parallel jobs: %d\n", eblob.PCost) + fmt.Printf("Blob size: %d\n", len(eblob.Blob)) + os.Exit(0) + } + if *decrypt { + os.Stderr.WriteString("Passphrase:") + password, err := terminal.ReadPassword(0) + if err != nil { + log.Fatalln(err) + } + os.Stderr.WriteString("\n") + cfgRaw, err := nncp.DeEBlob(data, password) + if err != nil { + log.Fatalln(err) + } + os.Stdout.Write(cfgRaw) + } else { + os.Stderr.WriteString("Passphrase:") + password1, err := terminal.ReadPassword(0) + if err != nil { + log.Fatalln(err) + } + os.Stderr.WriteString("\n") + os.Stderr.WriteString("Repeat passphrase:") + password2, err := terminal.ReadPassword(0) + if err != nil { + log.Fatalln(err) + } + os.Stderr.WriteString("\n") + if bytes.Compare(password1, password2) != 0 { + log.Fatalln(errors.New("Passphrases do not match")) + } + eblob, err := nncp.NewEBlob(*sOpt, *tOpt, *pOpt, password1, data) + if err != nil { + log.Fatalln(err) + } + os.Stdout.Write(eblob) + } +} diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-mincfg/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go similarity index 97% rename from src/cypherpunks.ru/nncp/cmd/nncp-mincfg/main.go rename to src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go index 5b5fdf5..48df34e 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-mincfg/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go @@ -32,7 +32,7 @@ import ( func usage() { fmt.Fprintf(os.Stderr, nncp.UsageHeader()) - fmt.Fprintln(os.Stderr, "nncp-mincfg -- print stripped configuration\n") + fmt.Fprintln(os.Stderr, "nncp-cfgmin -- print stripped configuration\n") fmt.Fprintf(os.Stderr, "Usage: %s [options]\nOptions:\n", os.Args[0]) flag.PrintDefaults() } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-newcfg/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go similarity index 97% rename from src/cypherpunks.ru/nncp/cmd/nncp-newcfg/main.go rename to src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go index cd36aa9..2601d46 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-newcfg/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-cfgnew/main.go @@ -30,7 +30,7 @@ import ( func usage() { fmt.Fprintf(os.Stderr, nncp.UsageHeader()) - fmt.Fprintln(os.Stderr, "nncp-newcfg -- generate new configuration and keys\nOptions:") + fmt.Fprintln(os.Stderr, "nncp-cfgnew -- generate new configuration and keys\nOptions:") flag.PrintDefaults() } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go index 4fafc48..f3406fd 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go @@ -35,17 +35,21 @@ func usage() { fmt.Fprintln(os.Stderr, "nncp-file -- send file\n") fmt.Fprintf(os.Stderr, "Usage: %s [options] SRC NODE:[DST]\nOptions:\n", os.Args[0]) flag.PrintDefaults() + fmt.Fprint(os.Stderr, ` +If SRC equals to -, then read data from stdin to temporary file. +`) } func main() { var ( - cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") - niceRaw = flag.Int("nice", nncp.DefaultNiceFile, "Outbound packet niceness") - minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size") - 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.Int("nice", nncp.DefaultNiceFile, "Outbound packet niceness") + minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB") + chunkSize = flag.Uint64("chunked", 0, "Split file on specified size chunks, in KiB") + 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") ) flag.Usage = usage flag.Parse() @@ -90,7 +94,19 @@ func main() { log.Fatalln("Invalid NODE specified:", err) } - if err = ctx.TxFile(node, nice, flag.Arg(0), splitted[1], int64(*minSize)); err != nil { + if *chunkSize == 0 { + err = ctx.TxFile(node, nice, flag.Arg(0), splitted[1], int64(*minSize)) + } else { + err = ctx.TxFileChunked( + node, + nice, + flag.Arg(0), + splitted[1], + int64(*minSize)*1024, + int64(*chunkSize)*1024, + ) + } + if err != nil { log.Fatalln(err) } } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go index d570bdf..a5e96e6 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go @@ -41,7 +41,7 @@ func main() { var ( cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") niceRaw = flag.Int("nice", nncp.DefaultNiceFreq, "Outbound packet niceness") - minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size") + minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB") quiet = flag.Bool("quiet", false, "Print only errors") debug = flag.Bool("debug", false, "Print debug messages") version = flag.Bool("version", false, "Print version information") @@ -90,7 +90,7 @@ func main() { log.Fatalln("Invalid NODE specified:", err) } - if err = ctx.TxFreq(node, nice, splitted[1], flag.Arg(1), int64(*minSize)); err != nil { + if err = ctx.TxFreq(node, nice, splitted[1], flag.Arg(1), int64(*minSize)*1024); err != nil { log.Fatalln(err) } } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go index c09fbe2..d14c3d6 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go @@ -42,7 +42,7 @@ func main() { var ( cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") niceRaw = flag.Int("nice", nncp.DefaultNiceMail, "Outbound packet niceness") - minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size") + minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB") quiet = flag.Bool("quiet", false, "Print only errors") debug = flag.Bool("debug", false, "Print debug messages") version = flag.Bool("version", false, "Print version information") @@ -91,7 +91,7 @@ func main() { log.Fatalln("Can not read mail body from stdin:", err) } - if err = ctx.TxMail(node, nice, strings.Join(flag.Args()[1:], " "), body, int64(*minSize)); err != nil { + if err = ctx.TxMail(node, nice, strings.Join(flag.Args()[1:], " "), body, int64(*minSize)*1024); err != nil { log.Fatalln(err) } } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go new file mode 100644 index 0000000..45354d7 --- /dev/null +++ b/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go @@ -0,0 +1,355 @@ +/* +NNCP -- Node to Node copy, utilities for store-and-forward data exchange +Copyright (C) 2016-2017 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +// Send file via NNCP +package main + +import ( + "bufio" + "bytes" + "encoding/hex" + "flag" + "fmt" + "hash" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "strconv" + "strings" + + "cypherpunks.ru/nncp" + "github.com/davecgh/go-xdr/xdr2" + "github.com/dustin/go-humanize" + "golang.org/x/crypto/blake2b" +) + +func usage() { + fmt.Fprintf(os.Stderr, nncp.UsageHeader()) + fmt.Fprintln(os.Stderr, "nncp-reass -- reassemble chunked files\n") + fmt.Fprintf(os.Stderr, "Usage: %s [options] [FILE.nncp.meta]\nOptions:\n", os.Args[0]) + flag.PrintDefaults() + fmt.Fprint(os.Stderr, ` +Neither FILE, nor -node nor -all can be set simultaneously, +but at least one of them must be specified. +`) +} + +func process(ctx *nncp.Ctx, path string, keep, dryRun, stdout, dumpMeta bool) bool { + fd, err := os.Open(path) + defer fd.Close() + if err != nil { + log.Fatalln("Can not open file:", err) + } + var metaPkt nncp.ChunkedMeta + if _, err = xdr.Unmarshal(fd, &metaPkt); err != nil { + ctx.LogE("nncp-reass", nncp.SDS{"path": path, "err": err}, "bad meta file") + return false + } + fd.Close() + if metaPkt.Magic != nncp.MagicNNCPMv1 { + ctx.LogE("nncp-reass", nncp.SDS{"path": path, "err": nncp.BadMagic}, "") + return false + } + + metaName := filepath.Base(path) + if !strings.HasSuffix(metaName, nncp.ChunkedSuffixMeta) { + ctx.LogE("nncp-reass", nncp.SDS{ + "path": path, + "err": "invalid filename suffix", + }, "") + return false + } + mainName := strings.TrimSuffix(metaName, nncp.ChunkedSuffixMeta) + if dumpMeta { + fmt.Printf("Original filename: %s\n", mainName) + fmt.Printf( + "File size: %s (%d bytes)\n", + humanize.IBytes(metaPkt.FileSize), + metaPkt.FileSize, + ) + fmt.Printf( + "Chunk size: %s (%d bytes)\n", + humanize.IBytes(metaPkt.ChunkSize), + metaPkt.ChunkSize, + ) + fmt.Printf("Number of chunks: %d\n", len(metaPkt.Checksums)) + fmt.Println("Checksums:") + for chunkNum, checksum := range metaPkt.Checksums { + fmt.Printf("\t%d: %s\n", chunkNum, hex.EncodeToString(checksum[:])) + } + return true + } + mainDir := filepath.Dir(path) + + chunksPaths := make([]string, 0, len(metaPkt.Checksums)) + for i := 0; i < len(metaPkt.Checksums); i++ { + chunksPaths = append( + chunksPaths, + filepath.Join(mainDir, mainName+nncp.ChunkedSuffixPart+strconv.Itoa(i)), + ) + } + + allChunksExist := true + for chunkNum, chunkPath := range chunksPaths { + fi, err := os.Stat(chunkPath) + if err != nil && os.IsNotExist(err) { + ctx.LogI("nncp-reass", nncp.SDS{ + "path": path, + "chunk": strconv.Itoa(chunkNum), + }, "missing") + allChunksExist = false + continue + } + var badSize bool + if chunkNum+1 == len(chunksPaths) { + badSize = uint64(fi.Size()) != metaPkt.FileSize%metaPkt.ChunkSize + } else { + badSize = uint64(fi.Size()) != metaPkt.ChunkSize + } + if badSize { + ctx.LogE("nncp-reass", nncp.SDS{ + "path": path, + "chunk": strconv.Itoa(chunkNum), + }, "invalid size") + allChunksExist = false + } + } + if !allChunksExist { + return false + } + + var hsh hash.Hash + allChecksumsGood := true + for chunkNum, chunkPath := range chunksPaths { + fd, err = os.Open(chunkPath) + if err != nil { + log.Fatalln("Can not open file:", err) + } + hsh, err = blake2b.New256(nil) + if err != nil { + log.Fatalln(err) + } + if _, err = io.Copy(hsh, bufio.NewReader(fd)); err != nil { + log.Fatalln(err) + } + fd.Close() + if bytes.Compare(hsh.Sum(nil), metaPkt.Checksums[chunkNum][:]) != 0 { + ctx.LogE("nncp-reass", nncp.SDS{ + "path": path, + "chunk": strconv.Itoa(chunkNum), + }, "checksum is bad") + allChecksumsGood = false + } + } + if !allChecksumsGood { + return false + } + if dryRun { + ctx.LogI("nncp-reass", nncp.SDS{"path": path}, "ready") + return true + } + + var dst io.Writer + var tmp *os.File + var sds nncp.SDS + if stdout { + dst = os.Stdout + sds = nncp.SDS{"path": path} + } else { + tmp, err = ioutil.TempFile(mainDir, "nncp-reass") + if err != nil { + log.Fatalln(err) + } + sds = nncp.SDS{"path": path, "tmp": tmp.Name()} + ctx.LogD("nncp-reass", sds, "created") + dst = tmp + } + dstW := bufio.NewWriter(dst) + + hasErrors := false + for chunkNum, chunkPath := range chunksPaths { + fd, err = os.Open(chunkPath) + if err != nil { + log.Fatalln("Can not open file:", err) + } + if _, err = io.Copy(dstW, bufio.NewReader(fd)); err != nil { + log.Fatalln(err) + } + fd.Close() + if !keep { + if err = os.Remove(chunkPath); err != nil { + ctx.LogE("nncp-reass", nncp.SdsAdd(sds, nncp.SDS{ + "chunk": strconv.Itoa(chunkNum), + "err": err, + }), "") + hasErrors = true + } + } + } + dstW.Flush() + if tmp != nil { + tmp.Sync() + tmp.Close() + } + ctx.LogD("nncp-reass", sds, "written") + if !keep { + if err = os.Remove(path); err != nil { + ctx.LogE("nncp-reass", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "") + hasErrors = true + } + } + if stdout { + ctx.LogI("nncp-reass", nncp.SDS{"path": path}, "done") + return !hasErrors + } + + dstPathOrig := filepath.Join(mainDir, mainName) + dstPath := dstPathOrig + dstPathCtr := 0 + for { + if _, err = os.Stat(dstPath); err != nil { + if os.IsNotExist(err) { + break + } + log.Fatalln(err) + } + dstPath = dstPathOrig + strconv.Itoa(dstPathCtr) + dstPathCtr++ + } + if err = os.Rename(tmp.Name(), dstPath); err != nil { + log.Fatalln(err) + } + ctx.LogI("nncp-reass", nncp.SDS{"path": path}, "done") + return !hasErrors +} + +func findMetas(ctx *nncp.Ctx, dirPath string) []string { + dir, err := os.Open(dirPath) + defer dir.Close() + if err != nil { + ctx.LogE("nncp-reass", nncp.SDS{"path": dirPath, "err": err}, "") + return nil + } + fis, err := dir.Readdir(0) + dir.Close() + if err != nil { + ctx.LogE("nncp-reass", nncp.SDS{"path": dirPath, "err": err}, "") + return nil + } + metaPaths := make([]string, 0) + for _, fi := range fis { + if strings.HasSuffix(fi.Name(), nncp.ChunkedSuffixMeta) { + metaPaths = append(metaPaths, filepath.Join(dirPath, fi.Name())) + } + } + return metaPaths +} + +func main() { + var ( + cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") + allNodes = flag.Bool("all", false, "Process all found chunked files for all nodes") + nodeRaw = flag.String("node", "", "Process all found chunked files for that node") + keep = flag.Bool("keep", false, "Do not remove chunks while assembling") + dryRun = flag.Bool("dryrun", false, "Do not assemble whole file") + dumpMeta = flag.Bool("dump", false, "Print decoded human-readable FILE.nncp.meta") + stdout = flag.Bool("stdout", false, "Output reassembled FILE to stdout") + 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") + ) + flag.Usage = usage + flag.Parse() + if *warranty { + fmt.Println(nncp.Warranty) + return + } + if *version { + fmt.Println(nncp.VersionGet()) + return + } + + cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) + if err != nil { + log.Fatalln("Can not read config:", err) + } + ctx, err := nncp.CfgParse(cfgRaw) + if err != nil { + log.Fatalln("Can not parse config:", err) + } + ctx.Quiet = *quiet + ctx.Debug = *debug + + var nodeOnly *nncp.Node + if *nodeRaw != "" { + nodeOnly, err = ctx.FindNode(*nodeRaw) + if err != nil { + log.Fatalln("Invalid -node specified:", err) + } + } + + if !(*allNodes || nodeOnly != nil || flag.NArg() > 0) { + usage() + os.Exit(1) + } + if flag.NArg() > 0 && (*allNodes || nodeOnly != nil) { + usage() + os.Exit(1) + } + if *allNodes && nodeOnly != nil { + usage() + os.Exit(1) + } + + if flag.NArg() > 0 { + if !process(ctx, flag.Arg(0), *keep, *dryRun, *stdout, *dumpMeta) { + os.Exit(1) + } + return + } + + hasErrors := false + if nodeOnly == nil { + seenMetaPaths := make(map[string]struct{}) + for _, node := range ctx.Neigh { + if node.Incoming == nil { + continue + } + for _, metaPath := range findMetas(ctx, *node.Incoming) { + if _, seen := seenMetaPaths[metaPath]; seen { + continue + } + hasErrors = hasErrors || !process(ctx, metaPath, *keep, *dryRun, false, false) + seenMetaPaths[metaPath] = struct{}{} + } + } + } else { + if nodeOnly.Incoming == nil { + log.Fatalln("Specified -node does not allow incoming") + } + for _, metaPath := range findMetas(ctx, *nodeOnly.Incoming) { + hasErrors = hasErrors || !process(ctx, metaPath, *keep, *dryRun, false, false) + } + } + if hasErrors { + os.Exit(1) + } +} diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go index b11fc5d..bcd7625 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go @@ -48,7 +48,7 @@ func main() { niceRaw = flag.Int("nice", 255, "Minimal required niceness") rxOnly = flag.Bool("rx", false, "Only receive packets") txOnly = flag.Bool("tx", false, "Only transfer packets") - force = flag.Bool("force", false, "Force outbound directories creation") + mkdir = flag.Bool("mkdir", false, "Create necessary outbound directories") keep = flag.Bool("keep", false, "Do not delete transferred packets") quiet = flag.Bool("quiet", false, "Print only errors") debug = flag.Bool("debug", false, "Print debug messages") @@ -241,7 +241,7 @@ Tx: if err != nil { if os.IsNotExist(err) { ctx.LogD("nncp-xfer", sds, "does not exist") - if !*force { + if !*mkdir { ctx.UnlockDir(dirLock) continue } @@ -316,6 +316,7 @@ Tx: isBad = true continue } + os.Remove(filepath.Join(dstPath, pktName+".part")) delete(sds, "tmp") ctx.LogI("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{ "size": strconv.FormatInt(copied, 10), diff --git a/src/cypherpunks.ru/nncp/eblob.go b/src/cypherpunks.ru/nncp/eblob.go new file mode 100644 index 0000000..cb241ab --- /dev/null +++ b/src/cypherpunks.ru/nncp/eblob.go @@ -0,0 +1,153 @@ +/* +NNCP -- Node to Node copy, utilities for store-and-forward data exchange +Copyright (C) 2016-2017 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package nncp + +import ( + "bytes" + "crypto/cipher" + "crypto/rand" + "crypto/subtle" + "errors" + "io" + + "cypherpunks.ru/balloon" + "github.com/davecgh/go-xdr/xdr2" + "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/hkdf" + "golang.org/x/crypto/twofish" +) + +const ( + DefaultS = 1 << 20 / 32 + DefaultT = 1 << 4 + DefaultP = 2 +) + +var ( + MagicNNCPBv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'B', 0, 0, 1} +) + +type EBlob struct { + Magic [8]byte + SCost uint32 + TCost uint32 + PCost uint32 + Salt *[32]byte + Blob []byte + MAC *[blake2b.Size256]byte +} + +// Create an encrypted blob. sCost -- memory space requirements, number +// of hash-output sized (32 bytes) blocks. tCost -- time requirements, +// number of rounds. pCost -- number of parallel jobs. +func NewEBlob(sCost, tCost, pCost int, password, data []byte) ([]byte, error) { + salt := new([32]byte) + var err error + if _, err = rand.Read(salt[:]); err != nil { + return nil, err + } + key := balloon.H(blake256, password, salt[:], sCost, tCost, pCost) + kdf := hkdf.New(blake256, key, nil, MagicNNCPBv1[:]) + keyEnc := make([]byte, 32) + 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 + } + ciph, err := twofish.NewCipher(keyEnc) + if err != nil { + return nil, err + } + ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize)) + mac, err := blake2b.New256(keyAuth) + if err != nil { + return nil, err + } + var blob bytes.Buffer + mw := io.MultiWriter(&blob, mac) + ae := &cipher.StreamWriter{S: ctr, W: mw} + if _, err = ae.Write(data); err != nil { + return nil, err + } + macTag := new([blake2b.Size256]byte) + mac.Sum(macTag[:0]) + eblob := EBlob{ + Magic: MagicNNCPBv1, + SCost: uint32(sCost), + TCost: uint32(tCost), + PCost: uint32(pCost), + Salt: salt, + Blob: blob.Bytes(), + MAC: macTag, + } + var eblobRaw bytes.Buffer + if _, err = xdr.Marshal(&eblobRaw, &eblob); err != nil { + return nil, err + } + return eblobRaw.Bytes(), nil +} + +func DeEBlob(eblobRaw, password []byte) ([]byte, error) { + var eblob EBlob + var err error + if _, err = xdr.Unmarshal(bytes.NewReader(eblobRaw), &eblob); err != nil { + return nil, err + } + if eblob.Magic != MagicNNCPBv1 { + return nil, BadMagic + } + key := balloon.H( + blake256, + password, + eblob.Salt[:], + int(eblob.SCost), + int(eblob.TCost), + int(eblob.PCost), + ) + kdf := hkdf.New(blake256, key, nil, MagicNNCPBv1[:]) + keyEnc := make([]byte, 32) + 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 + } + ciph, err := twofish.NewCipher(keyEnc) + if err != nil { + return nil, err + } + ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize)) + mac, err := blake2b.New256(keyAuth) + if err != nil { + return nil, err + } + var blob bytes.Buffer + tr := io.TeeReader(bytes.NewReader(eblob.Blob), mac) + ae := &cipher.StreamReader{S: ctr, R: tr} + if _, err = io.Copy(&blob, ae); err != nil { + return nil, err + } + if subtle.ConstantTimeCompare(mac.Sum(nil), eblob.MAC[:]) != 1 { + return nil, errors.New("Unauthenticated blob") + } + return blob.Bytes(), nil +} diff --git a/src/cypherpunks.ru/nncp/humanizer.go b/src/cypherpunks.ru/nncp/humanizer.go index 06b33bd..49b715a 100644 --- a/src/cypherpunks.ru/nncp/humanizer.go +++ b/src/cypherpunks.ru/nncp/humanizer.go @@ -218,6 +218,25 @@ func (ctx *Ctx) Humanize(s string) string { default: return s } + case "nncp-reass": + chunkNum, exists := sds["chunk"] + if exists { + msg = fmt.Sprintf( + "Reassembling chunked file \"%s\" (chunk %s): %s", + sds["path"], + chunkNum, + rem, + ) + } else { + msg = fmt.Sprintf( + "Reassembling chunked file \"%s\": %s", + sds["path"], + rem, + ) + } + if err, exists := sds["err"]; exists { + msg += ": " + err + } default: return s } diff --git a/src/cypherpunks.ru/nncp/node.go b/src/cypherpunks.ru/nncp/node.go index 7992f4b..22fa3fb 100644 --- a/src/cypherpunks.ru/nncp/node.go +++ b/src/cypherpunks.ru/nncp/node.go @@ -45,6 +45,8 @@ type Node struct { Sendmail []string Incoming *string Freq *string + FreqChunked int64 + FreqMinSize int64 Via []*NodeId Addrs map[string]string OnlineDeadline uint diff --git a/src/cypherpunks.ru/nncp/toss.go b/src/cypherpunks.ru/nncp/toss.go index 8a48e91..4ae9711 100644 --- a/src/cypherpunks.ru/nncp/toss.go +++ b/src/cypherpunks.ru/nncp/toss.go @@ -264,7 +264,25 @@ func (ctx *Ctx) Toss(nodeId *NodeId, nice uint8, dryRun bool) bool { goto Closing } if !dryRun { - if err = ctx.TxFile(sender, job.PktEnc.Nice, filepath.Join(*freq, src), dst, 0); err != nil { + if sender.FreqChunked == 0 { + err = ctx.TxFile( + sender, + job.PktEnc.Nice, + filepath.Join(*freq, src), + dst, + sender.FreqMinSize, + ) + } else { + err = ctx.TxFileChunked( + sender, + job.PktEnc.Nice, + filepath.Join(*freq, src), + dst, + sender.FreqMinSize, + sender.FreqChunked, + ) + } + if err != nil { ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "tx file") isBad = true goto Closing diff --git a/src/cypherpunks.ru/nncp/tx.go b/src/cypherpunks.ru/nncp/tx.go index 65ad7ec..7faef49 100644 --- a/src/cypherpunks.ru/nncp/tx.go +++ b/src/cypherpunks.ru/nncp/tx.go @@ -22,14 +22,20 @@ import ( "bufio" "bytes" "compress/zlib" + "crypto/cipher" + "crypto/rand" "errors" + "hash" "io" + "io/ioutil" "os" "path/filepath" "strconv" "strings" + "github.com/davecgh/go-xdr/xdr2" "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/twofish" ) func (ctx *Ctx) Tx(node *Node, pkt *Pkt, nice uint8, size, minSize int64, src io.Reader) (*Node, error) { @@ -101,8 +107,57 @@ func (ctx *Ctx) Tx(node *Node, pkt *Pkt, nice uint8, size, minSize int64, src io return lastNode, err } +func prepareTxFile(srcPath string) (io.Reader, *os.File, int64, error) { + var reader io.Reader + var src *os.File + var fileSize int64 + var err error + if srcPath == "-" { + src, err = ioutil.TempFile("", "nncp-file") + if err != nil { + return nil, nil, 0, err + } + os.Remove(src.Name()) + tmpW := bufio.NewWriter(src) + + tmpKey := make([]byte, 32) + if _, err = rand.Read(tmpKey); err != nil { + return nil, nil, 0, err + } + ciph, err := twofish.NewCipher(tmpKey) + if err != nil { + return nil, nil, 0, err + } + ctr := cipher.NewCTR(ciph, make([]byte, twofish.BlockSize)) + encrypter := &cipher.StreamWriter{S: ctr, W: tmpW} + fileSize, err = io.Copy(encrypter, bufio.NewReader(os.Stdin)) + if err != nil { + return nil, nil, 0, err + } + tmpW.Flush() + src.Seek(0, 0) + ctr = cipher.NewCTR(ciph, make([]byte, twofish.BlockSize)) + reader = &cipher.StreamReader{S: ctr, R: bufio.NewReader(src)} + } else { + src, err = os.Open(srcPath) + if err != nil { + return nil, nil, 0, err + } + srcStat, err := src.Stat() + if err != nil { + return nil, nil, 0, err + } + fileSize = srcStat.Size() + reader = bufio.NewReader(src) + } + return reader, src, fileSize, nil +} + func (ctx *Ctx) TxFile(node *Node, nice uint8, srcPath, dstPath string, minSize int64) error { if dstPath == "" { + if srcPath == "-" { + return errors.New("Must provide destination filename") + } dstPath = filepath.Base(srcPath) } dstPath = filepath.Clean(dstPath) @@ -113,16 +168,14 @@ func (ctx *Ctx) TxFile(node *Node, nice uint8, srcPath, dstPath string, minSize if err != nil { return err } - src, err := os.Open(srcPath) - if err != nil { - return err + reader, src, fileSize, err := prepareTxFile(srcPath) + if src != nil { + defer src.Close() } - defer src.Close() - srcStat, err := src.Stat() if err != nil { return err } - _, err = ctx.Tx(node, pkt, nice, srcStat.Size(), minSize, bufio.NewReader(src)) + _, err = ctx.Tx(node, pkt, nice, fileSize, minSize, reader) if err == nil { ctx.LogI("tx", SDS{ "type": "file", @@ -130,7 +183,7 @@ func (ctx *Ctx) TxFile(node *Node, nice uint8, srcPath, dstPath string, minSize "nice": strconv.Itoa(int(nice)), "src": srcPath, "dst": dstPath, - "size": strconv.FormatInt(srcStat.Size(), 10), + "size": strconv.FormatInt(fileSize, 10), }, "sent") } else { ctx.LogE("tx", SDS{ @@ -139,7 +192,128 @@ func (ctx *Ctx) TxFile(node *Node, nice uint8, srcPath, dstPath string, minSize "nice": strconv.Itoa(int(nice)), "src": srcPath, "dst": dstPath, - "size": strconv.FormatInt(srcStat.Size(), 10), + "size": strconv.FormatInt(fileSize, 10), + "err": err, + }, "sent") + } + return err +} + +func (ctx *Ctx) TxFileChunked(node *Node, nice uint8, srcPath, dstPath string, minSize int64, chunkSize int64) error { + if dstPath == "" { + if srcPath == "-" { + return errors.New("Must provide destination filename") + } + dstPath = filepath.Base(srcPath) + } + dstPath = filepath.Clean(dstPath) + if filepath.IsAbs(dstPath) { + return errors.New("Relative destination path required") + } + reader, src, fileSize, err := prepareTxFile(srcPath) + if src != nil { + defer src.Close() + } + if err != nil { + return err + } + + leftSize := fileSize + metaPkt := ChunkedMeta{ + Magic: MagicNNCPMv1, + FileSize: uint64(fileSize), + ChunkSize: uint64(chunkSize), + Checksums: make([][32]byte, 0, (fileSize/chunkSize)+1), + } + for i := int64(0); i < (fileSize/chunkSize)+1; i++ { + hsh := new([32]byte) + metaPkt.Checksums = append(metaPkt.Checksums, *hsh) + } + var sizeToSend int64 + var hsh hash.Hash + var pkt *Pkt + var chunkNum int + var path string + for { + if leftSize <= chunkSize { + sizeToSend = leftSize + } else { + sizeToSend = chunkSize + } + path = dstPath + ChunkedSuffixPart + strconv.Itoa(chunkNum) + pkt, err = NewPkt(PktTypeFile, path) + if err != nil { + return err + } + hsh, err = blake2b.New256(nil) + if err != nil { + return err + } + _, err = ctx.Tx( + node, + pkt, + nice, + sizeToSend, + minSize, + io.TeeReader(reader, hsh), + ) + if err == nil { + ctx.LogI("tx", SDS{ + "type": "file", + "node": node.Id, + "nice": strconv.Itoa(int(nice)), + "src": srcPath, + "dst": path, + "size": strconv.FormatInt(sizeToSend, 10), + }, "sent") + } else { + ctx.LogE("tx", SDS{ + "type": "file", + "node": node.Id, + "nice": strconv.Itoa(int(nice)), + "src": srcPath, + "dst": path, + "size": strconv.FormatInt(sizeToSend, 10), + "err": err, + }, "sent") + return err + } + hsh.Sum(metaPkt.Checksums[chunkNum][:0]) + leftSize -= sizeToSend + chunkNum++ + if leftSize == 0 { + break + } + } + var metaBuf bytes.Buffer + _, err = xdr.Marshal(&metaBuf, metaPkt) + if err != nil { + return err + } + path = dstPath + ChunkedSuffixMeta + pkt, err = NewPkt(PktTypeFile, path) + if err != nil { + return err + } + metaPktSize := int64(metaBuf.Len()) + _, err = ctx.Tx(node, pkt, nice, metaPktSize, minSize, &metaBuf) + if err == nil { + ctx.LogI("tx", SDS{ + "type": "file", + "node": node.Id, + "nice": strconv.Itoa(int(nice)), + "src": srcPath, + "dst": path, + "size": strconv.FormatInt(metaPktSize, 10), + }, "sent") + } else { + ctx.LogE("tx", SDS{ + "type": "file", + "node": node.Id, + "nice": strconv.Itoa(int(nice)), + "src": srcPath, + "dst": path, + "size": strconv.FormatInt(metaPktSize, 10), "err": err, }, "sent") } diff --git a/src/github.com/dustin/go-humanize b/src/github.com/dustin/go-humanize index ef638b6..259d2a1 160000 --- a/src/github.com/dustin/go-humanize +++ b/src/github.com/dustin/go-humanize @@ -1 +1 @@ -Subproject commit ef638b6c2e62b857442c6443dace9366a48c0ee2 +Subproject commit 259d2a102b871d17f30e3cd9881a642961a1e486 diff --git a/src/golang.org/x/crypto b/src/golang.org/x/crypto index 3cb0727..c7af5bf 160000 --- a/src/golang.org/x/crypto +++ b/src/golang.org/x/crypto @@ -1 +1 @@ -Subproject commit 3cb07270c9455e8ad27956a70891c962d121a228 +Subproject commit c7af5bf2638a1164f2eb5467c39c6cffbd13a02e diff --git a/src/golang.org/x/net b/src/golang.org/x/net index ffcf1be..da118f7 160000 --- a/src/golang.org/x/net +++ b/src/golang.org/x/net @@ -1 +1 @@ -Subproject commit ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d +Subproject commit da118f7b8e5954f39d0d2130ab35d4bf0e3cb344 diff --git a/src/golang.org/x/sys b/src/golang.org/x/sys index 9a7256c..9ccfe84 160000 --- a/src/golang.org/x/sys +++ b/src/golang.org/x/sys @@ -1 +1 @@ -Subproject commit 9a7256cb28ed514b4e1e5f68959914c4c28a92e0 +Subproject commit 9ccfe848b9db8435a24c424abbc07a921adf1df5 diff --git a/supplementary_files.sh b/supplementary_files.sh new file mode 100755 index 0000000..5fce6c1 --- /dev/null +++ b/supplementary_files.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +texi=`mktemp` + +cat > $texi < $texi < $texi < $texi <