]> Cypherpunks.ru repositories - nncp.git/commitdiff
Merge branch 'develop' 1.0
authorSergey Matveev <stargrave@stargrave.org>
Sat, 2 Dec 2017 16:00:05 +0000 (19:00 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sat, 2 Dec 2017 16:00:05 +0000 (19:00 +0300)
50 files changed:
README
README.RU
VERSION
common.mk
doc/about.ru.texi
doc/about.texi
doc/bundles.texi [new file with mode: 0644]
doc/cmds.texi
doc/comparison.ru.texi
doc/download.texi
doc/index.texi
doc/news.ru.texi
doc/news.texi
doc/pkt.texi
doc/spool.texi
doc/usecases.ru.texi
doc/usecases.texi
makedist.sh
ports/nncp/Makefile
ports/nncp/pkg-descr
src/cypherpunks.ru/nncp/cfg.go
src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go [new file with mode: 0644]
src/cypherpunks.ru/nncp/cmd/nncp-call/main.go
src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go
src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go
src/cypherpunks.ru/nncp/cmd/nncp-check/main.go
src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go
src/cypherpunks.ru/nncp/cmd/nncp-file/main.go
src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go
src/cypherpunks.ru/nncp/cmd/nncp-log/main.go
src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go
src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go
src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go
src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go
src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go
src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go
src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go
src/cypherpunks.ru/nncp/ctx.go
src/cypherpunks.ru/nncp/humanizer.go
src/cypherpunks.ru/nncp/jobs.go
src/cypherpunks.ru/nncp/pkt.go
src/cypherpunks.ru/nncp/sp.go
src/cypherpunks.ru/nncp/tmp.go
src/cypherpunks.ru/nncp/toss.go
src/cypherpunks.ru/nncp/toss_test.go
src/github.com/dustin/go-humanize
src/golang.org/x/crypto
src/golang.org/x/net
src/golang.org/x/sys
src/gopkg.in/yaml.v2

diff --git a/README b/README
index 3445eee7f688692518d1b47547c89db5af6490c2..dab1ca8fb45b7576e91d88b58c9e649dcc020231 100644 (file)
--- a/README
+++ b/README
@@ -9,9 +9,9 @@ encrypted (E2EE), explicitly authenticated by known participants public
 keys. 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 sneakernet/floppynet, dead drops and air-gapped
-computers support. But online TCP daemon with full-duplex resumable data
-transmission exists.
+Out-of-box offline sneakernet/floppynet, dead drops, sequential and
+append-only CD-ROM/tape storages, air-gapped computers support. But
+online TCP daemon with full-duplex resumable data transmission exists.
 
 NNCP is copylefted free software: see the file COPYING for copying
 conditions. It should work on all POSIX-compatible systems. Easy
index ad26251fd3dae90da61ac35cf494b634bbe9516b..eccba53fffa5a08560a3c5d81f4b70a79f3d8309 100644 (file)
--- a/README.RU
+++ b/README.RU
@@ -13,7 +13,8 @@ NNCP (Node to Node copy) это набор утилит упрощающий б
 поведения.
 
 Поддержка из коробки offline флоппинета, тайников для сброса информации
-(dead drop) и компьютеров с "воздушным зазором" (air-gap). Но также
+(dead drop), последовательных и не перезаписываемых CD-ROM/ленточных
+хранилищ, компьютеров с "воздушным зазором" (air-gap). Но также
 существует и online TCP демон с полнодуплексной возобновляемой передачей
 данных.
 
diff --git a/VERSION b/VERSION
index c43e1055fd3f2e00540c298404934e8097459098..d3827e75a5cadb9fe4a27e1cb9b6d192e7323120 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.12
+1.0
index 327f8dfff34ca607685ca0e3db9e22fe4a2765bc..34d6404a03ac15ac6e14eb6137639e9a148f9403 100644 (file)
--- a/common.mk
+++ b/common.mk
@@ -17,6 +17,7 @@ LDFLAGS = \
        -X cypherpunks.ru/nncp.DefaultLogPath=$(LOGPATH)
 
 ALL = \
+       nncp-bundle \
        nncp-call \
        nncp-caller \
        nncp-cfgenc \
@@ -37,6 +38,9 @@ ALL = \
 
 all: $(ALL)
 
+nncp-bundle:
+       GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-bundle
+
 nncp-call:
        GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-call
 
index c285e164f379610a92315b9a97e9bfe94bc2d680..727402c4a3512a6847107263550f1dd21965f4e6 100644 (file)
@@ -22,7 +22,10 @@ NNCP (Node to Node copy) это набор утилит упрощающий б
 @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) и компьютеров с
+тайников} для сброса информации (dead drop), последовательных и не
+перезаписываемых @url{https://ru.wikipedia.org/wiki/CD-ROM,
+CD-ROM}/@url{https://ru.wikipedia.org/wiki/%D0%A1%D1%82%D1%80%D0%B8%D0%BC%D0%B5%D1%80,
+ленточных} хранилищ, компьютеров с
 @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 демон с
 полнодуплексной возобновляемой передачей данных.
index cfd53212341360376db6bcdaa9fd1598e209356d..ffe4be082a5411e57eac6e0598d9670de59782e3 100644 (file)
@@ -17,9 +17,12 @@ 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.
+dead drops}, sequential and append-only
+@url{https://en.wikipedia.org/wiki/CD-ROM,
+CD-ROM}/@url{https://en.wikipedia.org/wiki/Tape_drive, tape} storages,
+@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}!
 
diff --git a/doc/bundles.texi b/doc/bundles.texi
new file mode 100644 (file)
index 0000000..7541bfc
--- /dev/null
@@ -0,0 +1,55 @@
+@node Bundles
+@unnumbered Bundles
+
+Usual @ref{nncp-xfer} command requires filesystem it can operate on.
+That presumes random access media storage usage, like hard drives, USB
+flash drives and similar. But media like CD-ROM and especially tape
+drives are sequential by nature. You can prepare intermediate directory
+for recording to CD-ROM disc/tape, but that requires additional storage
+and is inconvenient.
+
+Bundles, created with @ref{nncp-bundle} command are convenient
+alternative to ordinary @command{nncp-xfer}. Bundle is just a collection
+of @ref{Encrypted, encrypted packets}, stream of packets. It could be
+sequentially streamed for recording and digested back.
+
+@itemize
+
+@item They do not require intermediate storage before recording on
+either CD-ROM or tape drive.
+@verbatim
+% nncp-bundle -tx SOMENODE | cdrecord -tao -         # record directly to CD
+% nncp-bundle -tx SOMENODE | dd of=/dev/sa0 bs=10240 # record directly to tape
+
+% dd if=/dev/cd0 bs=2048 | nncp-bundle -rx  # read directly from CD
+% dd if=/dev/sa0 bs=10240 | nncp-bundle -rx # read directly from tape
+@end verbatim
+
+@item They do not require filesystem existence to deal with, simplifying
+administration when operating in heterogeneous systems with varying
+filesystems. No @command{mount}/@command{umount}, @command{zpool
+import}/@command{zpool export} and struggling with file permissions.
+@verbatim
+% nncp-bundle -tx SOMENODE | dd of=/dev/da0 bs=1M # record directly to
+                                                  # hard/flash drive
+% dd if=/dev/da0 bs=1M | nncp-bundle -rx # read directly from drive
+@end verbatim
+
+@item This is the fastest way to record outbound packets for offline
+transmission -- sequential write is always faster, when no
+metainformation needs to be updated.
+
+@item This is convenient to use with append-only storages, just
+sending/appending new bundles.
+
+@item Bundles could be repeatedly broadcasted in one-way transmission.
+@ref{Sync, Sync protocol} requires interactive connection, but bundles
+can contain mix of various recipients.
+
+@end itemize
+
+Technically bundle is valid POSIX.1-2001 (pax)
+@url{http://www.freebsd.org/cgi/man.cgi?query=tar&sektion=5, tar archive},
+with directory/files hierarchy identical to that is used in
+@ref{nncp-xfer}: @file{NNCP/RECIPIENT/SENDER/PACKET}. So bundle can also
+be created by manual tar-ing of @command{nncp-xfer} resulting directory.
index 38f77eefd0830fe7be3eff7228106304ae5124fa..1de83a4e8dc361f1ac0fade948fa853a905dddda 100644 (file)
@@ -20,6 +20,12 @@ Nearly all commands have the following common options:
     1-255 values are allowed.
 @item -node
     Process only single specified node.
+@item -spool
+    Override path to spool directory. May be specified by
+    @env{NNCPSPOOL} environment variable.
+@item -log
+    Override path to logfile. May be specified by @env{NNCPLOG}
+    environment variable.
 @item -quiet
     Print only errors, omit simple informational messages. In any case
     those messages are logged, so you can reread them using
@@ -30,6 +36,53 @@ Nearly all commands have the following common options:
     Print warranty information (no warranty).
 @end table
 
+@node nncp-bundle
+@section nncp-bundle
+
+@verbatim
+% nncp-bundle [options] -tx [-delete] NODE [NODE ...] > ...
+% nncp-bundle [options] -rx -delete [-dryrun] [NODE ...] < ...
+% nncp-bundle [options] -rx [-check] [-dryrun] [NODE ...] < ...
+@end verbatim
+
+With @option{-tx} option, this command creates @ref{Bundles, bundle} of
+@ref{Encrypted, encrypted packets} from the spool directory and writes
+it to stdout.
+
+With @option{-rx} option, this command takes bundle from stdin and
+copies all found packets for our node to the spool directory. Pay
+attention that @strong{no} integrity checking is done by default. Modern
+tape drives could easily provide too much throughput your CPU won't be
+able to verify on the fly. So if you won't @ref{nncp-toss, toss}
+received packets at the place, it is advisable either to run
+@ref{nncp-check} utility for packets integrity verification, or to use
+@option{-check} option to enable on the fly integrity check.
+
+You can specify multiple @option{NODE} arguments, telling for what nodes
+you want to create the stream, or take it from. If no nodes are
+specified for @option{-rx} mode, then all packets aimed at us will be
+processed.
+
+When packets are sent through the stream, they are still kept in the
+spool directory, because there is no assurance that they are transferred
+to the media (media (CD-ROM, tape drive, raw hard drive) can end). If
+you want to forcefully delete them (after they are successfully flushed
+to stdout) anyway, use @option{-delete} option.
+
+But you can verify produced stream after, by digesting it by yourself
+with @option{-rx} and @option{-delete} options -- in that mode, stream
+packets integrity will be checked and they will be deleted from the
+spool if everything is good. So it is advisable to recheck your streams:
+
+@verbatim
+% nncp-bundle -tx ALICE BOB WHATEVER | cdrecord -tao -
+% dd if=/dev/cd0 bs=2048 | nncp-bundle -rx -delete
+@end verbatim
+
+@option{-dryrun} option prevents any writing to the spool. This is
+useful when you need to see what packets will pass by and possibly check
+their integrity.
+
 @node nncp-call
 @section nncp-call
 
@@ -352,12 +405,31 @@ Checksums:
 @section nncp-rm
 
 @verbatim
-% nncp-rm [options] NODE PKT
+% nncp-rm [options] -tmp
+% nncp-rm [options] -lock
+% nncp-rm [options] -node NODE -part
+% nncp-rm [options] -node NODE -seen
+% nncp-rm [options] -node NODE [-rx] [-tx]
+% nncp-rm [options] -node NODE -pkt 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.
+This command is aimed to delete various files from your spool directory:
+
+@itemize
+@item If @option{-tmp} option is specified, then it will delete all
+temporary files in @file{spool/tmp} directory. Files may stay in it when
+commands like @ref{nncp-file} fail for some reason.
+@item If @option{-lock} option is specified, then all @file{.lock} files
+will be deleted in your spool directory.
+@item If @option{-pkt} option is specified, then @file{PKT} packet (its
+Base32 name) will be deleted. This is useful when you see some packet
+failing to be processed.
+@item When either @option{-rx} or @option{-tx} options are specified
+(maybe both of them), then delete all packets from that given queues. If
+@option{-part} is given, then delete only @file{.part}ly downloaded
+ones. If @option{-seen} option is specified, then delete only
+@file{.seen} files.
+@end itemize
 
 @node nncp-stat
 @section nncp-stat
@@ -375,7 +447,7 @@ queues.
 @section nncp-toss
 
 @verbatim
-% nncp-toss [options] [-dryrun] [-cycle INT]
+% nncp-toss [options] [-dryrun] [-cycle INT] [-seen]
 @end verbatim
 
 Perform "tossing" operation on all inbound packets. This is the tool
@@ -390,6 +462,11 @@ tells what it will do.
 @option{INT} seconds in an infinite loop. That can be useful when
 running this command as a daemon.
 
+@option{-seen} option creates empty @file{XXX.seen} file after
+successful tossing of @file{XXX} packet. @ref{nncp-xfer} and
+@ref{nncp-bundle} commands skip inbound packets that has been already
+seen, processed and tossed. This is helpful to defeat duplicates.
+
 @node nncp-xfer
 @section nncp-xfer
 
@@ -418,3 +495,6 @@ 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.
+
+Also look for @ref{nncp-bundle}, especially if you deal with CD-ROM and
+tape drives.
index a4d101b354d08f85e0c539d5af7ac2e4346fc3d1..73580ae68279dfd5d79439e2b0de66bc6967e347 100644 (file)
@@ -42,7 +42,7 @@
     дополнительный уровень шифрования и аутентификации для безопасного
     обмена данными.
 
-    FTN сложен в настройке, потому-что это совершенно другой мир
+    FTN сложен в настройке, потому что это совершенно другой мир
     программного обеспечения, по-сравнению с Unix-ом. Даже редактор
     почты будет какой-нибудь GoldEd, а не обычный почтовый клиент. Более
     того, из коробки не предоставляется никакого шифрования и сильной
@@ -53,7 +53,7 @@
 
 @item Передача новостей
     SMTP ничего не знает о новостях, NNTP и тому подобному. NNCP тоже не
-    знает, потому-что на текущий день они уже мало используются.
+    знает, потому что на текущий день они уже мало используются.
 
 @item Передача файлов
     SMTP может передавать файлы только в Base64 кодировке -- это очень
index e325722b1d6536d23ad39aef40f0f06f58e0b7ca..7edc0608c3e53a4acfadb8b8a973b2a2c0e1d35d 100644 (file)
@@ -23,6 +23,10 @@ Tarballs include all necessary required libraries:
 @multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
 @headitem Version @tab Size @tab Tarball @tab SHA256 checksum
 
+@item @ref{Release 0.12, 0.12} @tab 978 KiB
+@tab @url{download/nncp-0.12.tar.xz, link} @url{download/nncp-0.12.tar.xz.sig, sign}
+@tab @code{707B4005 97753B29 73A5F3E5 DAB51B92 21CC296D 690EF4BC ADE93E0D 2595A5F2}
+
 @item @ref{Release 0.11, 0.11} @tab 1031 KiB
 @tab @url{download/nncp-0.11.tar.xz, link} @url{download/nncp-0.11.tar.xz.sig, sign}
 @tab @code{D0F73C3B ADBF6B8B 13641A61 4D34F65F 20AF4C84 90894331 BF1F1609 2D65E719}
index 4ce8613b4895236cff269f3db8cd24c35ae52f16..a01d6545287abb47636bf7e838d870ada6f6e130 100644 (file)
@@ -37,6 +37,7 @@ A copy of the license is included in the section entitled "Copying conditions".
 * Commands::
 * Niceness::
 * Chunked files: Chunked.
+* Bundles::
 * Spool directory: Spool.
 * Log format: Log.
 * Packet format: Packet.
@@ -59,6 +60,7 @@ A copy of the license is included in the section entitled "Copying conditions".
 @include cmds.texi
 @include niceness.texi
 @include chunked.texi
+@include bundles.texi
 @include spool.texi
 @include log.texi
 @include pkt.texi
index 98804fb87c014fde77431e6174b5445d45d7910d..ce983f37e2ee634d1d15e6cac71559b60c82cfa7 100644 (file)
@@ -1,6 +1,32 @@
 @node Новости
 @section Новости
 
+@node Релиз 1.0
+@subsection Релиз 1.0
+@itemize
+@item
+@strong{Несовместимое} изменение формата зашифрованных пакетов. Работа
+со старыми версиями не поддерживается.
+@item
+@command{nncp-bundle} команда может создавать потоки зашифрованных
+пакетов или потреблять их. Это полезно когда речь идёт о stdin/stdout
+методах передачи (например запись на CD-ROM без создания промежуточного
+подготовленного ISO образа или работа с ленточными накопителями).
+@item
+@command{nncp-toss} команда может создавать @file{.seen} файлы,
+предотвращая приём дублированных пакетов.
+@item
+В команде @command{nncp-call} разрешается иметь только одного
+обработчика контрольной суммы в фоне. Это полезно когда тысячи маленьких
+входящих пакетов могут создать много горутин.
+@item
+Возможность переопределить путь до spool директории и файла журнала
+через аргумент командной строки или переменную окружения.
+@item
+@command{nncp-rm} команда может удалять все исходящие/входящие,
+@file{.seen}, @file{.part}, @file{.lock} и временные файлы.
+@end itemize
+
 @node Релиз 0.12
 @subsection Релиз 0.12
 @itemize
index 8f36b4b590be2b2faa0a1a51b8911be68d189307..189311da4cee414f8984e567e70fc900d7464bcf 100644 (file)
@@ -3,6 +3,32 @@
 
 See also this page @ref{Новости, on russian}.
 
+@node Release 1.0
+@section Release 1.0
+@itemize
+@item
+@strong{Incompatible} encrypted packet format changes. Older versions
+are not supported.
+@item
+@command{nncp-bundle} command can either create stream of encrypted
+packets, or digest it. It is useful when dealing with stdin/stdout based
+transmission methods (like writing to CD-ROM without intermediate
+prepared ISO image and working with tape drives).
+@item
+@command{nncp-toss} is able to create @file{.seen} files preventing
+duplicate packets receiving.
+@item
+Single background checksum verifier worker is allowed in
+@command{nncp-call}. This is helpful when thousands of small inbound
+packets could create many goroutines.
+@item
+Ability to override path to spool directory and logfile through either
+command line argument, or environment variable.
+@item
+@command{nncp-rm} is able to delete outbound/inbound, @file{.seen},
+@file{.part}, @file{.lock} and temporary files.
+@end itemize
+
 @node Release 0.12
 @section Release 0.12
 @itemize
index 4549505961533aed1290bffe37d79f85824abb60..c0103994ee8a24180a19d8ed4527af3d666cb55a 100644 (file)
@@ -70,13 +70,13 @@ Each encrypted packet has the following header:
 @verbatim
   +------------ HEADER -------------+   +-------- ENCRYPTED --------+
  /                                   \ /                             \
-+-------------------------------------+------------+----...-----------+------+
-| MAGIC | NICE | SENDER | EPUB | SIGN | SIZE | MAC | CIPHERTEXT | MAC | JUNK |
-+------------------------------/------\------------+----...-----------+------+
-                              /        \
-               +-------------------------------------+
-               | MAGIC | NICE | RCPT | SENDER | EPUB |
-               +-------------------------------------+
++--------------------------------------------+------------+----...-----------+------+
+| MAGIC | NICE | SENDER | RCPT | EPUB | SIGN | SIZE | MAC | CIPHERTEXT | MAC | JUNK |
++-------------------------------------/------\------------+----...-----------+------+
+                                     /        \
+                      +-------------------------------------+
+                      | MAGIC | NICE | SENDER | RCPT | EPUB |
+                      +-------------------------------------+
 @end verbatim
 
 @multitable @columnfractions 0.2 0.3 0.5
@@ -90,6 +90,9 @@ Each encrypted packet has the following header:
 @item Sender @tab
     32-byte, fixed length opaque data @tab
     Sender node's id
+@item Recipient @tab
+    32-byte, fixed length opaque data @tab
+    Recipient node's id
 @item Exchange public key @tab
     32-byte, fixed length opaque data @tab
     Ephemeral curve25519 public key
@@ -98,15 +101,7 @@ Each encrypted packet has the following header:
     ed25519 signature for that packet's header
 @end multitable
 
-Signature is calculated over the following structure:
-
-@itemize
-@item Magic number
-@item Niceness
-@item Recipient (32-byte recipient node's id)
-@item Sender
-@item Exchange public key
-@end itemize
+Signature is calculated over all previous fields.
 
 All following encryption is done using
 @url{https://www.schneier.com/academic/twofish/, Twofish} algorithm with
index 53ea301cdc0f9b3f782df70a4ad7f4b5c377d622..8a4ce6c816b364a9b67e5ba5e4c306bd6a66ea18 100644 (file)
@@ -13,7 +13,9 @@ spool/2WHB...OABQ/tx.lock
 spool/BYRR...CG6Q/rx.lock
 spool/BYRR...CG6Q/rx/
 spool/BYRR...CG6Q/tx.lock
+spool/BYRR...CG6Q/tx/AQUT...DGNT.seen
 spool/BYRR...CG6Q/tx/NSYY...ZUU6
+spool/BYRR...CG6Q/tx/VCSR...3VXX.seen
 spool/BYRR...CG6Q/tx/ZI5U...5RRQ
 @end verbatim
 
@@ -30,5 +32,9 @@ partly received file from @file{2WHB...OABQ} node. @file{tx} directory
 can not contain partly written files -- they are moved atomically from
 @file{tmp}.
 
+When @ref{nncp-toss} utility is called with @option{-seen} option, it
+will create empty @file{XXX.seen} files, telling that some kind of
+packet was already tossed sometime.
+
 Only one process can work with @file{rx}/@file{tx} directories at once,
 so there are corresponding lock files.
index cebf333e4dbed3139ca41b95dd94450df3c2fd61..1407c8e7e359f52c71117ab46bac23b3e8fe1cd3 100644 (file)
@@ -7,6 +7,7 @@
 * Ненадёжный/дорогой канал связи: UsecaseUnreliableRU.
 * Медленная/дорогая связь для больших объёмов данных, плохой QoS: UsecaseQoSRU.
 * Экстремальные наземные окружающие условия, нет связи: UsecaseNoLinkRU.
+* Односторонняя широковещательная связь: UsecaseBroadcastRU.
 * Частные, изолированные MitM/Sybil-устойчивые сети: UsecaseF2FRU.
 * Высоко защищённые изолированные компьютеры с воздушным зазором: UsecaseAirgapRU.
 * Обход сетевой цензуры, здоровье: UsecaseCensorRU.
@@ -113,6 +114,13 @@ NNCP поддерживает @ref{Niceness, приоритезацию траф
 давая возможность передачи, по сути, любых объёмов используя накопители
 небольших размеров.
 
+Вы также можете использовать CD-ROM и ленточные накопители:
+
+@verbatim
+% nncp-bundle -tx bob | cdrecord -tao -
+% nncp-bundle -tx bob | dd of=/dev/sa0 bs=10240
+@end verbatim
+
 @node UsecaseNoLinkRU
 @subsection Экстремальные наземные окружающие условия, нет связи
 
@@ -153,6 +161,27 @@ NNCP поддерживает @ref{Niceness, приоритезацию траф
 дальнейшей обработки. @command{nncp-xfer} это единственная команда
 используемая с переносными устройствами хранения.
 
+@node UsecaseBroadcastRU
+@subsection Односторонняя широковещательная связь
+
+Иногда у вас есть ёмкий, но односторонний, канал связи, например
+широковещательный сигнал со спутника. Вы не можете использовать online
+@ref{Sync, протокол синхронизации}, потому что он требует двустороннего
+взаимодействия.
+
+Вы можете использовать, так называемые, @ref{Bundles, пачки} и потоково
+отсылать их. Они -- всего-лишь последовательность @ref{Encrypted,
+зашифрованных пакетов}, которые вы можете принять.
+
+@verbatim
+% nncp-bundle -tx alice bob eve ... | команда для отправки широковещательной рассылки
+% команда для приёма широковещательной рассылки | nncp-bundle -rx
+@end verbatim
+
+Встроенная возможность определять дубляжи пакетов позволит вам
+переотправлять широковещательные рассылки время от времени, повышая
+шансы на то, что получатель примет их, регулярно слушая рассылку.
+
 @node UsecaseF2FRU
 @subsection Частные, изолированные MitM/Sybil-устойчивые сети
 
@@ -291,7 +320,7 @@ Bluetooth и WiFi могут быть и довольно быстрыми, по
 необходимо запустить другую фазу: @ref{nncp-toss, распаковку}, которая
 использует ваши приватные криптографические ключи. То есть, даже если вы
 потеряете свой компьютер, устройства хранения и тому прочее -- это не
-так плохо, потому-что вы не носите с собой приватные ключи (ведь так?),
+так плохо, потому что вы не носите с собой приватные ключи (ведь так?),
 вы не "распаковываете" эти пакеты сразу же на том же самом устройстве.
 Распаковка (чтение этих зашифрованных пакетов с извлечением переданных
 файлов и почтовых сообщений) может и должна бы быть произведена на
index 81af4bbcfc8c1e5fa62422dd76adac1332aa6510..47307f8d0546bdf08e4eb80570f639de616c2331 100644 (file)
@@ -9,6 +9,7 @@ See also this page @ref{Сценарии, on russian}.
 * Unreliable/expensive communication link: UsecaseUnreliable.
 * Slow/expensive link for high-volume data, bad QoS: UsecaseQoS.
 * Extreme terrestrial environments, no link: UsecaseNoLink.
+* One-way broadcasting communications: UsecaseBroadcast.
 * Private, isolated MitM/Sybil-resistant networks: UsecaseF2F.
 * Highly secure isolated air-gap computers: UsecaseAirgap.
 * Network censorship bypassing, health: UsecaseCensor.
@@ -108,6 +109,13 @@ Huge files could be split on smaller @ref{Chunked, chunks}, giving
 possibility to transfer virtually any volumes using small capacity
 storages.
 
+You can also use CD-ROM and tape drives:
+
+@verbatim
+% nncp-bundle -tx bob | cdrecord -tao -
+% nncp-bundle -tx bob | dd of=/dev/sa0 bs=10240
+@end verbatim
+
 @node UsecaseNoLink
 @section Extreme terrestrial environments, no link
 
@@ -145,6 +153,25 @@ to find all packets related to their node and copy them locally for
 further processing. @command{nncp-xfer} is the only command used with
 removable devices.
 
+@node UsecaseBroadcast
+@section One-way broadcasting communications
+
+Sometimes you have got high-bandwidth but unidirectional link, for
+example, satellite's broadcasting signal. You are not able to use online
+@ref{Sync, synchronization protocol} because it requires mutual interaction.
+
+You can use @ref{Bundles, bundles} and stream them above. They are just
+a sequence of @ref{Encrypted, encrypted packets} you can catch on.
+
+@verbatim
+% nncp-bundle -tx alice bob eve ... | command to send broadcast
+% command to receive broadcast | nncp-bundle -rx
+@end verbatim
+
+With built-in packet duplicates detection ability, you can retransmit
+your broadcasts from time to time, to increase chances the recipient
+will catch them by regular stream listening.
+
 @node UsecaseF2F
 @section Private, isolated MitM/Sybil-resistant networks
 
index fafb226f3c733a81b307eedaed226f1e0f87f6d1..03870daf529fca70fcab2c631fa2a13c5d1d8eb1 100755 (executable)
@@ -112,9 +112,9 @@ encrypted (E2EE), explicitly authenticated by known participants public
 keys. 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 sneakernet/floppynet, dead drops and air-gapped
-computers support. But online TCP daemon with full-duplex resumable data
-transmission exists.
+Out-of-box offline sneakernet/floppynet, dead drops, sequential and
+append-only CD-ROM/tape storages, air-gapped computers support. But
+online TCP daemon with full-duplex resumable data transmission exists.
 
 ------------------------ >8 ------------------------
 
@@ -161,7 +161,8 @@ NNCP (Node to Node copy) это набор утилит упрощающий б
 поведения.
 
 Поддержка из коробки offline флоппинета, тайников для сброса информации
-(dead drop) и компьютеров с "воздушным зазором" (air-gap). Но также
+(dead drop), последовательных и только-для-записи CD-ROM/ленточных
+хранилищ, компьютеров с "воздушным зазором" (air-gap). Но также
 существует и online TCP демон с полнодуплексной возобновляемой передачей
 данных.
 
index 22b33138499952702da92323258ec637a56e8ef8..c6bf12ddd8b33df446ab5272a51c9ce010add3bb 100644 (file)
@@ -25,7 +25,8 @@ PORTDOCS=     AUTHORS NEWS NEWS.RU README README.RU THANKS
 INFO=          nncp
 INSTALL_TARGET=        install-strip
 
-PLIST_FILES=   bin/nncp-call \
+PLIST_FILES=   bin/nncp-bundle \
+               bin/nncp-call \
                bin/nncp-caller \
                bin/nncp-cfgenc \
                bin/nncp-cfgmin \
index d33b292e02e54762b68dfc20a42ba0c336d1c9f7..37b709118e5bd1d7c36b1f4aba82b15f103be4e6 100644 (file)
@@ -9,8 +9,8 @@ encrypted (E2EE), explicitly authenticated by known participants public
 keys. 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 sneakernet/floppynet, dead drops and air-gapped
-computers support. But online TCP daemon with full-duplex resumable data
-transmission exists.
+Out-of-box offline sneakernet/floppynet, dead drops, sequential and
+append-only CD-ROM/tape storages, air-gapped computers support. But
+online TCP daemon with full-duplex resumable data transmission exists.
 
 WWW: http://www.nncpgo.org/
index 3a642f68567ff4487630dab5e3b96b676d5cba34..0a1b1d8a0a86d73874e221b8087f5d82ac4734c6 100644 (file)
@@ -32,7 +32,9 @@ import (
 )
 
 const (
-       CfgPathEnv = "NNCPCFG"
+       CfgPathEnv  = "NNCPCFG"
+       CfgSpoolEnv = "NNCPSPOOL"
+       CfgLogEnv   = "NNCPLOG"
 )
 
 var (
@@ -414,11 +416,3 @@ func CfgParse(data []byte) (*Ctx, error) {
        }
        return &ctx, nil
 }
-
-func CfgPathFromEnv(cmdlineFlag *string) (p string) {
-       p = os.Getenv(CfgPathEnv)
-       if p == "" {
-               p = *cmdlineFlag
-       }
-       return
-}
diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go
new file mode 100644 (file)
index 0000000..e79f9e1
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+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/>.
+*/
+
+// Create/digest stream of NNCP encrypted packets
+package main
+
+import (
+       "archive/tar"
+       "bufio"
+       "bytes"
+       "flag"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "log"
+       "os"
+       "path/filepath"
+       "strconv"
+       "strings"
+
+       "cypherpunks.ru/nncp"
+       "github.com/davecgh/go-xdr/xdr2"
+       "golang.org/x/crypto/blake2b"
+)
+
+const (
+       CopyBufSize = 1 << 17
+)
+
+func usage() {
+       fmt.Fprintf(os.Stderr, nncp.UsageHeader())
+       fmt.Fprintln(os.Stderr, "nncp-bundle -- Create/digest stream of NNCP encrypted packets\n")
+       fmt.Fprintf(os.Stderr, "Usage: %s [options] -tx [-delete] NODE [NODE ...] > ...\n", os.Args[0])
+       fmt.Fprintf(os.Stderr, "       %s [options] -rx -delete [-dryrun] [NODE ...] < ...\n", os.Args[0])
+       fmt.Fprintf(os.Stderr, "       %s [options] -rx [-check] [-dryrun] [NODE ...] < ...\n", os.Args[0])
+       fmt.Fprintln(os.Stderr, "Options:")
+       flag.PrintDefaults()
+}
+
+func main() {
+       var (
+               cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
+               niceRaw   = flag.Int("nice", 255, "Minimal required niceness")
+               doRx      = flag.Bool("rx", false, "Receive packets")
+               doTx      = flag.Bool("tx", false, "Transfer packets")
+               doDelete  = flag.Bool("delete", false, "Delete transferred packets")
+               doCheck   = flag.Bool("check", false, "Check integrity while receiving")
+               dryRun    = flag.Bool("dryrun", false, "Do not writings")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
+               quiet     = flag.Bool("quiet", false, "Print only errors")
+               debug     = flag.Bool("debug", false, "Print debug messages")
+               version   = flag.Bool("version", false, "Print version information")
+               warranty  = flag.Bool("warranty", false, "Print warranty information")
+       )
+       flag.Usage = usage
+       flag.Parse()
+       if *warranty {
+               fmt.Println(nncp.Warranty)
+               return
+       }
+       if *version {
+               fmt.Println(nncp.VersionGet())
+               return
+       }
+       if *niceRaw < 1 || *niceRaw > 255 {
+               log.Fatalln("-nice must be between 1 and 255")
+       }
+       nice := uint8(*niceRaw)
+       if *doRx && *doTx {
+               log.Fatalln("-rx and -tx can not be set simultaneously")
+       }
+       if !*doRx && !*doTx {
+               log.Fatalln("At least one of -rx and -tx must be specified")
+       }
+
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
+       if err != nil {
+               log.Fatalln("Error during initialization:", err)
+       }
+
+       nodeIds := make(map[nncp.NodeId]struct{}, flag.NArg())
+       for i := 0; i < flag.NArg(); i++ {
+               node, err := ctx.FindNode(flag.Arg(i))
+               if err != nil {
+                       log.Fatalln("Invalid specified:", err)
+               }
+               nodeIds[*node.Id] = struct{}{}
+       }
+
+       sds := nncp.SDS{}
+       if *doTx {
+               sds["xx"] = string(nncp.TTx)
+               var pktName string
+               bufStdout := bufio.NewWriter(os.Stdout)
+               tarWr := tar.NewWriter(bufStdout)
+               for nodeId, _ := range nodeIds {
+                       sds["node"] = nodeId.String()
+                       for job := range ctx.Jobs(&nodeId, nncp.TTx) {
+                               pktName = filepath.Base(job.Fd.Name())
+                               sds["pkt"] = pktName
+                               if job.PktEnc.Nice > nice {
+                                       ctx.LogD("nncp-bundle", sds, "too nice")
+                                       job.Fd.Close()
+                                       continue
+                               }
+                               if err = tarWr.WriteHeader(&tar.Header{
+                                       Name: strings.Join([]string{
+                                               nncp.NNCPBundlePrefix,
+                                               nodeId.String(),
+                                               ctx.SelfId.String(),
+                                               pktName,
+                                       }, "/"),
+                                       Mode:     0440,
+                                       Size:     job.Size,
+                                       Typeflag: tar.TypeReg,
+                               }); err != nil {
+                                       log.Fatalln("Error writing tar header:", err)
+                               }
+                               if _, err = io.Copy(tarWr, job.Fd); err != nil {
+                                       log.Fatalln("Error during copying to tar:", err)
+                               }
+                               job.Fd.Close()
+                               if err = tarWr.Flush(); err != nil {
+                                       log.Fatalln("Error during tar flushing:", err)
+                               }
+                               if err = bufStdout.Flush(); err != nil {
+                                       log.Fatalln("Error during stdout flushing:", err)
+                               }
+                               if *doDelete {
+                                       if err = os.Remove(job.Fd.Name()); err != nil {
+                                               log.Fatalln("Error during deletion:", err)
+                                       }
+                               }
+                               ctx.LogI("nncp-bundle", nncp.SdsAdd(sds, nncp.SDS{
+                                       "size": strconv.FormatInt(job.Size, 10),
+                               }), "")
+                       }
+               }
+               if err = tarWr.Close(); err != nil {
+                       log.Fatalln("Error during tar closing:", err)
+               }
+       } else {
+               bufStdin := bufio.NewReaderSize(os.Stdin, CopyBufSize*2)
+               var peeked []byte
+               var prefixIdx int
+               var tarR *tar.Reader
+               var entry *tar.Header
+               var exists bool
+               pktEncBuf := make([]byte, nncp.PktEncOverhead)
+               var pktEnc *nncp.PktEnc
+               var pktName string
+               var selfPath string
+               var dstPath string
+               for {
+                       peeked, err = bufStdin.Peek(CopyBufSize)
+                       if err != nil && err != io.EOF {
+                               log.Fatalln("Error during reading:", err)
+                       }
+                       prefixIdx = bytes.Index(peeked, []byte(nncp.NNCPBundlePrefix))
+                       if prefixIdx == -1 {
+                               if err == io.EOF {
+                                       break
+                               }
+                               bufStdin.Discard(bufStdin.Buffered() - (len(nncp.NNCPBundlePrefix) - 1))
+                               continue
+                       }
+                       bufStdin.Discard(prefixIdx)
+                       tarR = tar.NewReader(bufStdin)
+                       sds["xx"] = string(nncp.TRx)
+                       entry, err = tarR.Next()
+                       if err != nil {
+                               if err != io.EOF {
+                                       ctx.LogD(
+                                               "nncp-bundle",
+                                               nncp.SdsAdd(sds, nncp.SDS{"err": err}),
+                                               "error reading tar",
+                                       )
+                               }
+                               continue
+                       }
+                       sds["pkt"] = entry.Name
+                       if entry.Size < nncp.PktEncOverhead {
+                               ctx.LogD("nncp-bundle", sds, "Too small packet")
+                               continue
+                       }
+                       pktName = filepath.Base(entry.Name)
+                       if _, err = nncp.FromBase32(pktName); err != nil {
+                               ctx.LogD("nncp-bundle", sds, "Bad packet name")
+                               continue
+                       }
+                       if _, err = io.ReadFull(tarR, pktEncBuf); err != nil {
+                               ctx.LogD("nncp-bundle", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "read")
+                               continue
+                       }
+                       if _, err = xdr.Unmarshal(bytes.NewReader(pktEncBuf), &pktEnc); err != nil {
+                               ctx.LogD("nncp-bundle", sds, "Bad packet structure")
+                               continue
+                       }
+                       if pktEnc.Magic != nncp.MagicNNCPEv2 {
+                               ctx.LogD("nncp-bundle", sds, "Bad packet magic number")
+                               continue
+                       }
+                       if pktEnc.Nice > nice {
+                               ctx.LogD("nncp-bundle", sds, "too nice")
+                               continue
+                       }
+                       if *pktEnc.Sender == *ctx.SelfId && *doDelete {
+                               if len(nodeIds) > 0 {
+                                       if _, exists = nodeIds[*pktEnc.Recipient]; !exists {
+                                               ctx.LogD("nncp-bundle", sds, "Recipient is not requested")
+                                               continue
+                                       }
+                               }
+                               nodeId32 := nncp.ToBase32(pktEnc.Recipient[:])
+                               sds["xx"] = string(nncp.TTx)
+                               sds["node"] = nodeId32
+                               sds["pkt"] = pktName
+                               dstPath = filepath.Join(
+                                       ctx.Spool,
+                                       nodeId32,
+                                       string(nncp.TTx),
+                                       pktName,
+                               )
+                               if _, err = os.Stat(dstPath); err != nil {
+                                       ctx.LogD("nncp-bundle", sds, "Packet is already missing")
+                                       continue
+                               }
+                               hsh, err := blake2b.New256(nil)
+                               if err != nil {
+                                       log.Fatalln("Error during hasher creation:", err)
+                               }
+                               if _, err = hsh.Write(pktEncBuf); err != nil {
+                                       log.Fatalln("Error during writing:", err)
+                               }
+                               if _, err = io.Copy(hsh, tarR); err != nil {
+                                       log.Fatalln("Error during copying:", err)
+                               }
+                               if nncp.ToBase32(hsh.Sum(nil)) == pktName {
+                                       ctx.LogI("nncp-bundle", sds, "removed")
+                                       if !*dryRun {
+                                               os.Remove(dstPath)
+                                       }
+                               } else {
+                                       ctx.LogE("nncp-bundle", sds, "bad checksum")
+                               }
+                               continue
+                       }
+                       if *pktEnc.Recipient != *ctx.SelfId {
+                               ctx.LogD("nncp-bundle", sds, "Unknown recipient")
+                               continue
+                       }
+                       if len(nodeIds) > 0 {
+                               if _, exists = nodeIds[*pktEnc.Sender]; !exists {
+                                       ctx.LogD("nncp-bundle", sds, "Sender is not requested")
+                                       continue
+                               }
+                       }
+                       sds["node"] = nncp.ToBase32(pktEnc.Recipient[:])
+                       sds["pkt"] = pktName
+                       selfPath = filepath.Join(ctx.Spool, ctx.SelfId.String(), string(nncp.TRx))
+                       dstPath = filepath.Join(selfPath, pktName)
+                       if _, err = os.Stat(dstPath); err == nil || !os.IsNotExist(err) {
+                               ctx.LogD("nncp-bundle", sds, "Packet already exists")
+                               continue
+                       }
+                       if _, err = os.Stat(dstPath + nncp.SeenSuffix); err == nil || !os.IsNotExist(err) {
+                               ctx.LogD("nncp-bundle", sds, "Packet already exists")
+                               continue
+                       }
+                       if *doCheck {
+                               if *dryRun {
+                                       hsh, err := blake2b.New256(nil)
+                                       if err != nil {
+                                               log.Fatalln("Error during hasher creation:", err)
+                                       }
+                                       if _, err = hsh.Write(pktEncBuf); err != nil {
+                                               log.Fatalln("Error during writing:", err)
+                                       }
+                                       if _, err = io.Copy(hsh, tarR); err != nil {
+                                               log.Fatalln("Error during copying:", err)
+                                       }
+                                       if nncp.ToBase32(hsh.Sum(nil)) != pktName {
+                                               ctx.LogE("nncp-bundle", sds, "bad checksum")
+                                               continue
+                                       }
+                               } else {
+                                       tmp, err := ctx.NewTmpFileWHash()
+                                       if err != nil {
+                                               log.Fatalln("Error during temporary file creation:", err)
+                                       }
+                                       if _, err = tmp.W.Write(pktEncBuf); err != nil {
+                                               log.Fatalln("Error during writing:", err)
+                                       }
+                                       if _, err = io.Copy(tmp.W, tarR); err != nil {
+                                               log.Fatalln("Error during copying:", err)
+                                       }
+                                       if err = tmp.W.Flush(); err != nil {
+                                               log.Fatalln("Error during flusing:", err)
+                                       }
+                                       if nncp.ToBase32(tmp.Hsh.Sum(nil)) == pktName {
+                                               if err = tmp.Commit(selfPath); err != nil {
+                                                       log.Fatalln("Error during commiting:", err)
+                                               }
+                                       } else {
+                                               ctx.LogE("nncp-bundle", sds, "bad checksum")
+                                               tmp.Cancel()
+                                               continue
+                                       }
+                               }
+                       } else {
+                               if *dryRun {
+                                       if _, err = io.Copy(ioutil.Discard, tarR); err != nil {
+                                               log.Fatalln("Error during copying:", err)
+                                       }
+                               } else {
+                                       tmp, err := ctx.NewTmpFile()
+                                       if err != nil {
+                                               log.Fatalln("Error during temporary file creation:", err)
+                                       }
+                                       bufTmp := bufio.NewWriterSize(tmp, CopyBufSize)
+                                       if _, err = bufTmp.Write(pktEncBuf); err != nil {
+                                               log.Fatalln("Error during writing:", err)
+                                       }
+                                       if _, err = io.Copy(bufTmp, tarR); err != nil {
+                                               log.Fatalln("Error during copying:", err)
+                                       }
+                                       if err = bufTmp.Flush(); err != nil {
+                                               log.Fatalln("Error during flushing:", err)
+                                       }
+                                       tmp.Sync()
+                                       tmp.Close()
+                                       if err = os.MkdirAll(selfPath, os.FileMode(0700)); err != nil {
+                                               log.Fatalln("Error during mkdir:", err)
+                                       }
+                                       if err = os.Rename(tmp.Name(), dstPath); err != nil {
+                                               log.Fatalln("Error during renaming:", err)
+                                       }
+                               }
+                       }
+                       ctx.LogI("nncp-bundle", nncp.SdsAdd(sds, nncp.SDS{
+                               "size": strconv.FormatInt(entry.Size, 10),
+                       }), "")
+               }
+       }
+}
index 42f761faa10c9952964a4f96857d61b1b4869132..062593e6301ecd1f88d8cafc8721d68c623fe6bc 100644 (file)
@@ -22,7 +22,6 @@ package main
 import (
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
        "strings"
@@ -40,14 +39,16 @@ func usage() {
 
 func main() {
        var (
-               cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw  = flag.Int("nice", 255, "Minimal required niceness")
-               rxOnly   = flag.Bool("rx", false, "Only receive packets")
-               txOnly   = flag.Bool("tx", false, "Only transfer packets")
-               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", 255, "Minimal required niceness")
+               rxOnly    = flag.Bool("rx", false, "Only receive packets")
+               txOnly    = flag.Bool("tx", false, "Only transfer packets")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
+               quiet     = flag.Bool("quiet", false, "Print only errors")
+               debug     = flag.Bool("debug", false, "Print debug messages")
+               version   = flag.Bool("version", false, "Print version information")
+               warranty  = flag.Bool("warranty", false, "Print warranty information")
 
                onlineDeadline = flag.Uint("onlinedeadline", 0, "Override onlinedeadline option")
                maxOnlineTime  = flag.Uint("maxonlinetime", 0, "Override maxonlinetime option")
@@ -74,19 +75,13 @@ func main() {
                log.Fatalln("-rx and -tx can not be set simultaneously")
        }
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        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)
+               log.Fatalln("Error during initialization:", err)
        }
        if ctx.Self == nil {
                log.Fatalln("Config lacks private keys")
        }
-       ctx.Quiet = *quiet
-       ctx.Debug = *debug
 
        splitted := strings.SplitN(flag.Arg(0), ":", 2)
        node, err := ctx.FindNode(splitted[0])
index 3cbbe8f19d05ca8b9797139a2450e6f98773d82d..459a623858c7d117d04fc1abc0a5c380c1dc4b5a 100644 (file)
@@ -22,7 +22,6 @@ package main
 import (
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
        "strconv"
@@ -42,11 +41,13 @@ func usage() {
 
 func main() {
        var (
-               cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               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")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
+               quiet     = flag.Bool("quiet", false, "Print only errors")
+               debug     = flag.Bool("debug", false, "Print debug messages")
+               version   = flag.Bool("version", false, "Print version information")
+               warranty  = flag.Bool("warranty", false, "Print warranty information")
        )
        flag.Usage = usage
        flag.Parse()
@@ -59,19 +60,13 @@ func main() {
                return
        }
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        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)
+               log.Fatalln("Error during initialization:", err)
        }
        if ctx.Self == nil {
                log.Fatalln("Config lacks private keys")
        }
-       ctx.Quiet = *quiet
-       ctx.Debug = *debug
 
        var nodes []*nncp.Node
        if flag.NArg() > 0 {
index 48df34e0284f45e8a655bc7457c94ca430d7510f..7de3247e466ac1f839a55807f6be5006aa9600f2 100644 (file)
@@ -22,7 +22,6 @@ package main
 import (
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
 
@@ -54,13 +53,9 @@ func main() {
                return
        }
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, "", "", false, false)
        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)
+               log.Fatalln("Error during initialization:", err)
        }
 
        cfg := nncp.CfgYAML{
index 2c06b1425e93169989e0b2dac2c427c6ccc6b802..a10c8cfd2f0417f749b661ef3c13b5a23b9d7018 100644 (file)
@@ -22,7 +22,6 @@ package main
 import (
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
 
@@ -38,12 +37,14 @@ func usage() {
 
 func main() {
        var (
-               cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               nodeRaw  = flag.String("node", "", "Process only that node")
-               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")
+               nodeRaw   = flag.String("node", "", "Process only that node")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
+               quiet     = flag.Bool("quiet", false, "Print only errors")
+               debug     = flag.Bool("debug", false, "Print debug messages")
+               version   = flag.Bool("version", false, "Print version information")
+               warranty  = flag.Bool("warranty", false, "Print warranty information")
        )
        flag.Usage = usage
        flag.Parse()
@@ -56,16 +57,10 @@ func main() {
                return
        }
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        if err != nil {
-               log.Fatalln("Can not read config:", err)
+               log.Fatalln("Error during initialization:", 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 != "" {
index e6ec2d385648ae1c84c8e959cc381addbc3d8e92..de0a0b3bf62b10cd6b5a0e43e0bcd3a0d489240d 100644 (file)
@@ -22,7 +22,6 @@ package main
 import (
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "net"
        "os"
@@ -41,14 +40,16 @@ func usage() {
 
 func main() {
        var (
-               cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw  = flag.Int("nice", 255, "Minimal required niceness")
-               bind     = flag.String("bind", "[::]:5400", "Address to bind to")
-               maxConn  = flag.Int("maxconn", 128, "Maximal number of simultaneous connections")
-               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", 255, "Minimal required niceness")
+               bind      = flag.String("bind", "[::]:5400", "Address to bind to")
+               maxConn   = flag.Int("maxconn", 128, "Maximal number of simultaneous connections")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
+               quiet     = flag.Bool("quiet", false, "Print only errors")
+               debug     = flag.Bool("debug", false, "Print debug messages")
+               version   = flag.Bool("version", false, "Print version information")
+               warranty  = flag.Bool("warranty", false, "Print warranty information")
        )
        flag.Usage = usage
        flag.Parse()
@@ -65,19 +66,13 @@ func main() {
        }
        nice := uint8(*niceRaw)
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        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)
+               log.Fatalln("Error during initialization:", err)
        }
        if ctx.Self == nil {
                log.Fatalln("Config lacks private keys")
        }
-       ctx.Quiet = *quiet
-       ctx.Debug = *debug
 
        ln, err := net.Listen("tcp", *bind)
        if err != nil {
index d922882fec7a3a301e9c793cddfc378ee122de61..3c35ab9081799429d12dcd6edcabb997a5759440 100644 (file)
@@ -22,7 +22,6 @@ package main
 import (
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
        "strings"
@@ -46,6 +45,8 @@ func main() {
                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")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
                quiet     = flag.Bool("quiet", false, "Print only errors")
                debug     = flag.Bool("debug", false, "Print debug messages")
                version   = flag.Bool("version", false, "Print version information")
@@ -70,19 +71,13 @@ func main() {
        }
        nice := uint8(*niceRaw)
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        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)
+               log.Fatalln("Error during initialization:", err)
        }
        if ctx.Self == nil {
                log.Fatalln("Config lacks private keys")
        }
-       ctx.Quiet = *quiet
-       ctx.Debug = *debug
 
        splitted := strings.SplitN(flag.Arg(1), ":", 2)
        if len(splitted) != 2 {
index da5268fbda69d63ac8f803ece6e73364d7802014..c089abc102c88a9c8e6d6fb8ed649f9d1c000eb0 100644 (file)
@@ -22,7 +22,6 @@ package main
 import (
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
        "path/filepath"
@@ -40,13 +39,15 @@ func usage() {
 
 func main() {
        var (
-               cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw  = flag.Int("nice", nncp.DefaultNiceFreq, "Outbound packet niceness")
-               minSize  = flag.Uint64("minsize", 0, "Minimal required resulting packet size, 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")
+               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, in KiB")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
+               quiet     = flag.Bool("quiet", false, "Print only errors")
+               debug     = flag.Bool("debug", false, "Print debug messages")
+               version   = flag.Bool("version", false, "Print version information")
+               warranty  = flag.Bool("warranty", false, "Print warranty information")
        )
        flag.Usage = usage
        flag.Parse()
@@ -67,19 +68,13 @@ func main() {
        }
        nice := uint8(*niceRaw)
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        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)
+               log.Fatalln("Error during initialization:", err)
        }
        if ctx.Self == nil {
                log.Fatalln("Config lacks private keys")
        }
-       ctx.Quiet = *quiet
-       ctx.Debug = *debug
 
        splitted := strings.SplitN(flag.Arg(0), ":", 2)
        if len(splitted) != 2 {
index 653f213b7977049d1985fc99d9d172c97c435de2..c045715447ee36bb72e7c0815bbbadb7145ebc70 100644 (file)
@@ -23,7 +23,6 @@ import (
        "bufio"
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
 
@@ -40,6 +39,7 @@ func usage() {
 func main() {
        var (
                cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
+               logPath  = flag.String("log", "", "Override path to logfile")
                debug    = flag.Bool("debug", false, "Print debug messages")
                version  = flag.Bool("version", false, "Print version information")
                warranty = flag.Bool("warranty", false, "Print warranty information")
@@ -55,13 +55,9 @@ func main() {
                return
        }
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, "", *logPath, false, *debug)
        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)
+               log.Fatalln("Error during initialization:", err)
        }
 
        fd, err := os.Open(ctx.LogPath)
index f5f4de791d1e8023d75f8b34a6eaeaed018896e4..f9d346f4f27cb5f206ec1b6192cfdcf635b9dbf6 100644 (file)
@@ -40,13 +40,15 @@ func usage() {
 
 func main() {
        var (
-               cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw  = flag.Int("nice", nncp.DefaultNiceMail, "Outbound packet niceness")
-               minSize  = flag.Uint64("minsize", 0, "Minimal required resulting packet size, 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")
+               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, in KiB")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
+               quiet     = flag.Bool("quiet", false, "Print only errors")
+               debug     = flag.Bool("debug", false, "Print debug messages")
+               version   = flag.Bool("version", false, "Print version information")
+               warranty  = flag.Bool("warranty", false, "Print warranty information")
        )
        flag.Usage = usage
        flag.Parse()
@@ -67,19 +69,13 @@ func main() {
        }
        nice := uint8(*niceRaw)
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        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)
+               log.Fatalln("Error during initialization:", err)
        }
        if ctx.Self == nil {
                log.Fatalln("Config lacks private keys")
        }
-       ctx.Quiet = *quiet
-       ctx.Debug = *debug
 
        node, err := ctx.FindNode(flag.Arg(0))
        if err != nil {
index 93759b69ee7037fca2984d42580fbd7bc2c1e13f..a7b346f4042d1b938f9d9e06c3b137895fdd7fef 100644 (file)
@@ -26,7 +26,6 @@ import (
        "flag"
        "fmt"
        "io"
-       "io/ioutil"
        "log"
        "os"
 
@@ -112,15 +111,11 @@ func main() {
        }
        var pktEnc nncp.PktEnc
        _, err = xdr.Unmarshal(bytes.NewReader(beginning), &pktEnc)
-       if err == nil && pktEnc.Magic == nncp.MagicNNCPEv1 {
+       if err == nil && pktEnc.Magic == nncp.MagicNNCPEv2 {
                if *dump {
-                       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+                       ctx, err := nncp.CtxFromCmdline(*cfgPath, "", "", false, false)
                        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)
+                               log.Fatalln("Error during initialization:", err)
                        }
                        if ctx.Self == nil {
                                log.Fatalln("Config lacks private keys")
@@ -143,8 +138,8 @@ func main() {
                        return
                }
                fmt.Printf(
-                       "Packet type: encrypted\nNiceness: %d\nSender: %s\n",
-                       pktEnc.Nice, pktEnc.Sender,
+                       "Packet type: encrypted\nNiceness: %d\nSender: %s\nRecipient: %s\n",
+                       pktEnc.Nice, pktEnc.Sender, pktEnc.Recipient,
                )
                return
        }
index 45354d73e1eb3b6b1f09eb6e2b2d704399e6207d..9c6cae2af1fe4dd1ab3358548eaecfaf11f9eaa2 100644 (file)
@@ -264,17 +264,19 @@ func findMetas(ctx *nncp.Ctx, dirPath string) []string {
 
 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")
+               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")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
+               quiet     = flag.Bool("quiet", false, "Print only errors")
+               debug     = flag.Bool("debug", false, "Print debug messages")
+               version   = flag.Bool("version", false, "Print version information")
+               warranty  = flag.Bool("warranty", false, "Print warranty information")
        )
        flag.Usage = usage
        flag.Parse()
@@ -287,16 +289,10 @@ func main() {
                return
        }
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        if err != nil {
-               log.Fatalln("Can not read config:", err)
+               log.Fatalln("Error during initialization:", 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 != "" {
index 00a13a4fd2652ea13bb3f56a7180fbf13a5df307..b616ad8163cc578faae942a3c7cdde052dc99365 100644 (file)
@@ -22,10 +22,10 @@ package main
 import (
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
        "path/filepath"
+       "strings"
 
        "cypherpunks.ru/nncp"
 )
@@ -33,17 +33,32 @@ import (
 func usage() {
        fmt.Fprintf(os.Stderr, nncp.UsageHeader())
        fmt.Fprintln(os.Stderr, "nncp-rm -- remove packet\n")
-       fmt.Fprintf(os.Stderr, "Usage: %s [options] NODE PKT\nOptions:\n", os.Args[0])
+       fmt.Fprintf(os.Stderr, "Usage: %s [options] -tmp\n", os.Args[0])
+       fmt.Fprintf(os.Stderr, "       %s [options] -lock\n", os.Args[0])
+       fmt.Fprintf(os.Stderr, "       %s [options] -node NODE -part\n", os.Args[0])
+       fmt.Fprintf(os.Stderr, "       %s [options] -node NODE -seen\n", os.Args[0])
+       fmt.Fprintf(os.Stderr, "       %s [options] -node NODE {-rx|-tx}\n", os.Args[0])
+       fmt.Fprintf(os.Stderr, "       %s [options] -node NODE -pkt PKT\n", os.Args[0])
+       fmt.Fprintln(os.Stderr, "Options:")
        flag.PrintDefaults()
 }
 
 func main() {
        var (
-               cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               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")
+               doTmp     = flag.Bool("tmp", false, "Remove all temporary files")
+               doLock    = flag.Bool("lock", false, "Remove all lock files")
+               nodeRaw   = flag.String("node", "", "Node to remove files in")
+               doRx      = flag.Bool("rx", false, "Process received packets")
+               doTx      = flag.Bool("tx", false, "Process transfered packets")
+               doPart    = flag.Bool("part", false, "Remove only .part files")
+               doSeen    = flag.Bool("seen", false, "Remove only .seen files")
+               pktRaw    = flag.String("pkt", "", "Packet to remove")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               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()
@@ -55,42 +70,93 @@ func main() {
                fmt.Println(nncp.VersionGet())
                return
        }
-       if flag.NArg() != 2 {
-               usage()
-               os.Exit(1)
-       }
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, "", *quiet, *debug)
        if err != nil {
-               log.Fatalln("Can not read config:", err)
+               log.Fatalln("Error during initialization:", err)
        }
-       ctx, err := nncp.CfgParse(cfgRaw)
-       if err != nil {
-               log.Fatalln("Can not parse config:", err)
-       }
-       ctx.Quiet = *quiet
-       ctx.Debug = *debug
 
-       node, err := ctx.FindNode(flag.Arg(0))
+       if *doTmp {
+               err = filepath.Walk(filepath.Join(ctx.Spool, "tmp"), func(path string, info os.FileInfo, err error) error {
+                       if err != nil {
+                               return err
+                       }
+                       if info.IsDir() {
+                               return nil
+                       }
+                       ctx.LogI("nncp-rm", nncp.SDS{"file": path}, "")
+                       return os.Remove(path)
+               })
+               if err != nil {
+                       log.Fatalln("Error during walking:", err)
+               }
+               return
+       }
+       if *doLock {
+               err = filepath.Walk(ctx.Spool, func(path string, info os.FileInfo, err error) error {
+                       if err != nil {
+                               return err
+                       }
+                       if info.IsDir() {
+                               return nil
+                       }
+                       if strings.HasSuffix(info.Name(), ".lock") {
+                               ctx.LogI("nncp-rm", nncp.SDS{"file": path}, "")
+                               return os.Remove(path)
+                       }
+                       return nil
+               })
+               if err != nil {
+                       log.Fatalln("Error during walking:", err)
+               }
+               return
+       }
+       if *nodeRaw == "" {
+               usage()
+               os.Exit(1)
+       }
+       node, err := ctx.FindNode(*nodeRaw)
        if err != nil {
-               log.Fatalln("Invalid NODE specified:", err)
+               log.Fatalln("Invalid -node specified:", err)
        }
-
-       pktName := flag.Arg(1)
-       remove := func(xx nncp.TRxTx) bool {
-               for job := range ctx.Jobs(node.Id, xx) {
-                       job.Fd.Close()
-                       if filepath.Base(job.Fd.Name()) == pktName {
-                               if err = os.Remove(job.Fd.Name()); err != nil {
-                                       log.Fatalln("Can not remove packet:", err)
-                               }
-                               return true
+       remove := func(xx nncp.TRxTx) error {
+               return filepath.Walk(filepath.Join(ctx.Spool, node.Id.String(), string(xx)), func(path string, info os.FileInfo, err error) error {
+                       if err != nil {
+                               return err
+                       }
+                       if info.IsDir() {
+                               return nil
                        }
+                       if *doSeen && strings.HasSuffix(info.Name(), nncp.SeenSuffix) {
+                               ctx.LogI("nncp-rm", nncp.SDS{"file": path}, "")
+                               return os.Remove(path)
+                       }
+                       if *doPart && strings.HasSuffix(info.Name(), nncp.PartSuffix) {
+                               ctx.LogI("nncp-rm", nncp.SDS{"file": path}, "")
+                               return os.Remove(path)
+                       }
+                       if *pktRaw != "" && filepath.Base(info.Name()) == *pktRaw {
+                               ctx.LogI("nncp-rm", nncp.SDS{"file": path}, "")
+                               return os.Remove(path)
+                       }
+                       if !*doSeen &&
+                               !*doPart &&
+                               (*doRx || *doTx) &&
+                               ((*doRx && xx == nncp.TRx) || (*doTx && xx == nncp.TTx)) {
+                               ctx.LogI("nncp-rm", nncp.SDS{"file": path}, "")
+                               return os.Remove(path)
+                       }
+                       return nil
+               })
+       }
+       if *pktRaw != "" || *doRx || *doSeen || *doPart {
+               if err = remove(nncp.TRx); err != nil {
+                       log.Fatalln("Can not remove:", err)
                }
-               return false
        }
-
-       if !(remove(nncp.TRx) || remove(nncp.TTx)) {
-               log.Fatalln("Have not found specified packet")
+       if *pktRaw != "" || *doTx {
+               if err = remove(nncp.TTx); err != nil {
+                       log.Fatalln("Can not remove:", err)
+               }
        }
 }
index 0c27b1b8d27f4ae0940d806cf539c29e0d139a39..fd20e620a6a51954c22c08e2a8adaadb66588f60 100644 (file)
@@ -22,7 +22,6 @@ package main
 import (
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
        "sort"
@@ -40,11 +39,12 @@ func usage() {
 
 func main() {
        var (
-               cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               nodeRaw  = flag.String("node", "", "Process only that node")
-               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")
+               nodeRaw   = flag.String("node", "", "Process only that node")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               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()
@@ -57,15 +57,10 @@ func main() {
                return
        }
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, "", false, *debug)
        if err != nil {
-               log.Fatalln("Can not read config:", err)
+               log.Fatalln("Error during initialization:", err)
        }
-       ctx, err := nncp.CfgParse(cfgRaw)
-       if err != nil {
-               log.Fatalln("Can not parse config:", err)
-       }
-       ctx.Debug = *debug
 
        var nodeOnly *nncp.Node
        if *nodeRaw != "" {
index 71a15ee87561742e0aeb1d985661bbdb10bec2fc..eb2158c9c67d17b2a1ddfa6dd4eac52a05f8c03f 100644 (file)
@@ -22,7 +22,6 @@ package main
 import (
        "flag"
        "fmt"
-       "io/ioutil"
        "log"
        "os"
        "time"
@@ -39,15 +38,18 @@ func usage() {
 
 func main() {
        var (
-               cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               nodeRaw  = flag.String("node", "", "Process only that node")
-               niceRaw  = flag.Int("nice", 255, "Minimal required niceness")
-               dryRun   = flag.Bool("dryrun", false, "Do not actually write any tossed data")
-               cycle    = flag.Uint("cycle", 0, "Repeat tossing after N seconds in infinite loop")
-               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")
+               nodeRaw   = flag.String("node", "", "Process only that node")
+               niceRaw   = flag.Int("nice", 255, "Minimal required niceness")
+               dryRun    = flag.Bool("dryrun", false, "Do not actually write any tossed data")
+               doSeen    = flag.Bool("seen", false, "Create .seen files")
+               cycle     = flag.Uint("cycle", 0, "Repeat tossing after N seconds in infinite loop")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
+               quiet     = flag.Bool("quiet", false, "Print only errors")
+               debug     = flag.Bool("debug", false, "Print debug messages")
+               version   = flag.Bool("version", false, "Print version information")
+               warranty  = flag.Bool("warranty", false, "Print warranty information")
        )
        flag.Usage = usage
        flag.Parse()
@@ -64,19 +66,13 @@ func main() {
        }
        nice := uint8(*niceRaw)
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        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)
+               log.Fatalln("Error during initialization:", err)
        }
        if ctx.Self == nil {
                log.Fatalln("Config lacks private keys")
        }
-       ctx.Quiet = *quiet
-       ctx.Debug = *debug
 
        var nodeOnly *nncp.Node
        if *nodeRaw != "" {
@@ -92,7 +88,7 @@ Cycle:
                if nodeOnly != nil && nodeId != *nodeOnly.Id {
                        continue
                }
-               isBad = ctx.Toss(node.Id, nice, *dryRun)
+               isBad = ctx.Toss(node.Id, nice, *dryRun, *doSeen)
        }
        if *cycle > 0 {
                time.Sleep(time.Duration(*cycle) * time.Second)
index bcd76257bf0581a0fd2abf026893014411861104..ffd1c0c9ae930ede4cc6d8bcd2dcac81fdd5e861 100644 (file)
@@ -43,17 +43,19 @@ func usage() {
 
 func main() {
        var (
-               cfgPath  = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               nodeRaw  = flag.String("node", "", "Process only that node")
-               niceRaw  = flag.Int("nice", 255, "Minimal required niceness")
-               rxOnly   = flag.Bool("rx", false, "Only receive packets")
-               txOnly   = flag.Bool("tx", false, "Only transfer packets")
-               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")
-               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")
+               nodeRaw   = flag.String("node", "", "Process only that node")
+               niceRaw   = flag.Int("nice", 255, "Minimal required niceness")
+               rxOnly    = flag.Bool("rx", false, "Only receive packets")
+               txOnly    = flag.Bool("tx", false, "Only transfer packets")
+               mkdir     = flag.Bool("mkdir", false, "Create necessary outbound directories")
+               keep      = flag.Bool("keep", false, "Do not delete transferred packets")
+               spoolPath = flag.String("spool", "", "Override path to spool")
+               logPath   = flag.String("log", "", "Override path to logfile")
+               quiet     = flag.Bool("quiet", false, "Print only errors")
+               debug     = flag.Bool("debug", false, "Print debug messages")
+               version   = flag.Bool("version", false, "Print version information")
+               warranty  = flag.Bool("warranty", false, "Print warranty information")
        )
        flag.Usage = usage
        flag.Parse()
@@ -77,16 +79,10 @@ func main() {
                log.Fatalln("-rx and -tx can not be set simultaneously")
        }
 
-       cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath))
+       ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        if err != nil {
-               log.Fatalln("Can not read config:", err)
+               log.Fatalln("Error during initialization:", 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 != "" {
@@ -174,7 +170,7 @@ func main() {
                        }
                        var pktEnc nncp.PktEnc
                        _, err = xdr.Unmarshal(fd, &pktEnc)
-                       if err != nil || pktEnc.Magic != nncp.MagicNNCPEv1 {
+                       if err != nil || pktEnc.Magic != nncp.MagicNNCPEv2 {
                                ctx.LogD("nncp-xfer", sds, "is not a packet")
                                fd.Close()
                                continue
@@ -285,6 +281,16 @@ Tx:
                                job.Fd.Close()
                                continue
                        }
+                       if _, err = os.Stat(filepath.Join(dstPath, pktName)); err == nil || !os.IsNotExist(err) {
+                               ctx.LogD("nncp-xfer", sds, "already exists")
+                               job.Fd.Close()
+                               continue
+                       }
+                       if _, err = os.Stat(filepath.Join(dstPath, pktName+nncp.SeenSuffix)); err == nil || !os.IsNotExist(err) {
+                               ctx.LogD("nncp-xfer", sds, "already exists")
+                               job.Fd.Close()
+                               continue
+                       }
                        tmp, err := ioutil.TempFile(dstPath, "nncp-xfer")
                        if err != nil {
                                ctx.LogE("nncp-xfer", nncp.SdsAdd(sds, nncp.SDS{"err": err}), "mktemp")
index 21b3d6fd8e8efe76e146b02e1d47af38d0df675a..8e550fb94e8c1bb3d18d166e9bf1da1c37e0cbb4 100644 (file)
@@ -20,6 +20,7 @@ package nncp
 
 import (
        "errors"
+       "io/ioutil"
        "os"
        "path/filepath"
 )
@@ -68,3 +69,37 @@ func (ctx *Ctx) ensureRxDir(nodeId *NodeId) error {
        fd.Close()
        return nil
 }
+
+func CtxFromCmdline(cfgPath, spoolPath, logPath string, quiet, debug bool) (*Ctx, error) {
+       env := os.Getenv(CfgPathEnv)
+       if env != "" {
+               cfgPath = env
+       }
+       cfgRaw, err := ioutil.ReadFile(cfgPath)
+       if err != nil {
+               return nil, err
+       }
+       ctx, err := CfgParse(cfgRaw)
+       if err != nil {
+               return nil, err
+       }
+       if spoolPath == "" {
+               env = os.Getenv(CfgSpoolEnv)
+               if env != "" {
+                       ctx.Spool = env
+               }
+       } else {
+               ctx.Spool = spoolPath
+       }
+       if logPath == "" {
+               env = os.Getenv(CfgLogEnv)
+               if env != "" {
+                       ctx.LogPath = env
+               }
+       } else {
+               ctx.LogPath = logPath
+       }
+       ctx.Quiet = quiet
+       ctx.Debug = debug
+       return ctx, nil
+}
index 7abfcab0aec30113aa18d4f5a91fdef533037d4e..4a79fcd9e6facae990d490bdd55ba8e761c1d3d5 100644 (file)
@@ -150,6 +150,27 @@ func (ctx *Ctx) Humanize(s string) string {
                if err, exists := sds["err"]; exists {
                        msg += ": " + err
                }
+       case "nncp-bundle":
+               switch sds["xx"] {
+               case "rx":
+                       msg = "Bundle transfer, received from"
+               case "tx":
+                       msg = "Bundle transfer, sent to"
+               default:
+                       return s
+               }
+               if nodeS != "" {
+                       msg += " node " + nodeS
+               }
+               msg += " " + sds["pkt"]
+               if size != "" {
+                       msg += fmt.Sprintf(" (%s)", size)
+               }
+               if err, exists := sds["err"]; exists {
+                       msg += ": " + err
+               }
+       case "nncp-rm":
+               msg += "removing " + sds["file"]
        case "call-start":
                msg = fmt.Sprintf("Connected to %s", nodeS)
        case "call-finish":
index 1f3afd1e4f93534347b1826b73e28a62c48c16e1..4a59db02d5f60874230c549fea2df61bacef8e18 100644 (file)
@@ -64,7 +64,7 @@ func (ctx *Ctx) Jobs(nodeId *NodeId, xx TRxTx) chan Job {
                                continue
                        }
                        var pktEnc PktEnc
-                       if _, err = xdr.Unmarshal(fd, &pktEnc); err != nil || pktEnc.Magic != MagicNNCPEv1 {
+                       if _, err = xdr.Unmarshal(fd, &pktEnc); err != nil || pktEnc.Magic != MagicNNCPEv2 {
                                fd.Close()
                                continue
                        }
index b35f83e3ee969f6fdaad031ae3972b83b8cf04bb..aa7368d96036e7387423ae83e7ee8762875e18cd 100644 (file)
@@ -49,11 +49,13 @@ const (
        DefaultNiceMail = 64
        DefaultNiceFreq = 64
        DefaultNiceFile = 196
+
+       NNCPBundlePrefix = "NNCP"
 )
 
 var (
        MagicNNCPPv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'P', 0, 0, 1}
-       MagicNNCPEv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 1}
+       MagicNNCPEv2 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'E', 0, 0, 2}
        BadMagic     error   = errors.New("Unknown magic number")
        BadPktType   error   = errors.New("Unknown packet type")
 
@@ -71,17 +73,18 @@ type Pkt struct {
 type PktTbs struct {
        Magic     [8]byte
        Nice      uint8
-       Recipient *NodeId
        Sender    *NodeId
+       Recipient *NodeId
        ExchPub   *[32]byte
 }
 
 type PktEnc struct {
-       Magic   [8]byte
-       Nice    uint8
-       Sender  *NodeId
-       ExchPub *[32]byte
-       Sign    *[ed25519.SignatureSize]byte
+       Magic     [8]byte
+       Nice      uint8
+       Sender    *NodeId
+       Recipient *NodeId
+       ExchPub   *[32]byte
+       Sign      *[ed25519.SignatureSize]byte
 }
 
 func init() {
@@ -102,11 +105,12 @@ func init() {
                panic(err)
        }
        pktEnc := PktEnc{
-               Magic:   MagicNNCPEv1,
-               Nice:    123,
-               Sender:  dummyId,
-               ExchPub: new([32]byte),
-               Sign:    new([ed25519.SignatureSize]byte),
+               Magic:     MagicNNCPEv2,
+               Nice:      123,
+               Sender:    dummyId,
+               Recipient: dummyId,
+               ExchPub:   new([32]byte),
+               Sign:      new([ed25519.SignatureSize]byte),
        }
        n, err = xdr.Marshal(&buf, pktEnc)
        if err != nil {
@@ -157,10 +161,10 @@ func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize
                return err
        }
        tbs := PktTbs{
-               Magic:     MagicNNCPEv1,
+               Magic:     MagicNNCPEv2,
                Nice:      nice,
-               Recipient: their.Id,
                Sender:    our.Id,
+               Recipient: their.Id,
                ExchPub:   pubEph,
        }
        var tbsBuf bytes.Buffer
@@ -170,18 +174,19 @@ func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize
        signature := new([ed25519.SignatureSize]byte)
        copy(signature[:], ed25519.Sign(our.SignPrv, tbsBuf.Bytes()))
        pktEnc := PktEnc{
-               Magic:   MagicNNCPEv1,
-               Nice:    nice,
-               Sender:  our.Id,
-               ExchPub: pubEph,
-               Sign:    signature,
+               Magic:     MagicNNCPEv2,
+               Nice:      nice,
+               Sender:    our.Id,
+               Recipient: their.Id,
+               ExchPub:   pubEph,
+               Sign:      signature,
        }
        if _, err = xdr.Marshal(out, &pktEnc); err != nil {
                return err
        }
        sharedKey := new([32]byte)
        curve25519.ScalarMult(sharedKey, prvEph, their.ExchPub)
-       kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv1[:])
+       kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv2[:])
 
        keyEnc := make([]byte, 32)
        if _, err = io.ReadFull(kdf, keyEnc); err != nil {
@@ -257,10 +262,10 @@ func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize
 
 func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
        tbs := PktTbs{
-               Magic:     MagicNNCPEv1,
+               Magic:     MagicNNCPEv2,
                Nice:      pktEnc.Nice,
-               Recipient: our.Id,
                Sender:    their.Id,
+               Recipient: our.Id,
                ExchPub:   pktEnc.ExchPub,
        }
        var tbsBuf bytes.Buffer
@@ -276,13 +281,16 @@ func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Wri
        if err != nil {
                return nil, 0, err
        }
-       if pktEnc.Magic != MagicNNCPEv1 {
+       if pktEnc.Magic != MagicNNCPEv2 {
                return nil, 0, BadMagic
        }
        their, known := nodes[*pktEnc.Sender]
        if !known {
                return nil, 0, errors.New("Unknown sender")
        }
+       if *pktEnc.Recipient != *our.Id {
+               return nil, 0, errors.New("Invalid recipient")
+       }
        verified, err := TbsVerify(our, their, &pktEnc)
        if err != nil {
                return nil, 0, err
@@ -292,7 +300,7 @@ func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Wri
        }
        sharedKey := new([32]byte)
        curve25519.ScalarMult(sharedKey, our.ExchPrv, pktEnc.ExchPub)
-       kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv1[:])
+       kdf := hkdf.New(blake256, sharedKey[:], nil, MagicNNCPEv2[:])
 
        keyEnc := make([]byte, 32)
        if _, err = io.ReadFull(kdf, keyEnc); err != nil {
index be845109752a79b2393dfdb8be20b716c5584621..6daab72c643c4eb3f7df571c0c205da48abdf74d 100644 (file)
@@ -55,6 +55,8 @@ var (
                noise.CipherChaChaPoly,
                noise.HashBLAKE2b,
        )
+
+       spWorkersGroup sync.WaitGroup
 )
 
 type SPType uint8
@@ -838,6 +840,8 @@ func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
                                continue
                        }
                        state.RUnlock()
+                       spWorkersGroup.Wait()
+                       spWorkersGroup.Add(1)
                        go func() {
                                if err := fd.Sync(); err != nil {
                                        state.ctx.LogE("sp-file", SdsAdd(sdsp, SDS{"err": err}), "sync")
@@ -859,6 +863,7 @@ func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
                                state.Lock()
                                delete(state.infosTheir, *file.Hash)
                                state.Unlock()
+                               spWorkersGroup.Done()
                                go func() {
                                        state.payloads <- MarshalSP(SPTypeDone, SPDone{file.Hash})
                                }()
index 2ce02cb80dae79a81afa388e5ab27b01c91ce517..47a20d0a6e85623ee5774bc6d0f135a325aed119 100644 (file)
@@ -45,7 +45,7 @@ func (ctx *Ctx) NewTmpFile() (*os.File, error) {
 type TmpFileWHash struct {
        W   *bufio.Writer
        Fd  *os.File
-       hsh hash.Hash
+       Hsh hash.Hash
        ctx *Ctx
 }
 
@@ -61,7 +61,7 @@ func (ctx *Ctx) NewTmpFileWHash() (*TmpFileWHash, error) {
        return &TmpFileWHash{
                W:   bufio.NewWriter(io.MultiWriter(hsh, tmp)),
                Fd:  tmp,
-               hsh: hsh,
+               Hsh: hsh,
                ctx: ctx,
        }, nil
 }
@@ -81,9 +81,12 @@ func (tmp *TmpFileWHash) Commit(dir string) error {
                tmp.Fd.Close()
                return err
        }
-       tmp.Fd.Sync()
+       if err = tmp.Fd.Sync(); err != nil {
+               tmp.Fd.Close()
+               return err
+       }
        tmp.Fd.Close()
-       checksum := ToBase32(tmp.hsh.Sum(nil))
+       checksum := ToBase32(tmp.Hsh.Sum(nil))
        tmp.ctx.LogD("tmp", SDS{"src": tmp.Fd.Name(), "dst": checksum}, "commit")
        return os.Rename(tmp.Fd.Name(), filepath.Join(dir, checksum))
 }
index 962b240152626e4f9299cb4a9584e47b2797677d..7777ecd50e5c2c213ecf0d829758b713a5ba2af0 100644 (file)
@@ -38,6 +38,10 @@ import (
        "golang.org/x/crypto/blake2b"
 )
 
+const (
+       SeenSuffix = ".seen"
+)
+
 func newNotification(fromTo *FromToYAML, subject string) io.Reader {
        return strings.NewReader(fmt.Sprintf(
                "From: %s\nTo: %s\nSubject: %s\n",
@@ -47,7 +51,7 @@ func newNotification(fromTo *FromToYAML, subject string) io.Reader {
        ))
 }
 
-func (ctx *Ctx) Toss(nodeId *NodeId, nice uint8, dryRun bool) bool {
+func (ctx *Ctx) Toss(nodeId *NodeId, nice uint8, dryRun, doSeen bool) bool {
        isBad := false
        for job := range ctx.Jobs(nodeId, TRx) {
                pktName := filepath.Base(job.Fd.Name())
@@ -113,7 +117,7 @@ func (ctx *Ctx) Toss(nodeId *NodeId, nice uint8, dryRun bool) bool {
                                                strings.Split(recipients, " ")...,
                                        )...,
                                )
-                               cmd.Env = append(cmd.Env, "NNCP_SENDER=" + sender.Id.String())
+                               cmd.Env = append(cmd.Env, "NNCP_SENDER="+sender.Id.String())
                                cmd.Stdin = decompressor
                                if err = cmd.Run(); err != nil {
                                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "sendmail")
@@ -123,6 +127,11 @@ func (ctx *Ctx) Toss(nodeId *NodeId, nice uint8, dryRun bool) bool {
                        }
                        ctx.LogI("rx", sds, "")
                        if !dryRun {
+                               if doSeen {
+                                       if fd, err := os.Create(job.Fd.Name() + SeenSuffix); err == nil {
+                                               fd.Close()
+                                       }
+                               }
                                if err = os.Remove(job.Fd.Name()); err != nil {
                                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "remove")
                                        isBad = true
@@ -189,6 +198,11 @@ func (ctx *Ctx) Toss(nodeId *NodeId, nice uint8, dryRun bool) bool {
                        }
                        ctx.LogI("rx", sds, "")
                        if !dryRun {
+                               if doSeen {
+                                       if fd, err := os.Create(job.Fd.Name() + SeenSuffix); err == nil {
+                                               fd.Close()
+                                       }
+                               }
                                if err = os.Remove(job.Fd.Name()); err != nil {
                                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "remove")
                                        isBad = true
@@ -258,6 +272,11 @@ func (ctx *Ctx) Toss(nodeId *NodeId, nice uint8, dryRun bool) bool {
                        }
                        ctx.LogI("rx", sds, "")
                        if !dryRun {
+                               if doSeen {
+                                       if fd, err := os.Create(job.Fd.Name() + SeenSuffix); err == nil {
+                                               fd.Close()
+                                       }
+                               }
                                if err = os.Remove(job.Fd.Name()); err != nil {
                                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "remove")
                                        isBad = true
@@ -297,6 +316,11 @@ func (ctx *Ctx) Toss(nodeId *NodeId, nice uint8, dryRun bool) bool {
                        }
                        ctx.LogI("rx", sds, "")
                        if !dryRun {
+                               if doSeen {
+                                       if fd, err := os.Create(job.Fd.Name() + SeenSuffix); err == nil {
+                                               fd.Close()
+                                       }
+                               }
                                if err = os.Remove(job.Fd.Name()); err != nil {
                                        ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "remove")
                                        isBad = true
index c40c9d8628779ffe5b5855f0922b05575d930c3c..47a4ba48a0e5b18ada946cbb250c35520e4bbe73 100644 (file)
@@ -105,12 +105,12 @@ func TestTossEmail(t *testing.T) {
                        if len(dirFiles(rxPath)) == 0 {
                                continue
                        }
-                       ctx.Toss(ctx.Self.Id, DefaultNiceMail-1, false)
+                       ctx.Toss(ctx.Self.Id, DefaultNiceMail-1, false, false)
                        if len(dirFiles(rxPath)) == 0 {
                                return false
                        }
                        ctx.Neigh[*nodeOur.Id].Sendmail = []string{"/bin/sh", "-c", "false"}
-                       ctx.Toss(ctx.Self.Id, DefaultNiceMail, false)
+                       ctx.Toss(ctx.Self.Id, DefaultNiceMail, false, false)
                        if len(dirFiles(rxPath)) == 0 {
                                return false
                        }
@@ -118,7 +118,7 @@ func TestTossEmail(t *testing.T) {
                                "/bin/sh", "-c",
                                fmt.Sprintf("cat >> %s", filepath.Join(spool, "mbox")),
                        }
-                       ctx.Toss(ctx.Self.Id, DefaultNiceMail, false)
+                       ctx.Toss(ctx.Self.Id, DefaultNiceMail, false, false)
                        if len(dirFiles(rxPath)) != 0 {
                                return false
                        }
@@ -190,12 +190,12 @@ func TestTossFile(t *testing.T) {
                }
                rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
                os.Rename(filepath.Join(spool, ctx.Self.Id.String(), string(TTx)), rxPath)
-               ctx.Toss(ctx.Self.Id, DefaultNiceFile, false)
+               ctx.Toss(ctx.Self.Id, DefaultNiceFile, false, false)
                if len(dirFiles(rxPath)) == 0 {
                        return false
                }
                ctx.Neigh[*nodeOur.Id].Incoming = &incomingPath
-               ctx.Toss(ctx.Self.Id, DefaultNiceFile, false)
+               ctx.Toss(ctx.Self.Id, DefaultNiceFile, false, false)
                if len(dirFiles(rxPath)) != 0 {
                        return false
                }
@@ -262,7 +262,7 @@ func TestTossFileSameName(t *testing.T) {
                rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
                os.Rename(filepath.Join(spool, ctx.Self.Id.String(), string(TTx)), rxPath)
                ctx.Neigh[*nodeOur.Id].Incoming = &incomingPath
-               ctx.Toss(ctx.Self.Id, DefaultNiceFile, false)
+               ctx.Toss(ctx.Self.Id, DefaultNiceFile, false, false)
                expected := make(map[string]struct{})
                expected["samefile"] = struct{}{}
                for i := 0; i < files-1; i++ {
@@ -330,12 +330,12 @@ func TestTossFreq(t *testing.T) {
                txPath := filepath.Join(spool, ctx.Self.Id.String(), string(TTx))
                os.Rename(txPath, rxPath)
                os.MkdirAll(txPath, os.FileMode(0700))
-               ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false)
+               ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false, false)
                if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 {
                        return false
                }
                ctx.Neigh[*nodeOur.Id].Freq = &spool
-               ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false)
+               ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false, false)
                if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 {
                        return false
                }
@@ -348,7 +348,7 @@ func TestTossFreq(t *testing.T) {
                                panic(err)
                        }
                }
-               ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false)
+               ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false, false)
                if len(dirFiles(txPath)) == 0 || len(dirFiles(rxPath)) != 0 {
                        return false
                }
@@ -441,7 +441,7 @@ func TestTossTrns(t *testing.T) {
                                panic(err)
                        }
                }
-               ctx.Toss(ctx.Self.Id, 123, false)
+               ctx.Toss(ctx.Self.Id, 123, false, false)
                if len(dirFiles(rxPath)) != 0 {
                        return false
                }
index 79e699ccd02f240a1f1fbbdcee7e64c1c12e41aa..bb3d318650d48840a39aa21a027c6630e198e626 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 79e699ccd02f240a1f1fbbdcee7e64c1c12e41aa
+Subproject commit bb3d318650d48840a39aa21a027c6630e198e626
index 9419663f5a44be8b34ca85f08abc5fe1be11f8a3..9f005a07e0d31d45e6656d241bb5c0f2efd4bc94 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 9419663f5a44be8b34ca85f08abc5fe1be11f8a3
+Subproject commit 9f005a07e0d31d45e6656d241bb5c0f2efd4bc94
index a04bdaca5b32abe1c069418fb7088ae607de5bd0..9dfe39835686865bff950a07b394c12a98ddc811 160000 (submodule)
@@ -1 +1 @@
-Subproject commit a04bdaca5b32abe1c069418fb7088ae607de5bd0
+Subproject commit 9dfe39835686865bff950a07b394c12a98ddc811
index ebfc5b4631820b793c9010c87fd8fef0f39eb082..0dd5e194bbf5eb84a39666eb4c98a4d007e4203a 160000 (submodule)
@@ -1 +1 @@
-Subproject commit ebfc5b4631820b793c9010c87fd8fef0f39eb082
+Subproject commit 0dd5e194bbf5eb84a39666eb4c98a4d007e4203a
index cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b..287cf08546ab5e7e37d55a84f7ed3fd1db036de5 160000 (submodule)
@@ -1 +1 @@
-Subproject commit cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b
+Subproject commit 287cf08546ab5e7e37d55a84f7ed3fd1db036de5