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
поведения.
Поддержка из коробки offline флоппинета, тайников для сброса информации
-(dead drop) и компьютеров с "воздушным зазором" (air-gap). Но также
+(dead drop), последовательных и не перезаписываемых CD-ROM/ленточных
+хранилищ, компьютеров с "воздушным зазором" (air-gap). Но также
существует и online TCP демон с полнодуплексной возобновляемой передачей
данных.
-X cypherpunks.ru/nncp.DefaultLogPath=$(LOGPATH)
ALL = \
+ nncp-bundle \
nncp-call \
nncp-caller \
nncp-cfgenc \
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
@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 демон с
полнодуплексной возобновляемой передачей данных.
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}!
--- /dev/null
+@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.
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
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
@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
@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
@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
@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.
дополнительный уровень шифрования и аутентификации для безопасного
обмена данными.
- FTN сложен в настройке, потому-что это совершенно другой мир
+ FTN сложен в настройке, потому что это совершенно другой мир
программного обеспечения, по-сравнению с Unix-ом. Даже редактор
почты будет какой-нибудь GoldEd, а не обычный почтовый клиент. Более
того, из коробки не предоставляется никакого шифрования и сильной
@item Передача новостей
SMTP ничего не знает о новостях, NNTP и тому подобному. NNCP тоже не
- знает, потому-что на текущий день они уже мало используются.
+ знает, потому что на текущий день они уже мало используются.
@item Передача файлов
SMTP может передавать файлы только в Base64 кодировке -- это очень
@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}
* Commands::
* Niceness::
* Chunked files: Chunked.
+* Bundles::
* Spool directory: Spool.
* Log format: Log.
* Packet format: Packet.
@include cmds.texi
@include niceness.texi
@include chunked.texi
+@include bundles.texi
@include spool.texi
@include log.texi
@include pkt.texi
@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
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
@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
@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
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
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
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.
* Ненадёжный/дорогой канал связи: UsecaseUnreliableRU.
* Медленная/дорогая связь для больших объёмов данных, плохой QoS: UsecaseQoSRU.
* Экстремальные наземные окружающие условия, нет связи: UsecaseNoLinkRU.
+* Односторонняя широковещательная связь: UsecaseBroadcastRU.
* Частные, изолированные MitM/Sybil-устойчивые сети: UsecaseF2FRU.
* Высоко защищённые изолированные компьютеры с воздушным зазором: UsecaseAirgapRU.
* Обход сетевой цензуры, здоровье: UsecaseCensorRU.
давая возможность передачи, по сути, любых объёмов используя накопители
небольших размеров.
+Вы также можете использовать CD-ROM и ленточные накопители:
+
+@verbatim
+% nncp-bundle -tx bob | cdrecord -tao -
+% nncp-bundle -tx bob | dd of=/dev/sa0 bs=10240
+@end verbatim
+
@node UsecaseNoLinkRU
@subsection Экстремальные наземные окружающие условия, нет связи
дальнейшей обработки. @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-устойчивые сети
необходимо запустить другую фазу: @ref{nncp-toss, распаковку}, которая
использует ваши приватные криптографические ключи. То есть, даже если вы
потеряете свой компьютер, устройства хранения и тому прочее -- это не
-так плохо, потому-что вы не носите с собой приватные ключи (ведь так?),
+так плохо, потому что вы не носите с собой приватные ключи (ведь так?),
вы не "распаковываете" эти пакеты сразу же на том же самом устройстве.
Распаковка (чтение этих зашифрованных пакетов с извлечением переданных
файлов и почтовых сообщений) может и должна бы быть произведена на
* 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.
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
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
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 ------------------------
поведения.
Поддержка из коробки offline флоппинета, тайников для сброса информации
-(dead drop) и компьютеров с "воздушным зазором" (air-gap). Но также
+(dead drop), последовательных и только-для-записи CD-ROM/ленточных
+хранилищ, компьютеров с "воздушным зазором" (air-gap). Но также
существует и online TCP демон с полнодуплексной возобновляемой передачей
данных.
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 \
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/
)
const (
- CfgPathEnv = "NNCPCFG"
+ CfgPathEnv = "NNCPCFG"
+ CfgSpoolEnv = "NNCPSPOOL"
+ CfgLogEnv = "NNCPLOG"
)
var (
}
return &ctx, nil
}
-
-func CfgPathFromEnv(cmdlineFlag *string) (p string) {
- p = os.Getenv(CfgPathEnv)
- if p == "" {
- p = *cmdlineFlag
- }
- return
-}
--- /dev/null
+/*
+NNCP -- Node to Node copy, utilities for store-and-forward data exchange
+Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+// 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),
+ }), "")
+ }
+ }
+}
import (
"flag"
"fmt"
- "io/ioutil"
"log"
"os"
"strings"
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")
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])
import (
"flag"
"fmt"
- "io/ioutil"
"log"
"os"
"strconv"
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()
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 {
import (
"flag"
"fmt"
- "io/ioutil"
"log"
"os"
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{
import (
"flag"
"fmt"
- "io/ioutil"
"log"
"os"
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()
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 != "" {
import (
"flag"
"fmt"
- "io/ioutil"
"log"
"net"
"os"
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()
}
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 {
import (
"flag"
"fmt"
- "io/ioutil"
"log"
"os"
"strings"
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")
}
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 {
import (
"flag"
"fmt"
- "io/ioutil"
"log"
"os"
"path/filepath"
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()
}
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 {
"bufio"
"flag"
"fmt"
- "io/ioutil"
"log"
"os"
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")
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)
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()
}
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 {
"flag"
"fmt"
"io"
- "io/ioutil"
"log"
"os"
}
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")
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
}
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()
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 != "" {
import (
"flag"
"fmt"
- "io/ioutil"
"log"
"os"
"path/filepath"
+ "strings"
"cypherpunks.ru/nncp"
)
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()
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)
+ }
}
}
import (
"flag"
"fmt"
- "io/ioutil"
"log"
"os"
"sort"
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()
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 != "" {
import (
"flag"
"fmt"
- "io/ioutil"
"log"
"os"
"time"
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()
}
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 != "" {
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)
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()
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 != "" {
}
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
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")
import (
"errors"
+ "io/ioutil"
"os"
"path/filepath"
)
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
+}
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":
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
}
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")
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() {
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 {
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
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 {
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
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
}
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 {
noise.CipherChaChaPoly,
noise.HashBLAKE2b,
)
+
+ spWorkersGroup sync.WaitGroup
)
type SPType uint8
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")
state.Lock()
delete(state.infosTheir, *file.Hash)
state.Unlock()
+ spWorkersGroup.Done()
go func() {
state.payloads <- MarshalSP(SPTypeDone, SPDone{file.Hash})
}()
type TmpFileWHash struct {
W *bufio.Writer
Fd *os.File
- hsh hash.Hash
+ Hsh hash.Hash
ctx *Ctx
}
return &TmpFileWHash{
W: bufio.NewWriter(io.MultiWriter(hsh, tmp)),
Fd: tmp,
- hsh: hsh,
+ Hsh: hsh,
ctx: ctx,
}, 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))
}
"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",
))
}
-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())
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")
}
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
}
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
}
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
}
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
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
}
"/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
}
}
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
}
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++ {
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
}
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
}
panic(err)
}
}
- ctx.Toss(ctx.Self.Id, 123, false)
+ ctx.Toss(ctx.Self.Id, 123, false, false)
if len(dirFiles(rxPath)) != 0 {
return false
}
-Subproject commit 79e699ccd02f240a1f1fbbdcee7e64c1c12e41aa
+Subproject commit bb3d318650d48840a39aa21a027c6630e198e626
-Subproject commit 9419663f5a44be8b34ca85f08abc5fe1be11f8a3
+Subproject commit 9f005a07e0d31d45e6656d241bb5c0f2efd4bc94
-Subproject commit a04bdaca5b32abe1c069418fb7088ae607de5bd0
+Subproject commit 9dfe39835686865bff950a07b394c12a98ddc811
-Subproject commit ebfc5b4631820b793c9010c87fd8fef0f39eb082
+Subproject commit 0dd5e194bbf5eb84a39666eb4c98a4d007e4203a
-Subproject commit cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b
+Subproject commit 287cf08546ab5e7e37d55a84f7ed3fd1db036de5