[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
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.
--- /dev/null
+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.
+++ /dev/null
-There are people deserving to be thanked for helping this project:
-
-* Shawn K. Quinn <skquinn at rushpost dot com> for his descriptive
- instructions about building NNCP under Ubuntu GNU/Linux distributions
- and bug reports
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 \
nncp-caller:
GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-caller
+nncp-cfgenc:
+ GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgenc
+
+nncp-cfgmin:
+ GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgmin
+
+nncp-cfgnew:
+ GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-cfgnew
+
nncp-check:
GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-check
nncp-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
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
--- /dev/null
+@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 серверами -- вот и причины.
--- /dev/null
+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.
@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
signpub: GTGXG...IE3OA
sendmail: [/usr/sbin/sendmail]
freq: /home/bob/pub
+ freqchunked: 1024
+ freqminsize: 2048
via: [alice]
@end verbatim
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
--- /dev/null
+@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
@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.
@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
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
@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
(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.
@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
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
@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
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.
@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.
--- /dev/null
+@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
(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
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
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
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
@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
@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
@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}
--- /dev/null
+@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.
@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}}!
* 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.
@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
@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
--- /dev/null
+@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
@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
@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
@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.
@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|}
@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|}
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.
--- /dev/null
+@node Русский
+@unnumbered Информация на русском
+
+@menu
+* Подробнее об утилитах NNCP: Об утилитах.
+* Сравнение с существующими решениями: Сравнение.
+* Сценарии использования::
+* Новости::
+@end menu
+
+@include about.ru.texi
+@include comparison.ru.texi
+@include usecases.ru.texi
+@include news.ru.texi
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
% git checkout develop
% git submodule update --init
@end verbatim
-
-Github.com mirror exists: @url{https://github.com/stargrave/nncp}.
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
+-----------------+
@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|}
+------+--------------------+
@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
+------+---------------+
@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
+------+-------------------------+
@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
+------+------+
@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
@end table
-Typical peers behaviour is following:
+Typical peer's behaviour is following:
@enumerate
@item Perform Noise-IK handshake.
@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
--- /dev/null
+@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} команда может помочь с созданием
+конфигурационного файла без приватных ключей для этой цели).
* 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
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
% 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
[...]
@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
% 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
@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.
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
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
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.
@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
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).
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
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
@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
cd $cur
cat <<EOF
-Subject: NNCP $release release announcement
+Subject: [EN] NNCP $release release announcement
I am pleased to announce NNCP $release release availability!
Please send questions regarding the use of NNCP, bug reports and patches
to mailing list: https://lists.cypherpunks.ru/pipermail/nncp-devel/
EOF
+
+cat <<EOF
+Subject: [RU] Состоялся релиз NNCP $release
+
+Я рад сообщить о выходе релиза NNCP $release!
+
+NNCP (Node to Node copy) это набор утилит упрощающий безопасный обмен
+файлами и почтой в режиме сохранить-и-переслать.
+
+Эти утилиты предназначены помочь с построением одноранговых сетей
+небольшого размера (дюжины узлов), в режиме друг-к-другу (F2F) со
+статической маршрутизацией для безопасной надёжной передачи файлов,
+запросов на передачу файлов и Интернет почты по принципу
+выстрелил-и-забыл. Все пакеты проверяются на целостность, шифруются по
+принципу точка-точка (E2EE), аутентифицируются известными публичными
+ключами участников. Луковичное (onion) шифрование применяется ко всем
+ретранслируемым пакетам. Каждый узел выступает одновременно в роли
+клиента и сервера, может использовать как push, так и poll модель
+поведения.
+
+Поддержка из коробки offline флоппинета, тайников для сброса информации
+(dead drop) и компьютеров с "воздушным зазором" (air-gap). Но также
+существует и online TCP демон с полнодуплексной возобновляемой передачей
+данных.
+
+------------------------ >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 <releases@nncpgo.org>
+Отпечаток: 92C2 F0AE FE73 208E 46BF F3DE 2B25 868E 75A1 A953
+
+Пожалуйста, все вопросы касающиеся использования NNCP, отчёты об ошибках
+и патчи отправляйте в nncp-devel почтовую рассылку:
+https://lists.cypherpunks.ru/pipermail/nncp-devel/
+EOF
+++ /dev/null
-#!/bin/sh
-
-texi=`mktemp`
-cat > $texi <<EOF
-\input texinfo
-@documentencoding UTF-8
-@settitle NEWS
-
-`cat doc/news.texi`
-
-@bye
-EOF
-makeinfo --plaintext -o NEWS $texi
-rm -f $texi
-
-texi=$(TMPDIR=doc mktemp)
-cat > $texi <<EOF
-\input texinfo
-@documentencoding UTF-8
-@settitle INSTALL
-
-@include install.texi
-
-@bye
-EOF
-makeinfo --plaintext -o INSTALL $texi
-rm -f $texi
# $FreeBSD$
PORTNAME= nncp
-PORTVERSION= 0.6
+PORTVERSION= 0.7
CATEGORIES= net
MASTER_SITES= http://www.nncpgo.org/download/ \
http://sourceforge.net/projects/nncp/files/
USE_RC_SUBR= nncp-caller nncp-daemon nncp-toss
REQUIRE= DAEMON
-SUB_FILES= pkg-message pkg-deinstall nncp.newsyslog.conf
+SUB_FILES= pkg-message pkg-install pkg-deinstall nncp.newsyslog.conf.sample
-PORTDOCS= AUTHORS NEWS README THANKS
+PORTDOCS= AUTHORS NEWS NEWS.RU README README.RU THANKS
INFO= nncp
INSTALL_TARGET= install-strip
PLIST_FILES= bin/nncp-call \
bin/nncp-caller \
+ bin/nncp-cfgenc \
+ bin/nncp-cfgmin \
+ bin/nncp-cfgnew \
bin/nncp-check \
bin/nncp-daemon \
bin/nncp-file \
bin/nncp-freq \
bin/nncp-log \
bin/nncp-mail \
- bin/nncp-mincfg \
- bin/nncp-newcfg \
bin/nncp-pkt \
+ bin/nncp-reass \
bin/nncp-rm \
bin/nncp-stat \
bin/nncp-toss \
bin/nncp-xfer \
- etc/newsyslog.conf.d/nncp.conf
-PLIST_DIRS= /var/spool/nncp
+ "@sample etc/newsyslog.conf.d/nncp.conf.sample"
post-install:
${MKDIR} ${STAGEDIR}${PREFIX}/etc/newsyslog.conf.d
- ${INSTALL_DATA} ${WRKDIR}/nncp.newsyslog.conf ${STAGEDIR}${PREFIX}/etc/newsyslog.conf.d/nncp.conf
- ${MKDIR} ${STAGEDIR}/var/spool/nncp
+ ${INSTALL_DATA} ${WRKDIR}/nncp.newsyslog.conf.sample ${STAGEDIR}${PREFIX}/etc/newsyslog.conf.d/nncp.conf.sample
.include <bsd.port.mk>
#!/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
--- /dev/null
+#!/bin/sh
+
+[ "$2" = "POST-INSTALL" ] || exit 0
+
+[ -e /var/spool/nncp ] || exec mkdir -p /var/spool/nncp
--- /dev/null
+Subproject commit 2be074075c635f95406490655039988c8e3633d8
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"
)
)
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`
}
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 {
Sendmail: yml.Sendmail,
Incoming: incoming,
Freq: freq,
+ FreqChunked: freqChunked,
+ FreqMinSize: freqMinSize,
Calls: calls,
Addrs: yml.Addrs,
OnlineDeadline: defOnlineDeadline,
}
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 {
--- /dev/null
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, 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 <http://www.gnu.org/licenses/>.
+*/
+
+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
+}
--- /dev/null
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, 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 <http://www.gnu.org/licenses/>.
+*/
+
+// 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)
+ }
+}
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()
}
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()
}
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()
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)
}
}
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")
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)
}
}
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")
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)
}
}
--- /dev/null
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, 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 <http://www.gnu.org/licenses/>.
+*/
+
+// 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)
+ }
+}
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")
if err != nil {
if os.IsNotExist(err) {
ctx.LogD("nncp-xfer", sds, "does not exist")
- if !*force {
+ if !*mkdir {
ctx.UnlockDir(dirLock)
continue
}
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),
--- /dev/null
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, 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 <http://www.gnu.org/licenses/>.
+*/
+
+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
+}
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
}
Sendmail []string
Incoming *string
Freq *string
+ FreqChunked int64
+ FreqMinSize int64
Via []*NodeId
Addrs map[string]string
OnlineDeadline uint
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
"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) {
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)
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",
"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{
"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")
}
-Subproject commit ef638b6c2e62b857442c6443dace9366a48c0ee2
+Subproject commit 259d2a102b871d17f30e3cd9881a642961a1e486
-Subproject commit 3cb07270c9455e8ad27956a70891c962d121a228
+Subproject commit c7af5bf2638a1164f2eb5467c39c6cffbd13a02e
-Subproject commit ffcf1bedda3b04ebb15a168a59800a73d6dc0f4d
+Subproject commit da118f7b8e5954f39d0d2130ab35d4bf0e3cb344
-Subproject commit 9a7256cb28ed514b4e1e5f68959914c4c28a92e0
+Subproject commit 9ccfe848b9db8435a24c424abbc07a921adf1df5
--- /dev/null
+#!/bin/sh
+
+texi=`mktemp`
+
+cat > $texi <<EOF
+\input texinfo
+@documentencoding UTF-8
+@settitle NEWS
+
+@node News
+@unnumbered News
+
+`sed -n '5,$p' < doc/news.texi`
+
+@bye
+EOF
+makeinfo --plaintext -o NEWS $texi
+
+cat > $texi <<EOF
+\input texinfo
+@documentencoding UTF-8
+@settitle NEWS.RU
+
+@node Новости
+@unnumbered Новости
+
+`sed -n '3,$p' < doc/news.ru.texi | sed 's/^@subsection/@section/'`
+
+@bye
+EOF
+makeinfo --plaintext -o NEWS.RU $texi
+
+rm -f $texi
+
+texi=$(TMPDIR=doc mktemp)
+cat > $texi <<EOF
+\input texinfo
+@documentencoding UTF-8
+@settitle INSTALL
+
+@include install.texi
+
+@bye
+EOF
+makeinfo --plaintext -o INSTALL $texi
+rm -f $texi
+
+texi=`mktemp`
+
+cat > $texi <<EOF
+\input texinfo
+@documentencoding UTF-8
+@settitle THANKS
+
+`cat doc/thanks.texi`
+
+@bye
+EOF
+makeinfo --plaintext -o THANKS $texi
+rm -f $texi