README | 6 +++--- README.RU | 3 ++- VERSION | 2 +- common.mk | 4 ++++ doc/about.ru.texi | 5 ++++- doc/about.texi | 9 ++++++--- doc/bundles.texi | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/cmds.texi | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++--- doc/comparison.ru.texi | 4 ++-- doc/download.texi | 4 ++++ doc/index.texi | 2 ++ doc/news.ru.texi | 26 ++++++++++++++++++++++++++ doc/news.texi | 26 ++++++++++++++++++++++++++ doc/pkt.texi | 27 +++++++++++---------------- doc/spool.texi | 6 ++++++ doc/usecases.ru.texi | 31 ++++++++++++++++++++++++++++++- doc/usecases.texi | 27 +++++++++++++++++++++++++++ makedist.sh | 9 +++++---- ports/nncp/Makefile | 3 ++- ports/nncp/pkg-descr | 6 +++--- src/cypherpunks.ru/nncp/cfg.go | 12 +++--------- src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go | 361 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/cypherpunks.ru/nncp/cmd/nncp-call/main.go | 29 ++++++++++++----------------- src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go | 23 +++++++++-------------- src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go | 9 ++------- src/cypherpunks.ru/nncp/cmd/nncp-check/main.go | 25 ++++++++++--------------- src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go | 29 ++++++++++++----------------- src/cypherpunks.ru/nncp/cmd/nncp-file/main.go | 13 ++++--------- src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go | 27 +++++++++++---------------- src/cypherpunks.ru/nncp/cmd/nncp-log/main.go | 10 +++------- src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go | 26 +++++++++++--------------- src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go | 15 +++++---------- src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go | 34 +++++++++++++++------------------- src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go | 136 +++++++++++++++++++++++++++++++++++++++-------------- src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go | 21 ++++++++------------- src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go | 34 +++++++++++++++------------------- src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go | 46 ++++++++++++++++++++++++++-------------------- src/cypherpunks.ru/nncp/ctx.go | 35 +++++++++++++++++++++++++++++++++++ src/cypherpunks.ru/nncp/humanizer.go | 21 +++++++++++++++++++++ src/cypherpunks.ru/nncp/jobs.go | 2 +- src/cypherpunks.ru/nncp/pkt.go | 56 ++++++++++++++++++++++++++++++----------------------- src/cypherpunks.ru/nncp/sp.go | 5 +++++ src/cypherpunks.ru/nncp/tmp.go | 11 +++++++---- src/cypherpunks.ru/nncp/toss.go | 28 ++++++++++++++++++++++++++-- src/cypherpunks.ru/nncp/toss_test.go | 20 ++++++++++---------- diff --git a/README b/README index 9ddd3e96d2ff2d5545236790801d51412b3ecb5e9d54d758d12b7cb3934abedf..9549f8ef84eb836c6245fdbad9ba0efaa6b6a2e64e0ad8d8370fa540e3b4c11c 100644 --- 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 diff --git a/README.RU b/README.RU index c17c5ed06d28831dced7b0be356f35461825ab142f8318286f7fec2f196f43b0..dbac4f4b423dae9ed8258a134e4cddead590348edbb51d1225675659d2d57325 100644 --- a/README.RU +++ b/README.RU @@ -13,7 +13,8 @@ клиента и сервера, может использовать как push, так и poll модель поведения. Поддержка из коробки offline флоппинета, тайников для сброса информации -(dead drop) и компьютеров с "воздушным зазором" (air-gap). Но также +(dead drop), последовательных и не перезаписываемых CD-ROM/ленточных +хранилищ, компьютеров с "воздушным зазором" (air-gap). Но также существует и online TCP демон с полнодуплексной возобновляемой передачей данных. diff --git a/VERSION b/VERSION index 1fc2c3042a1c37ef05fabbbcdd5189b558aea8c8c73b124e00026c96ff597b80..e224e77bf7ecd5b287819d199ce8a75dba225909a19a0679dd966dba7d61fa70 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.12 +1.0 diff --git a/common.mk b/common.mk index 1f56c08ce283d4d6eb4c553d79ba3b6bd5c7a82c91c56eed40cce475a6efef98..666e78e9dce735d045105e86f9b524a8e49157d896f17f1a95e7142a3d623b98 100644 --- a/common.mk +++ b/common.mk @@ -17,6 +17,7 @@ -X cypherpunks.ru/nncp.DefaultSpoolPath=$(SPOOLPATH) \ -X cypherpunks.ru/nncp.DefaultLogPath=$(LOGPATH) ALL = \ + nncp-bundle \ nncp-call \ nncp-caller \ nncp-cfgenc \ @@ -36,6 +37,9 @@ nncp-toss \ nncp-xfer all: $(ALL) + +nncp-bundle: + GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-bundle nncp-call: GOPATH=$(GOPATH) go build -ldflags "$(LDFLAGS)" cypherpunks.ru/nncp/cmd/nncp-call diff --git a/doc/about.ru.texi b/doc/about.ru.texi index 5c3c4294e7f485b82632aaa6bdb45e456d90b69965a608ac3b61b260df9f3183..018bcedf7f2fcb2fc9c2af6e11bcd8b47156e852f897b06715b3d1dc880689c8 100644 --- a/doc/about.ru.texi +++ b/doc/about.ru.texi @@ -22,7 +22,10 @@ Поддержка из коробки offline @url{https://ru.wikipedia.org/wiki/%D0%A4%D0%BB%D0%BE%D0%BF%D0%BF%D0%B8%D0%BD%D0%B5%D1%82, флоппинета}, @url{https://ru.wikipedia.org/wiki/%D0%A2%D0%B0%D0%B9%D0%BD%D0%B8%D0%BA, -тайников} для сброса информации (dead drop) и компьютеров с +тайников} для сброса информации (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 демон с полнодуплексной возобновляемой передачей данных. diff --git a/doc/about.texi b/doc/about.texi index df307fb80d590ba410d88584e120498715fbef80f366fc2c16dbd5434cfeb5d2..331f4328b117041700811a9958d28e04df96a9381f7d6a032511d90c2631075f 100644 --- a/doc/about.texi +++ b/doc/about.texi @@ -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 index 0000000000000000000000000000000000000000..e00c3326914cfd6bac78eac251a844789880432059bc17d9e935158683426a36 --- /dev/null +++ b/doc/bundles.texi @@ -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. diff --git a/doc/cmds.texi b/doc/cmds.texi index 02f3680828170178cdda5ab576c0d22a3a857596ee4b4cf2895982139e168eb2..31480106e85400bb6b085a1f8900f381f799a3320f94bd63963eee52a6744e4d 100644 --- a/doc/cmds.texi +++ b/doc/cmds.texi @@ -20,6 +20,12 @@ Set desired outgoing packet @ref{Niceness, niceness level}. 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 @@ -29,6 +35,53 @@ Print version information. @item -warranty 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 @@ @node nncp-rm @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 @@ @node nncp-toss @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 @@ -389,6 +461,11 @@ @option{-cycle} option tells not to quit, but to repeat tossing every @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 @@ @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. diff --git a/doc/comparison.ru.texi b/doc/comparison.ru.texi index 61c9cca45160a1cf1d79173955376d869d34c8c95411431413301d2a37d0eb81..48a06e7d1be07cf9f1730d962d0d88037207e844c93c1ae6d9177f1fa2456af4 100644 --- a/doc/comparison.ru.texi +++ b/doc/comparison.ru.texi @@ -42,7 +42,7 @@ нескольких конфигурационных файлах. Но вы вынуждены добавить дополнительный уровень шифрования и аутентификации для безопасного обмена данными. - FTN сложен в настройке, потому-что это совершенно другой мир + FTN сложен в настройке, потому что это совершенно другой мир программного обеспечения, по-сравнению с Unix-ом. Даже редактор почты будет какой-нибудь GoldEd, а не обычный почтовый клиент. Более того, из коробки не предоставляется никакого шифрования и сильной @@ -53,7 +53,7 @@ конфигурационного файла}. @item Передача новостей SMTP ничего не знает о новостях, NNTP и тому подобному. NNCP тоже не - знает, потому-что на текущий день они уже мало используются. + знает, потому что на текущий день они уже мало используются. @item Передача файлов SMTP может передавать файлы только в Base64 кодировке -- это очень diff --git a/doc/download.texi b/doc/download.texi index 529a1c4d6e578f0898cc7aca166d53e89c2a144a998708c9f96b3201805a7ef5..665b2e3563eb9abd225b66f9848717d18474a9c133f3fb86e130a258ab1145a8 100644 --- a/doc/download.texi +++ b/doc/download.texi @@ -23,6 +23,10 @@ @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} diff --git a/doc/index.texi b/doc/index.texi index 2da192df8b2dee68a15c0d3c45626d50a5b345b613a4641d5308a4b54822d159..41eff81f08d5700f513ff92f83736205e4c8e6fa038afad50612040653c2d4d7 100644 --- a/doc/index.texi +++ b/doc/index.texi @@ -37,6 +37,7 @@ * Integration:: * Commands:: * Niceness:: * Chunked files: Chunked. +* Bundles:: * Spool directory: Spool. * Log format: Log. * Packet format: Packet. @@ -59,6 +60,7 @@ @include integration.texi @include cmds.texi @include niceness.texi @include chunked.texi +@include bundles.texi @include spool.texi @include log.texi @include pkt.texi diff --git a/doc/news.ru.texi b/doc/news.ru.texi index ba95e3abc774910bef8faef81da11954b2ca82eb99b89edbf691d32fa4d2e167..32b2d8d158a367f5e70c347cf3291411c36dcc1e03079ef42457507b22b484c7 100644 --- a/doc/news.ru.texi +++ b/doc/news.ru.texi @@ -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 diff --git a/doc/news.texi b/doc/news.texi index db2d62cb8288a584843ab69f8f480c127ef4164ff12781d51a7194597e0f2a2d..2b20115fce2fe44eb9aa8766f442e6b7bf815a30f46b098506a8f831bea5ab8b 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -3,6 +3,32 @@ @unnumbered News 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 diff --git a/doc/pkt.texi b/doc/pkt.texi index 0a2c087c70079acb92af0e53b09a5da4c07504d727f1b2e44b8b523234c14b84..91c984e001434178a9ac8b9b7c56e08e3cf146676c187c068dba397380694069 100644 --- a/doc/pkt.texi +++ b/doc/pkt.texi @@ -70,13 +70,13 @@ @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 @@ 1-255, packet @ref{Niceness, niceness} level @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 @@ 64-byte, fixed length opaque data @tab 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 diff --git a/doc/spool.texi b/doc/spool.texi index 7e203c0101131a70f30799e4769bd23e5bae6b7ad5d75a7046f66bd40151d774..9bf158babe24a5935c7852ca846fb2e2014b41abfd037c798db23e4dbb7e9dae 100644 --- a/doc/spool.texi +++ b/doc/spool.texi @@ -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 @@ -29,6 +31,10 @@ it can be integrity checked at any time. @file{5ZIB...UMKW.part} is 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. diff --git a/doc/usecases.ru.texi b/doc/usecases.ru.texi index 51f5f247cf60dee737d73637fddb069fe0df37c809515f14c643c296db061665..eeb1d687b7e4c7cf18ac8cecc73ea4790bcf681185fc192fae117f35c9dd664d 100644 --- a/doc/usecases.ru.texi +++ b/doc/usecases.ru.texi @@ -7,6 +7,7 @@ * Легковесная и быстрая замена POP3/IMAP4: UsecasePOPRU. * Ненадёжный/дорогой канал связи: UsecaseUnreliableRU. * Медленная/дорогая связь для больших объёмов данных, плохой QoS: UsecaseQoSRU. * Экстремальные наземные окружающие условия, нет связи: UsecaseNoLinkRU. +* Односторонняя широковещательная связь: UsecaseBroadcastRU. * Частные, изолированные MitM/Sybil-устойчивые сети: UsecaseF2FRU. * Высоко защищённые изолированные компьютеры с воздушным зазором: UsecaseAirgapRU. * Обход сетевой цензуры, здоровье: UsecaseCensorRU. @@ -113,6 +114,13 @@ Огромные файлы могут быть разбиты на маленькие @ref{Chunked, части}, давая возможность передачи, по сути, любых объёмов используя накопители небольших размеров. +Вы также можете использовать 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 @@ чтобы найти все пакеты относящиеся к их узлу и локально скопируют для дальнейшей обработки. @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 @@ пакеты. После передачи пакета вы всё-равно не сможете его прочитать: необходимо запустить другую фазу: @ref{nncp-toss, распаковку}, которая использует ваши приватные криптографические ключи. То есть, даже если вы потеряете свой компьютер, устройства хранения и тому прочее -- это не -так плохо, потому-что вы не носите с собой приватные ключи (ведь так?), +так плохо, потому что вы не носите с собой приватные ключи (ведь так?), вы не "распаковываете" эти пакеты сразу же на том же самом устройстве. Распаковка (чтение этих зашифрованных пакетов с извлечением переданных файлов и почтовых сообщений) может и должна бы быть произведена на diff --git a/doc/usecases.texi b/doc/usecases.texi index aefcfb3b6f679158e7d3a645a2d3f6b4bc322fa27830f6031b20aeb40549f8d2..ddb2c3a75e61e606f63dc8facf33d0b66e8a1ed5e5623ef09abedde706085c2a 100644 --- a/doc/usecases.texi +++ b/doc/usecases.texi @@ -9,6 +9,7 @@ * Lightweight fast POP3/IMAP4 replacement: UsecasePOP. * 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 @@ -144,6 +152,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 diff --git a/makedist.sh b/makedist.sh index 75bd97527c791b216c2992a23b21dd7d90a50315a5ed5de78817504194345c51..26b5ef07d4bf01237be008e45387c07c580098d375adbcb15e1b28b99b099d73 100755 --- a/makedist.sh +++ b/makedist.sh @@ -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 @@ клиента и сервера, может использовать как push, так и poll модель поведения. Поддержка из коробки offline флоппинета, тайников для сброса информации -(dead drop) и компьютеров с "воздушным зазором" (air-gap). Но также +(dead drop), последовательных и только-для-записи CD-ROM/ленточных +хранилищ, компьютеров с "воздушным зазором" (air-gap). Но также существует и online TCP демон с полнодуплексной возобновляемой передачей данных. diff --git a/ports/nncp/Makefile b/ports/nncp/Makefile index c34a535d9fddd8de26e21dbd89ea10e4d44eaca1651913d804eb7879c7853108..a559eda21a12c5b43c8fe30148a6a1be0dcfdfd4186e6de6006aa82087d482fe 100644 --- a/ports/nncp/Makefile +++ b/ports/nncp/Makefile @@ -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 \ diff --git a/ports/nncp/pkg-descr b/ports/nncp/pkg-descr index 3797df1df6dc72043fcad956768cd4c66b4765d464a72e51b8f0d82f15650f54..8ae73e08aa2227948ffbfcef32e54b2d05ebe3824b82703ffa7c57a55dc4c21a 100644 --- a/ports/nncp/pkg-descr +++ b/ports/nncp/pkg-descr @@ -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/ diff --git a/src/cypherpunks.ru/nncp/cfg.go b/src/cypherpunks.ru/nncp/cfg.go index 345b8809183e74f5e2a9972941f5c7201976370ae42197ac3fa01e65af736601..d62761b160c12fa276b6f1c7c2a91ef4f024edd4481408f72f38c28c636faa12 100644 --- a/src/cypherpunks.ru/nncp/cfg.go +++ b/src/cypherpunks.ru/nncp/cfg.go @@ -32,7 +32,9 @@ "gopkg.in/yaml.v2" ) const ( - CfgPathEnv = "NNCPCFG" + CfgPathEnv = "NNCPCFG" + CfgSpoolEnv = "NNCPSPOOL" + CfgLogEnv = "NNCPLOG" ) var ( @@ -414,11 +416,3 @@ } } 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 index 0000000000000000000000000000000000000000..72919502e0d73e3c22ccf2e160e5a8c0c79aaf2e284dcd236684fb105e04ac98 --- /dev/null +++ b/src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go @@ -0,0 +1,361 @@ +/* +NNCP -- Node to Node copy, utilities for store-and-forward data exchange +Copyright (C) 2016-2017 Sergey Matveev + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +// 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), + }), "") + } + } +} diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go index 4d85a99c4f357459a3eee80f70a3c8a03c4e9c29d0617b2834acb723889a787c..ae66fe2cddf299582cc5a5284487c1f8df3a02fcee5094bf89fe3f5e4c631ca1 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go @@ -22,7 +22,6 @@ import ( "flag" "fmt" - "io/ioutil" "log" "os" "strings" @@ -40,14 +39,16 @@ } 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 @@ if *rxOnly && *txOnly { log.Fatalln("-rx and -tx can not be set simultaneously") } - cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) - if err != nil { - log.Fatalln("Can not read config:", err) - } - ctx, err := nncp.CfgParse(cfgRaw) + ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug) 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]) diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go index dac1d0944c44611c264a25be1ad5e926c6e08460107b8cb192470dfcfd8b0831..e06bb88b41e6c3885d4ec4cf665b66a4b6455c35f0c336e0d35d3efba20dde4d 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go @@ -22,7 +22,6 @@ import ( "flag" "fmt" - "io/ioutil" "log" "os" "strconv" @@ -42,11 +41,13 @@ } 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 @@ fmt.Println(nncp.VersionGet()) 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 { diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go index 008e21176d6735565c33faddf5b3bcb99af91995ea61754b4ff64d51f3bc066f..57db531e5164460b346ab62e686ee34356a726c6eb491c078a40edf33b02e327 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-cfgmin/main.go @@ -22,7 +22,6 @@ import ( "flag" "fmt" - "io/ioutil" "log" "os" @@ -54,13 +53,9 @@ fmt.Println(nncp.VersionGet()) return } - cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) - if err != nil { - log.Fatalln("Can not read config:", err) - } - ctx, err := nncp.CfgParse(cfgRaw) + ctx, err := nncp.CtxFromCmdline(*cfgPath, "", "", false, false) if err != nil { - log.Fatalln("Can not parse config:", err) + log.Fatalln("Error during initialization:", err) } cfg := nncp.CfgYAML{ diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-check/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-check/main.go index edc2a6483a1cad367d5d46530495ed297b94de309072cb8c0e9e2d5dac7b2cc9..bc2f1fd44f3f25b5a02e25dbc848453f0c73fdbf08abfebe764132fc2c6c5d64 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-check/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-check/main.go @@ -22,7 +22,6 @@ import ( "flag" "fmt" - "io/ioutil" "log" "os" @@ -38,12 +37,14 @@ } 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 @@ fmt.Println(nncp.VersionGet()) return } - cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) - if err != nil { - log.Fatalln("Can not read config:", err) - } - ctx, err := nncp.CfgParse(cfgRaw) + ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug) if err != nil { - log.Fatalln("Can not parse config:", err) + log.Fatalln("Error during initialization:", err) } - ctx.Quiet = *quiet - ctx.Debug = *debug var nodeOnly *nncp.Node if *nodeRaw != "" { diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go index 0687adf6c31815461a625285ee2d0a12d71189f06c942adaff13d6d8d31823a9..cff2acb785c76b4bbed37dcaa14cbedbaf38f599f8bbc2faf1735f57d5f0a7a5 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go @@ -22,7 +22,6 @@ import ( "flag" "fmt" - "io/ioutil" "log" "net" "os" @@ -41,14 +40,16 @@ } 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 @@ log.Fatalln("-nice must be between 1 and 255") } nice := uint8(*niceRaw) - cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) - if err != nil { - log.Fatalln("Can not read config:", err) - } - ctx, err := nncp.CfgParse(cfgRaw) + ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug) 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 { diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go index 27b1bb0a6e38c5c7fc5870f3ae73b4d39cc86ff5264af2467ab696d0775ada31..0764c11ddc15e6fa67537f440d94eb99b47ad3ba28772fed1184dbdb17dbaaaa 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-file/main.go @@ -22,7 +22,6 @@ import ( "flag" "fmt" - "io/ioutil" "log" "os" "strings" @@ -46,6 +45,8 @@ cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") niceRaw = flag.Int("nice", nncp.DefaultNiceFile, "Outbound packet niceness") minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB") chunkSize = flag.Uint64("chunked", 0, "Split file on specified size chunks, in KiB") + 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 @@ log.Fatalln("-nice must be between 1 and 255") } 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 { diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go index 93e1a8798f1b7d37433ee5f61b394965a89f8f666a9871a6bd484bf879b16c6c..7317c25b1320768f382ab1e25fe84445fce734ab93afb69a469f4aabf7398e2d 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go @@ -22,7 +22,6 @@ import ( "flag" "fmt" - "io/ioutil" "log" "os" "path/filepath" @@ -40,13 +39,15 @@ } 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 @@ log.Fatalln("-nice must be between 1 and 255") } nice := uint8(*niceRaw) - cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) - if err != nil { - log.Fatalln("Can not read config:", err) - } - ctx, err := nncp.CfgParse(cfgRaw) + ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug) 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 { diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-log/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-log/main.go index 351f28e5dadd46bf486b9f7a6cf74e531fe40ea8e8aa11d459adaae4d00e9ba6..1f0a66c5f88a8e32af596f89f2bc51bbb4eade61e13c136066ad8d1ba3c5427c 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-log/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-log/main.go @@ -23,7 +23,6 @@ import ( "bufio" "flag" "fmt" - "io/ioutil" "log" "os" @@ -40,6 +39,7 @@ 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 @@ fmt.Println(nncp.VersionGet()) return } - cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) - if err != nil { - log.Fatalln("Can not read config:", err) - } - ctx, err := nncp.CfgParse(cfgRaw) + ctx, err := nncp.CtxFromCmdline(*cfgPath, "", *logPath, false, *debug) if err != nil { - log.Fatalln("Can not parse config:", err) + log.Fatalln("Error during initialization:", err) } fd, err := os.Open(ctx.LogPath) diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go index 3afb88420b4b8874940e154caf7a588d6a56aa107f8d00eb41c1004d7ef203fe..217f947b3034a706bdcbf50d39ba2b91642690fd8d61a55581d98bf06db4566b 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-mail/main.go @@ -40,13 +40,15 @@ } 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 @@ log.Fatalln("-nice must be between 1 and 255") } nice := uint8(*niceRaw) - cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) - if err != nil { - log.Fatalln("Can not read config:", err) - } - ctx, err := nncp.CfgParse(cfgRaw) + ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug) 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 { diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go index b93a29450b0e4f3340709b154e93a6d32322f2aa81e1f4208c976882f63f7e7e..88aa06912f0c6ce9dc63eb24f3e2e4026da33eaedd525a6f3870233454a5aec6 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go @@ -26,7 +26,6 @@ "compress/zlib" "flag" "fmt" "io" - "io/ioutil" "log" "os" @@ -112,15 +111,11 @@ return } 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)) - if err != nil { - log.Fatalln("Can not read config:", err) - } - ctx, err := nncp.CfgParse(cfgRaw) + ctx, err := nncp.CtxFromCmdline(*cfgPath, "", "", false, false) 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 @@ } 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 } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go index 7e0707fc93e8d38b7b4461bd5f02bdd151b521e84ea967fff802252655380cb9..b9b4c912ad63b2b724da5f8e10363232a0dba6c075e1cc2e0c020f9df260e20f 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-reass/main.go @@ -264,17 +264,19 @@ } 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 @@ fmt.Println(nncp.VersionGet()) return } - cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) - if err != nil { - log.Fatalln("Can not read config:", err) - } - ctx, err := nncp.CfgParse(cfgRaw) + ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug) if err != nil { - log.Fatalln("Can not parse config:", err) + log.Fatalln("Error during initialization:", err) } - ctx.Quiet = *quiet - ctx.Debug = *debug var nodeOnly *nncp.Node if *nodeRaw != "" { diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go index b934a404ccea32e37341de07103b601b4cfc2c3812211d330df4a775377f856f..db035785742f1296fd02fbc0bfadc22f38a96b57304e576292fdc4e3570272b9 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-rm/main.go @@ -22,10 +22,10 @@ import ( "flag" "fmt" - "io/ioutil" "log" "os" "path/filepath" + "strings" "cypherpunks.ru/nncp" ) @@ -33,17 +33,32 @@ 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 @@ if *version { 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) + } } } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go index e1cf12a1f78f9bfb1f485d55fb79a31549666dc0ef1d68556b8677b787a597eb..61e190c3cd9a94a6ffe4badeb0da1895b336d2ff9dc9edc88f3cb5de565cdb8d 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go @@ -22,7 +22,6 @@ import ( "flag" "fmt" - "io/ioutil" "log" "os" "sort" @@ -40,11 +39,12 @@ } 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 @@ fmt.Println(nncp.VersionGet()) 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 != "" { diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go index b434cffe1c4d3d8d634edd23815353e3c918d924bde4a645c363be8b604092bb..d0e9beee99789fd483b1882bb1baa87505ed2091112cf95aaef56faa3e6887ed 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go @@ -22,7 +22,6 @@ import ( "flag" "fmt" - "io/ioutil" "log" "os" "time" @@ -39,15 +38,18 @@ } 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 @@ log.Fatalln("-nice must be between 1 and 255") } 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 @@ for nodeId, node := range ctx.Neigh { 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) diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go index c4c475e6f3c4a336a7e2c67c50f5df00b1d3c94d3a74835cb0889989ae2eb03a..13b6ec17e8027839d4cbc2d26629bcdaf61679dc7ecbdb227d3a910025391214 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go @@ -43,17 +43,19 @@ } 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 @@ if *rxOnly && *txOnly { log.Fatalln("-rx and -tx can not be set simultaneously") } - cfgRaw, err := ioutil.ReadFile(nncp.CfgPathFromEnv(cfgPath)) - if err != nil { - log.Fatalln("Can not read config:", err) - } - ctx, err := nncp.CfgParse(cfgRaw) + ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug) if err != nil { - log.Fatalln("Can not parse config:", err) + log.Fatalln("Error during initialization:", err) } - ctx.Quiet = *quiet - ctx.Debug = *debug var nodeOnly *nncp.Node if *nodeRaw != "" { @@ -174,7 +170,7 @@ continue } 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 @@ -282,6 +278,16 @@ pktName := filepath.Base(job.Fd.Name()) sds["pkt"] = pktName if job.PktEnc.Nice > nice { ctx.LogD("nncp-xfer", sds, "too nice") + 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 } diff --git a/src/cypherpunks.ru/nncp/ctx.go b/src/cypherpunks.ru/nncp/ctx.go index 2cead9c2ebddb52f1cc1abfa424ca6d18ed720f155fe98bbd95b48b301e4dc8e..261d22232f84a1ba5795f857b99345fb83b1b5bfb820d074ed0f6e02700bb963 100644 --- a/src/cypherpunks.ru/nncp/ctx.go +++ b/src/cypherpunks.ru/nncp/ctx.go @@ -20,6 +20,7 @@ package nncp import ( "errors" + "io/ioutil" "os" "path/filepath" ) @@ -68,3 +69,37 @@ } 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 +} diff --git a/src/cypherpunks.ru/nncp/humanizer.go b/src/cypherpunks.ru/nncp/humanizer.go index 265ca4d185685d6bea833bf93719cc2645c826e296aa94a941e255ca8e319c7d..18aa7c6eaf541799b44d759b2723995e7b6da45e0d10f42b3dbcba512653e43e 100644 --- a/src/cypherpunks.ru/nncp/humanizer.go +++ b/src/cypherpunks.ru/nncp/humanizer.go @@ -150,6 +150,27 @@ } 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": diff --git a/src/cypherpunks.ru/nncp/jobs.go b/src/cypherpunks.ru/nncp/jobs.go index f93463916445f4585cbca79d9ec42d3705d7b1d0aca6761e4d23b6d9358e9bd5..6b6be55ca5b572ab9a67c3f71afafcf7040bcc75828cf06289499614468099ca 100644 --- a/src/cypherpunks.ru/nncp/jobs.go +++ b/src/cypherpunks.ru/nncp/jobs.go @@ -64,7 +64,7 @@ if err != nil { 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 } diff --git a/src/cypherpunks.ru/nncp/pkt.go b/src/cypherpunks.ru/nncp/pkt.go index 674e197293121416a7f1816a93b8f32fad7bcfc54c991769dd17150683731a11..d6a31adf80d9f54ee2813e41244fe307328b2cd8b57db17c5ed351ff213043ea 100644 --- a/src/cypherpunks.ru/nncp/pkt.go +++ b/src/cypherpunks.ru/nncp/pkt.go @@ -49,11 +49,13 @@ 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 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 @@ if err != nil { 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 @@ if _, err := xdr.Marshal(&pktBuf, pkt); err != nil { 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 @@ } 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 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 @@ _, err := xdr.Unmarshal(data, &pktEnc) 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 @@ return their, 0, errors.New("Invalid signature") } 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 { diff --git a/src/cypherpunks.ru/nncp/sp.go b/src/cypherpunks.ru/nncp/sp.go index cfc5007bae0177257835bdf8011539b507506efdd0121a02e91d18a5d102b8a3..ef08a174a625dfef6d9a18627fc87595adde7d39076e3fe2abb0d846a6bcc76b 100644 --- a/src/cypherpunks.ru/nncp/sp.go +++ b/src/cypherpunks.ru/nncp/sp.go @@ -55,6 +55,8 @@ noise.DH25519, noise.CipherChaChaPoly, noise.HashBLAKE2b, ) + + spWorkersGroup sync.WaitGroup ) type SPType uint8 @@ -838,6 +840,8 @@ fd.Close() 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 @@ os.Rename(filePath+PartSuffix, filePath) state.Lock() delete(state.infosTheir, *file.Hash) state.Unlock() + spWorkersGroup.Done() go func() { state.payloads <- MarshalSP(SPTypeDone, SPDone{file.Hash}) }() diff --git a/src/cypherpunks.ru/nncp/tmp.go b/src/cypherpunks.ru/nncp/tmp.go index 3917f8fe328669d2a5abb4df6c6038e2290539c15d90e800cd0594216baf4d46..ea17a205014c134454c2baa5d1c42a346d2b8b32e6ad28aee9d0201d957df55d 100644 --- a/src/cypherpunks.ru/nncp/tmp.go +++ b/src/cypherpunks.ru/nncp/tmp.go @@ -45,7 +45,7 @@ type TmpFileWHash struct { W *bufio.Writer Fd *os.File - hsh hash.Hash + Hsh hash.Hash ctx *Ctx } @@ -61,7 +61,7 @@ } return &TmpFileWHash{ W: bufio.NewWriter(io.MultiWriter(hsh, tmp)), Fd: tmp, - hsh: hsh, + Hsh: hsh, ctx: ctx, }, nil } @@ -81,9 +81,12 @@ if err = tmp.W.Flush(); err != nil { 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)) } diff --git a/src/cypherpunks.ru/nncp/toss.go b/src/cypherpunks.ru/nncp/toss.go index 70a767da249861ae63d54cb5d3bca0b09fd9cde9f7ffbd53b8c187b54294b4be..cf2735763945d1af9124e284b9c262f33e304d3c653e8c452546cdab81bf8787 100644 --- a/src/cypherpunks.ru/nncp/toss.go +++ b/src/cypherpunks.ru/nncp/toss.go @@ -38,6 +38,10 @@ "github.com/dustin/go-humanize" "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 @@ mime.BEncoding.Encode("UTF-8", subject), )) } -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 @@ sendmail[1:len(sendmail)], 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 @@ } } 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 @@ delete(sds, "tmp") } 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 @@ } } 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 @@ } } 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 diff --git a/src/cypherpunks.ru/nncp/toss_test.go b/src/cypherpunks.ru/nncp/toss_test.go index a96279d17d1e8d52547eece41daa23d71a0fa73fde9622127aae361460a44131..7fe80a1b5e34e94e48b9cdb4f404b4fd73a0af13c55a86636fbab935df9365db 100644 --- a/src/cypherpunks.ru/nncp/toss_test.go +++ b/src/cypherpunks.ru/nncp/toss_test.go @@ -105,12 +105,12 @@ os.Rename(filepath.Join(spool, ctx.Self.Id.String(), string(TTx)), rxPath) 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 @@ ctx.Neigh[*nodeOur.Id].Sendmail = []string{ "/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 @@ } } 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 @@ } 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 @@ rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx)) 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 @@ ); err != nil { 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 @@ ); err != nil { panic(err) } } - ctx.Toss(ctx.Self.Id, 123, false) + ctx.Toss(ctx.Self.Id, 123, false, false) if len(dirFiles(rxPath)) != 0 { return false }