Makefile | 5 +++-- VERSION | 2 +- doc/building.texi | 12 ++++++------ doc/bundles.texi | 4 ++-- doc/cfg.texi | 44 +++++++++++++++++++++++++++++++------------- doc/cmds.texi | 14 +++++++++++++- doc/download.texi | 5 +++++ doc/integrity.texi | 2 +- doc/news.ru.texi | 29 ++++++++++++++++++++++++++++- doc/news.texi | 26 ++++++++++++++++++++++++++ doc/thanks.texi | 2 ++ makedist.sh | 4 ++-- ports/nncp/Makefile | 17 ++++++++--------- ports/nncp/files/pkg-message.in | 8 ++++++-- src/cfg.go | 75 ++++++++++++++++++++++++++++++++--------------------- src/cmd/nncp-bundle/main.go | 2 +- src/cmd/nncp-cfgenc/main.go | 2 +- src/cmd/nncp-cfgnew/main.go | 77 +++++++++++++++++++++++++++++++++-------------------- src/cmd/nncp-file/main.go | 30 ++++++++++-------------------- src/cmd/nncp-freq/main.go | 3 +-- src/cmd/nncp-pkt/main.go | 2 +- src/cmd/nncp-reass/main.go | 2 +- src/cmd/nncp-xfer/main.go | 2 +- src/ctx.go | 4 +++- src/eblob.go | 2 +- src/go.mod | 2 ++ src/jobs.go | 2 +- src/node.go | 13 ++++++++----- src/pkt.go | 2 +- src/pkt_test.go | 2 +- src/sp.go | 2 +- src/toss.go | 94 +++++++++++++++++++++++++++++++---------------------- src/toss_test.go | 8 ++++++-- src/tx.go | 242 ++++++++++++++++++++++++++++++++++++----------------- src/tx_test.go | 2 +- diff --git a/Makefile b/Makefile index a5acd2ad46392083b95127bb51922866ce5679693e78468318d527b963a7a018..7eecd2ca15f15a60680f950c170535a5913142c8d4a26b474ddfee384cf05923 100644 --- a/Makefile +++ b/Makefile @@ -49,9 +49,10 @@ all: $(ALL) $(ALL): mkdir -p $(BIN) - cd $(SRC) ; GOPATH=$(GOPATH) $(GO) build -ldflags "$(LDFLAGS)" \ + cd $(SRC) ; GOPATH=$(GOPATH) $(GO) build \ + -o $(BIN)/$$(basename $@) \ + -ldflags "$(LDFLAGS)" \ $(MOD)/cmd/$$(basename $@) - mv $(SRC)/$$(basename $@) $(BIN) test: cd $(SRC) ; GOPATH=$(GOPATH) $(GO) test -failfast $(MOD)/... diff --git a/VERSION b/VERSION index 66f2a752442fc764e44f203c55ad3ded2f621d3d55dbdc1f1ae3b57f325add6e..1ae196976b7a2ef843d0d2f10e955748756b96f3d2c136472230332a6e3e82ac 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.0.0 +5.1.0 diff --git a/doc/building.texi b/doc/building.texi index 75b3b27d4c07209bd857942cc2e9b6cce590dbce82f6044b2bd6c7b8b332bc4d..041c7bef055d6dc1ee865771b28c8483669334494abcd0cb0db153365731e1c9 100644 --- a/doc/building.texi +++ b/doc/building.texi @@ -11,16 +11,16 @@ @verb{|apt install golang|} @end table @verbatim -$ [fetch|wget] http://www.nncpgo.org/download/nncp-5.0.0.tar.xz -$ [fetch|wget] http://www.nncpgo.org/download/nncp-5.0.0.tar.xz.sig -$ gpg --verify nncp-5.0.0.tar.xz.sig nncp-5.0.0.tar.xz -$ xz --decompress --stdout nncp-5.0.0.tar.xz | tar xf - -$ make -C nncp-5.0.0 all +$ [fetch|wget] http://www.nncpgo.org/download/nncp-5.1.0.tar.xz +$ [fetch|wget] http://www.nncpgo.org/download/nncp-5.1.0.tar.xz.sig +$ gpg --verify nncp-5.1.0.tar.xz.sig nncp-5.1.0.tar.xz +$ xz --decompress --stdout nncp-5.1.0.tar.xz | tar xf - +$ make -C nncp-5.1.0 all @end verbatim There is @command{install} make-target respecting @env{DESTDIR}. It will install binaries and info-documentation: @verbatim -# make -C nncp-5.0.0 install PREFIX=/usr/local +# make -C nncp-5.1.0 install PREFIX=/usr/local @end verbatim diff --git a/doc/bundles.texi b/doc/bundles.texi index be9dacdb96f866e6658ed295ef58a7c16b28d81cc326f389e17421d29bfddee8..ab567157b65c49e1e986413330422cb3a662d50839e51f98de6dc5b60d08cbf0 100644 --- a/doc/bundles.texi +++ b/doc/bundles.texi @@ -48,8 +48,8 @@ can contain mix of various recipients. @end itemize -Technically bundle is valid POSIX.1-2001 (pax) -@url{http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html, tar archive}, +Technically bundle is valid POSIX.1-2001 +@url{https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_01, pax 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/cfg.texi b/doc/cfg.texi index b6ffffa013b87037b626febbc3d0dd55f5acd4f409507b780ffea6ded502ced2..93b4af969a155b63690586055809074e9de149b08c2d10c850323e0a9344006f 100644 --- a/doc/cfg.texi +++ b/doc/cfg.texi @@ -18,6 +18,16 @@ freq: { from: nncp@localhost to: user+freq@example.com } + exec: { + "*.warcer": { + from: nncp@localhost + to: user+warcer@example.com + } + "eve.warcer": { + from: nncp@localhost + to: user+warcer-overriden@example.com + } + } } self: { @@ -67,9 +77,11 @@ sendmail: ["/usr/sbin/sendmail"] warcer: ["/path/to/warcer.sh"] wgeter: ["/path/to/wgeter.sh"] } - freq: "/home/bob/pub" - freqchunked: 1024 - freqminsize: 2048 + freq: { + path: "/home/bob/pub" + chunked: 1024 + minsize: 2048 + } via: ["alice"] rxrate: 10 txrate: 20 @@ -88,11 +100,17 @@ @ref{Shared spool, shared spool directories}. @anchor{CfgNotify} @strong{notify} section contains notification settings for successfully -tossed file and freq packets. Corresponding @strong{from} and +tossed file, freq and exec packets. Corresponding @strong{from} and @strong{to} fields will be substituted in notification email message. -@emph{neigh/self/exec/sendmail} will be used as a local mailer. You can -omit either of those two @emph{from}/@emph{to} sections to omit +@code{neigh.self.exec.sendmail} will be used as a local mailer. You can +omit either of those two @code{from}/@code{to} sections to omit corresponding notifications, or the whole section at once. + +@code{notify.exec} section is a mapping of exec handles and +corresponding @code{from}/@code{to} sections. Each handle has either +@code{NODE.HANDLE} or @code{*.HANDLE} syntax. You can override +notification options for some node with the first type of name. +Handle command's output will be included in notification messages. @strong{self} section contains our node's private keypairs. @strong{exch*} and @strong{sign*} are used during @ref{Encrypted, @@ -104,7 +122,7 @@ always has @strong{self} neighbour that is copy of our node's public data (public keys). It is useful for copy-paste sharing with your friends. Each section's key is a human-readable name of the neighbour. -Except for @emph{id}, @emph{exchpub} and @emph{signpub} each neighbour +Except for @code{id}, @code{exchpub} and @code{signpub} each neighbour node has the following fields: @table @strong @@ -141,24 +159,24 @@ Full path to directory where all file uploads will be saved. May be omitted to forbid file uploading on that node. @anchor{CfgFreq} -@item freq +@item freq.path Full path to directory from where file requests will queue files for transmission. May be omitted to forbid freqing from that node. -@item freqchunked +@item freq.chunked If set, then enable @ref{Chunked, chunked} file transmission during freqing. This is the desired chunk size in KiBs. -@item freqminsize +@item freq.minsize If set, then apply @ref{OptMinSize, -minsize} option during file transmission. @anchor{CfgVia} @item via An array of node identifiers that will be used as a relay to that node. -For example @verb{|[foo,bar]|} means that packet can reach current node -by transitioning through @emph{foo} and then @emph{bar} nodes. May be -omitted if direct connection exists and no relaying is required. +For example @verb{|["foo","bar"]|} means that packet can reach current +node by transitioning through @code{foo} and then @code{bar} nodes. May +be omitted if direct connection exists and no relaying is required. @anchor{CfgAddrs} @item addrs diff --git a/doc/cmds.texi b/doc/cmds.texi index 6af1b37374ad3827037c7c80d922ae8eeabf7382e0f05ae6ec6cdfefb3a57e02..909337877ce967861d6a165e36d86e83b537edc7c249acf1b802be6d2dee052e 100644 --- a/doc/cmds.texi +++ b/doc/cmds.texi @@ -287,6 +287,9 @@ NNCP_NICE=123 \ /usr/sbin/sendmail -t root@localhost @end verbatim +If @ref{CfgNotify, notification} is enabled on the remote side for exec +handles, then it will sent simple letter after successful command +execution with its output in message body. @node nncp-file @section nncp-file @@ -315,6 +318,15 @@ @url{https://cr.yp.to/chacha.html, ChaCha20}-@url{https://en.wikipedia.org/wiki/Poly1305, Poly1305} algorithms. Data is splitted on 128 KiB blocks. Each block is encrypted with increasing nonce counter. +If @file{SRC} points to directory, then +@url{https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_01, pax archive} +will be created on the fly with directory contents and destination +filename @file{.tar} appended. It @strong{won't} contain any entities +metainformation, but modification time with the names. UID/GID are set +to zero. Directories have 777 permissions, files have 666, for being +friendly with @command{umask}. Also each entity will have comment like +@verb{|Autogenerated by NNCP version X.Y.Z built with goXXX|}. + If @option{-chunked} is specified, then source file will be split @ref{Chunked, on chunks}. @option{INT} is the desired chunk size in KiBs. This mode is more CPU hungry. Pay attention that chunk is saved in @@ -334,7 +346,7 @@ $ nncp-freq [options] NODE:SRC [DST] @end verbatim Send file request to @option{NODE}, asking it to send its @file{SRC} -file from @ref{CfgFreq, freq} directory to our node under @file{DST} +file from @ref{CfgFreq, freq.path} directory to our node under @file{DST} filename in our @ref{CfgIncoming, incoming} one. If @file{DST} is not specified, then last element of @file{SRC} will be used. diff --git a/doc/download.texi b/doc/download.texi index b6ca193d0d7813e439bc46381798724edaab368a9f17f4d3825526d55245cbd7..56a825d8c6502521e415f3423d1734506f9b1437b70ff62add85b7f354baf859 100644 --- a/doc/download.texi +++ b/doc/download.texi @@ -23,6 +23,11 @@ @multitable {XXXXX} {XXXX-XX-XX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} @headitem Version @tab Date @tab Size @tab Tarball @tab SHA256 checksum +An entry for documentation: +@item @ref{Release 5.0.0, 5.0.0} @tab 2019-11-15 @tab 1099 KiB +@tab @url{download/nncp-5.0.0.tar.xz, link} @url{download/nncp-5.0.0.tar.xz.sig, sign} +@tab @code{3696D7EE B0783E91 87E5EEF4 EFC35235 10452353 7C51FA4C 9BD3CBEE A22678B3} + @item @ref{Release 4.1, 4.1} @tab 2019-05-01 @tab 1227 KiB @tab @url{download/nncp-4.1.tar.xz, link} @url{download/nncp-4.1.tar.xz.sig, sign} @tab @code{29AEC53D EC914906 D7C47194 0955A32E 2BF470E6 9B8E09D3 AF3B62D8 CC8E541E} diff --git a/doc/integrity.texi b/doc/integrity.texi index 98bf43ae65686b605a0120e6642aa54642962b606d212ec1ce350a60d0dc81ff..b4f9cdf0b814ae6e48588818b4894e8623a6e0f5a7afad7f8e0a1ac60994602c 100644 --- a/doc/integrity.texi +++ b/doc/integrity.texi @@ -31,5 +31,5 @@ @end itemize Then you could verify tarballs signature: @verbatim -$ gpg --verify nncp-5.0.0.tar.xz.sig nncp-5.0.0.tar.xz +$ gpg --verify nncp-5.1.0.tar.xz.sig nncp-5.1.0.tar.xz @end verbatim diff --git a/doc/news.ru.texi b/doc/news.ru.texi index ef35bcd1e844fe0590e26ee406f08719ababf857cdc2c2d2503e006890e27e4b..4ff4c1c967b8a365e1f997854dcfaef15f003506a5c79c1d1355ce16e4b1d76a 100644 --- a/doc/news.ru.texi +++ b/doc/news.ru.texi @@ -1,6 +1,33 @@ @node Новости @section Новости +@node Релиз 5.1.0 +@subsection Релиз 5.1.0 +@itemize + +@item +@command{nncp-file} может отправлять директории, автоматически на лету +создавая pax архив. + +@item +Во время создания исходящих сообщений проверяется наличие свободного +места на файловой системе. + +@item +@option{freq}, @option{freqminsize}, @option{freqchunked} опции +конфигурационного файла заменены на структуру +@option{freq: @{path: ..., minsize: ..., chunked: ...@}}. + +@item +Добавлена @option{freq.maxsize} опция конфигурационного файл, +запрещающая ответ на файловый запрос больше заданного размера. + +@item +Возможность оповещения об успешно выполненных командах (exec) через +@option{notify.exec} опцию конфигурационного файла. + +@end itemize + @node Релиз 5.0.0 @subsection Релиз 5.0.0 @itemize @@ -8,7 +35,7 @@ @item @strong{Несовместимое} изменение формата конфигурационного файла: YAML заменён на Hjson, из-за его гораздо большей простоты, без -заметного потеря функционала и удобства. +заметной потери функционала и удобства. @item @strong{Несовместимое} изменение формата простых пакетов. Работа со diff --git a/doc/news.texi b/doc/news.texi index 32ed92bab71d2de0781f6c9008b1261dbd617dec73cc98964cd33a7335232601..8937fd819ac5226d729e5a4453127d07da303087b75d3911dd89def8fd34a0f4 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -3,6 +3,32 @@ @unnumbered News See also this page @ref{Новости, on russian}. +@node Release 5.1.0 +@section Release 5.1.0 +@itemize + +@item +@command{nncp-file} can send directories, automatically creating pax +archive on the fly. + +@item +Free disk space is checked during outbound packets creation. + +@item +@option{freq}, @option{freqminsize}, @option{freqchunked} configuration +file options replaced with the structure: +@option{freq: @{path: ..., minsize: ..., chunked: ...@}}. + +@item +Added @option{freq.maxsize} configuration file option, forbidding of +freq sending larger than specified size. + +@item +Ability to notify about successfully executed commands (exec) with +@option{notify.exec} configuration file option. + +@end itemize + @node Release 5.0.0 @section Release 5.0.0 @itemize diff --git a/doc/thanks.texi b/doc/thanks.texi index 4b86dcec116c1f3e231afedc681195673366d761af18f1c8539e16ec3bb08aac..97df6ebdde19b707fcded898e423b7f6bb04fa042386495527204e262575ce78 100644 --- a/doc/thanks.texi +++ b/doc/thanks.texi @@ -6,4 +6,6 @@ @itemize @item Shawn K. Quinn for his descriptive instructions about building NNCP under Ubuntu GNU/Linux distributions and bug reports. +@item @url{mailto:jgoerzen@@complete.org, John Goerzen} for his feature +suggestions and Debian package maintenance. @end itemize diff --git a/makedist.sh b/makedist.sh index eb4f571c68e2e882b1731bf07a7fcc8793777c120adea506c7fb70feea124499..6ea75c23101225ca596a5a1edb2914003646e86c2554d502a007b132492e786c 100755 --- a/makedist.sh +++ b/makedist.sh @@ -226,7 +226,7 @@ ------------------------ >8 ------------------------ The main improvements for that release are: -$(git cat-file -p $release | sed -n '6,/^.*BEGIN/p' | sed '$d') +$(git cat-file -p v$release | sed -n '6,/^.*BEGIN/p' | sed '$d') ------------------------ >8 ------------------------ @@ -274,7 +274,7 @@ ------------------------ >8 ------------------------ Основные усовершенствования в этом релизе: -$(git cat-file -p $release | sed -n '6,/^.*BEGIN/p' | sed '$d') +$(git cat-file -p v$release | sed -n '6,/^.*BEGIN/p' | sed '$d') ------------------------ >8 ------------------------ diff --git a/ports/nncp/Makefile b/ports/nncp/Makefile index dbecb4d91eff97c269448747a16f3856c45944a7fa06f41b5262a54a44c29815..663a22333ab916287e6e90c08a1f28f31a43028a1519d9c798886fcbceabd32a 100644 --- a/ports/nncp/Makefile +++ b/ports/nncp/Makefile @@ -1,8 +1,7 @@ -# $FreeBSD: head/net/nncp/Makefile 484628 2018-11-10 18:12:57Z bapt $ +# $FreeBSD: head/net/nncp/Makefile 517819 2019-11-17 11:51:56Z dmgk $ PORTNAME= nncp -DISTVERSION= 5.0.0 -PORTREVISION= 1 +DISTVERSION= 5.1.0 CATEGORIES= net MASTER_SITES= http://www.nncpgo.org/download/ @@ -12,19 +11,19 @@ LICENSE= GPLv3 LICENSE_FILE= ${WRKSRC}/COPYING -BUILD_DEPENDS= go:lang/go +USES= go:no_targets tar:xz -USES= tar:xz USE_RC_SUBR= nncp-caller nncp-daemon nncp-toss + +MAKE_ARGS= INFODIR=${STAGEDIR}${PREFIX}/${INFO_PATH} INSTALL_TARGET= install-strip SUB_FILES= pkg-message pkg-install pkg-deinstall -OPTIONS_DEFINE= DOCS - -PORTDOCS= AUTHORS NEWS NEWS.RU README README.RU THANKS INFO= nncp -MAKE_ARGS= INFODIR=${STAGEDIR}${PREFIX}/${INFO_PATH} +PORTDOCS= AUTHORS NEWS NEWS.RU README README.RU THANKS + +OPTIONS_DEFINE= DOCS post-install: ${INSTALL_DATA} ${FILESDIR}/nncp.newsyslog.conf.sample ${STAGEDIR}${PREFIX}/etc/nncp.conf.sample diff --git a/ports/nncp/files/pkg-message.in b/ports/nncp/files/pkg-message.in index b6134a135b431c40c1643122a22cece00067a4ac3c46b23eb438598bf011c93a..288f4bffae981c616d8331609e1df0bfbb082856642c2df5a7e9dc243c2bcf48 100644 --- a/ports/nncp/files/pkg-message.in +++ b/ports/nncp/files/pkg-message.in @@ -1,4 +1,6 @@ -====================================================================== +[ +{ type: install + message: < %%PREFIX%%/etc/nncp.hjson -====================================================================== +EOM +} +] diff --git a/src/cfg.go b/src/cfg.go index 1cc050c612692403038dfa4b5d0e359881cc75e007cfb79b5e6ad08c078bcaf1..df1673f9c6eb1bcc8b215910ed2a2dbe853a5f8dd0810a5e3c52e717493ae4ff 100644 --- a/src/cfg.go +++ b/src/cfg.go @@ -46,17 +46,15 @@ DefaultLogPath string = "/var/spool/nncp/log" ) type NodeJSON struct { - Id string `json:"id"` - ExchPub string `json:"exchpub"` - SignPub string `json:"signpub"` - NoisePub *string `json:"noisepub,omitempty"` - Exec map[string][]string `json:"exec,omitempty"` - Incoming *string `json:"incoming,omitempty"` - Freq *string `json:"freq,omitempty"` - FreqChunked *uint64 `json:"freqchunked,omitempty"` - FreqMinSize *uint64 `json:"freqminsize,omitempty"` - Via []string `json:"via,omitempty"` - Calls []CallJSON `json:"calls,omitempty"` + Id string `json:"id"` + ExchPub string `json:"exchpub"` + SignPub string `json:"signpub"` + NoisePub *string `json:"noisepub,omitempty"` + Exec map[string][]string `json:"exec,omitempty"` + Incoming *string `json:"incoming,omitempty"` + Freq *NodeFreqJSON `json:"freq,omitempty"` + Via []string `json:"via,omitempty"` + Calls []CallJSON `json:"calls,omitempty"` Addrs map[string]string `json:"addrs,omitempty"` @@ -64,6 +62,13 @@ RxRate *int `json:"rxrate,omitempty"` TxRate *int `json:"txrate,omitempty"` OnlineDeadline *uint `json:"onlinedeadline,omitempty"` MaxOnlineTime *uint `json:"maxonlinetime,omitempty"` +} + +type NodeFreqJSON struct { + Path *string `json:"path,omitempty"` + Chunked *uint64 `json:"chunked,omitempty"` + MinSize *uint64 `json:"minsize,omitempty"` + MaxSize *uint64 `json:"maxsize,omitempty"` } type CallJSON struct { @@ -93,8 +98,9 @@ To string } type NotifyJSON struct { - File *FromToJSON `json:"file,omitempty"` - Freq *FromToJSON `json:"freq,omitempty"` + File *FromToJSON `json:"file,omitempty"` + Freq *FromToJSON `json:"freq,omitempty"` + Exec map[string]*FromToJSON `json:"exec,omitempty"` } type CfgJSON struct { @@ -150,24 +156,31 @@ } incoming = &inc } - var freq *string + var freqPath *string + freqChunked := int64(MaxFileSize) + var freqMinSize int64 + freqMaxSize := int64(MaxFileSize) if yml.Freq != nil { - fr := path.Clean(*yml.Freq) - if !path.IsAbs(fr) { - return nil, errors.New("Freq path must be absolute") + f := yml.Freq + if f.Path != nil { + fPath := path.Clean(*f.Path) + if !path.IsAbs(fPath) { + return nil, errors.New("freq.path path must be absolute") + } + freqPath = &fPath + } + if f.Chunked != nil { + if *f.Chunked == 0 { + return nil, errors.New("freq.chunked value must be greater than zero") + } + freqChunked = int64(*f.Chunked) * 1024 + } + if f.MinSize != nil { + freqMinSize = int64(*f.MinSize) * 1024 } - freq = &fr - } - var freqChunked int64 - if yml.FreqChunked != nil { - if *yml.FreqChunked == 0 { - return nil, errors.New("freqchunked value must be greater than zero") + if f.MaxSize != nil { + freqMaxSize = int64(*f.MaxSize) * 1024 } - freqChunked = int64(*yml.FreqChunked) * 1024 - } - var freqMinSize int64 - if yml.FreqMinSize != nil { - freqMinSize = int64(*yml.FreqMinSize) * 1024 } defRxRate := 0 @@ -268,9 +281,10 @@ ExchPub: new([32]byte), SignPub: ed25519.PublicKey(signPub), Exec: yml.Exec, Incoming: incoming, - Freq: freq, + FreqPath: freqPath, FreqChunked: freqChunked, FreqMinSize: freqMinSize, + FreqMaxSize: freqMaxSize, Calls: calls, Addrs: yml.Addrs, RxRate: defRxRate, @@ -423,6 +437,9 @@ ctx.NotifyFile = cfgJSON.Notify.File } if cfgJSON.Notify.Freq != nil { ctx.NotifyFreq = cfgJSON.Notify.Freq + } + if cfgJSON.Notify.Exec != nil { + ctx.NotifyExec = cfgJSON.Notify.Exec } } vias := make(map[NodeId][]string) diff --git a/src/cmd/nncp-bundle/main.go b/src/cmd/nncp-bundle/main.go index e311277785af3066b6dda32c7eda736f1ec38f3c849ce45b866c9fd7503dc35e..342b949983604c15998168153691add6b56a78437938b10e1ac7d9b2f0d8bf5c 100644 --- a/src/cmd/nncp-bundle/main.go +++ b/src/cmd/nncp-bundle/main.go @@ -32,7 +32,7 @@ "path/filepath" "strconv" "strings" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "go.cypherpunks.ru/nncp/v5" "golang.org/x/crypto/blake2b" ) diff --git a/src/cmd/nncp-cfgenc/main.go b/src/cmd/nncp-cfgenc/main.go index 6e3c0c063eee6a55d2339fa865ec7add1279858796ba3682d78550742d4c5639..ea46d32fbbc8d6660d5058702887ffc2b0fd1428d8e5c0b506db12d0e8cd37f6 100644 --- a/src/cmd/nncp-cfgenc/main.go +++ b/src/cmd/nncp-cfgenc/main.go @@ -27,7 +27,7 @@ "io/ioutil" "log" "os" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "go.cypherpunks.ru/nncp/v5" "golang.org/x/crypto/blake2b" "golang.org/x/crypto/ssh/terminal" diff --git a/src/cmd/nncp-cfgnew/main.go b/src/cmd/nncp-cfgnew/main.go index a53163416ac236f04094dae494d454a70c90ac490c94b7c5cc5109f28de1b333..7a5f3c627c2b958a44e46b8a880420e1f92084f891c187bfc7a713236dfc7656 100644 --- a/src/cmd/nncp-cfgnew/main.go +++ b/src/cmd/nncp-cfgnew/main.go @@ -113,6 +113,19 @@ # freq: { # from: nncp@localhost # to: user+freq@example.com # } + # # Send some exec commands execution notifications + # exec: { + # # bob neighbour's "somehandle" notification + # bob.somehandle: { + # from: nncp+bob@localhost + # to: user+somehandle@example.com + # } + # # Any neighboor's "anotherhandle" + # *.anotherhandle: { + # from: nncp@localhost + # to: user+anotherhandle@example.com + # } + # } # } self: { @@ -148,50 +161,55 @@ # signpub: T4AFC...N2FRQ # noisepub: UBM5K...VI42A # # # He is allowed to send email - # exec: {sendmail: ["/usr/sbin/sendmail"]} + # # exec: {sendmail: ["%s"]} # # # Allow incoming files saving in that directory - # incoming: "/home/alice/incoming" + # # incoming: "/home/alice/incoming" # # # Transitional nodes path - # via: ["bob", "eve"] + # # via: ["bob", "eve"] # # # Inactivity timeout when session with remote peer should be terminated - # onlinedeadline: 1800 + # # onlinedeadline: 1800 # # # Maximal online session lifetime - # maxonlinetime: 3600 + # # maxonlinetime: 3600 # - # # Allow freqing from that directory - # freq: "/home/bob/pub" - # # Send freqed files with chunks - # freqchunked: 1024 - # # Send freqed files with minumal chunk size - # freqminsize: 2048 + # # If neither freq section, nor freq.path exist, then no freqing allowed + # # freq: { + # # # Allow freqing from that directory + # # path: "/home/bob/pub" + # # # Send freqed files with chunks + # # # chunked: 1024 + # # # Send freqed files with minumal chunk size + # # # minsize: 2048 + # # # Maximal allowable freqing file size + # # # maxsize: 4096 + # # } # # # Set maximal packets per second receive and transmit rates - # rxrate: 10 - # txrate: 20 + # # rxrate: 10 + # # txrate: 20 # # # Address aliases - # addrs: { - # lan: "[fe80::1234%%igb0]:5400" - # internet: alice.com:3389 - # } + # # addrs: { + # # lan: "[fe80::1234%%igb0]:5400" + # # internet: alice.com:3389 + # # } # # # Calls configuration - # calls: [ - # { - # cron: "*/2 * * * *" - # onlinedeadline: 1800 - # maxonlinetime: 1750 - # nice: PRIORITY+10 - # rxrate: 10 - # txrate: 20 - # xx: rx - # addr: lan - # }, - # ] + # # calls: [ + # # { + # # cron: "*/2 * * * *" + # # onlinedeadline: 1800 + # # maxonlinetime: 1750 + # # nice: PRIORITY+10 + # # rxrate: 10 + # # txrate: 20 + # # xx: rx + # # addr: lan + # # }, + # # ] # } } }`, @@ -208,6 +226,7 @@ nodeOur.Id.String(), nncp.ToBase32(nodeOur.ExchPub[:]), nncp.ToBase32(nodeOur.SignPub[:]), nncp.ToBase32(nodeOur.NoisePub[:]), + nncp.DefaultSendmailPath, nncp.DefaultSendmailPath, ) } diff --git a/src/cmd/nncp-file/main.go b/src/cmd/nncp-file/main.go index 1d82d8cfc6456e6f28d7167d65c029494a40e224c5ade12b37d54b828ddb9df7..0874098c333d2056097793b47658f7c45f6f56d0cb1d7503485a5f998ca5559f 100644 --- a/src/cmd/nncp-file/main.go +++ b/src/cmd/nncp-file/main.go @@ -36,7 +36,7 @@ flag.PrintDefaults() fmt.Fprint(os.Stderr, ` If SRC equals to -, then read data from stdin to temporary file. --minsize/-chunked take NODE's FreqMinSize/FreqChunked configuration +-minsize/-chunked take NODE's freq.minsize/freq.chunked configuration options by default. You can forcefully turn them off by specifying 0 value. `) } @@ -109,25 +109,15 @@ } else if *argChunkSize > 0 { chunkSize = *argChunkSize * 1024 } - if chunkSize == 0 { - err = ctx.TxFile( - node, - nice, - flag.Arg(0), - splitted[1], - minSize, - ) - } else { - err = ctx.TxFileChunked( - node, - nice, - flag.Arg(0), - splitted[1], - minSize, - chunkSize, - ) - } - if err != nil { + if err = ctx.TxFile( + node, + nice, + flag.Arg(0), + splitted[1], + chunkSize, + minSize, + nncp.MaxFileSize, + ); err != nil { log.Fatalln(err) } } diff --git a/src/cmd/nncp-freq/main.go b/src/cmd/nncp-freq/main.go index a28e0a1dfe332b54b8cd059a2f82b3bd0ab7a237dd7f5338b414923ce70caac0..4cc3e7dfd5a7a1066109b9f6d26d7f5f05c57ac362a1044b6059d2a13ebd96d7 100644 --- a/src/cmd/nncp-freq/main.go +++ b/src/cmd/nncp-freq/main.go @@ -24,7 +24,6 @@ "fmt" "log" "os" "path/filepath" - "strconv" "strings" "go.cypherpunks.ru/nncp/v5" @@ -41,7 +40,7 @@ func main() { var ( cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") niceRaw = flag.String("nice", nncp.NicenessFmt(nncp.DefaultNiceFreq), "Outbound packet niceness") - replyNiceRaw = flag.String("replynice", strconv.Itoa(nncp.DefaultNiceFile), "Reply file packet niceness") + replyNiceRaw = flag.String("replynice", nncp.NicenessFmt(nncp.DefaultNiceFile), "Reply file packet niceness") minSize = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB") viaOverride = flag.String("via", "", "Override Via path to destination node") spoolPath = flag.String("spool", "", "Override path to spool") diff --git a/src/cmd/nncp-pkt/main.go b/src/cmd/nncp-pkt/main.go index 5f3977ee00cbc014500bd89ab451f3267a5c2f8f0ed2e436e84bfb99c8845938..a234ad1785b37301f164b00ea0d3a0213effa9dcf9435961ea1e1946519ad274 100644 --- a/src/cmd/nncp-pkt/main.go +++ b/src/cmd/nncp-pkt/main.go @@ -27,7 +27,7 @@ "io" "log" "os" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "github.com/klauspost/compress/zstd" "go.cypherpunks.ru/nncp/v5" ) diff --git a/src/cmd/nncp-reass/main.go b/src/cmd/nncp-reass/main.go index 1e59113c2b47306e75c52909fab751dc17c7dbadac2805f96623167790e3e246..14a98b9f0398c6d0bac513c3017dbb32f1b5cefaf9b10a020273440d43c01b99 100644 --- a/src/cmd/nncp-reass/main.go +++ b/src/cmd/nncp-reass/main.go @@ -32,7 +32,7 @@ "path/filepath" "strconv" "strings" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "github.com/dustin/go-humanize" "go.cypherpunks.ru/nncp/v5" "golang.org/x/crypto/blake2b" diff --git a/src/cmd/nncp-xfer/main.go b/src/cmd/nncp-xfer/main.go index 499364921cdeb4b73722d20d7f615ed721fb6803bd29fe627a5ceaf4a34affea..6de52b8dcc6e7a5fdd9fb526fb16b6e1d3e09e18e6486d5f1312d151aa725885 100644 --- a/src/cmd/nncp-xfer/main.go +++ b/src/cmd/nncp-xfer/main.go @@ -28,7 +28,7 @@ "os" "path/filepath" "strconv" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "go.cypherpunks.ru/nncp/v5" ) diff --git a/src/ctx.go b/src/ctx.go index b0501f7ccd1e512ac0df297f87e412ca4d3c1d53b75f494cdc98f7f413361e36..24eea3d848415224c3d1a46b538fc72b089ba551423a95600957bbceed37dd72 100644 --- a/src/ctx.go +++ b/src/ctx.go @@ -24,8 +24,9 @@ "log" "os" "path/filepath" + "syscall" + "golang.org/x/sys/unix" - "syscall" ) type Ctx struct { @@ -41,6 +42,7 @@ Quiet bool Debug bool NotifyFile *FromToJSON NotifyFreq *FromToJSON + NotifyExec map[string]*FromToJSON } func (ctx *Ctx) FindNode(id string) (*Node, error) { diff --git a/src/eblob.go b/src/eblob.go index e9fdc9e1f1b8444229ba1bac9964b0f74ec27ef260db895149bf3c5eb2f13658..b43b7307953f6c53d2ee25c423cc5da23ffa1d14acd5c1ec095b0aec1e100507 100644 --- a/src/eblob.go +++ b/src/eblob.go @@ -22,7 +22,7 @@ "bytes" "crypto/rand" "hash" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "go.cypherpunks.ru/balloon" "golang.org/x/crypto/blake2b" "golang.org/x/crypto/chacha20poly1305" diff --git a/src/go.mod b/src/go.mod index 637148a836bea72d5f3ed0d2e4903ffec057bbdeb4fe986a7a1f653d863e9920..249a7a9b50a50f568f77db630eca411afb5471b39b351f7c2a38aa694ea33f9c 100644 --- a/src/go.mod +++ b/src/go.mod @@ -15,3 +15,5 @@ golang.org/x/net v0.0.0-20191112182307-2180aed22343 golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) + +go 1.10 diff --git a/src/jobs.go b/src/jobs.go index b0b5c76c927e9f15101edc4e157ead7a7f615b9f6310f09f3cddb7371787aa64..e5bb4cbd66e96d5b6011284d734bc50c1b2f96c8fc96beef7a2001f601010359 100644 --- a/src/jobs.go +++ b/src/jobs.go @@ -23,7 +23,7 @@ "os" "path/filepath" "strconv" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" ) type TRxTx string diff --git a/src/node.go b/src/node.go index 9817303c9000601473bc10058933b06fd3591e9514bb84501f1f23db82260c89..e8107dd3692beee714ca1fa15fa175202fb2661dcab2405a209030a00bd40ef6 100644 --- a/src/node.go +++ b/src/node.go @@ -42,9 +42,10 @@ SignPub ed25519.PublicKey NoisePub *[32]byte Exec map[string][]string Incoming *string - Freq *string + FreqPath *string FreqChunked int64 FreqMinSize int64 + FreqMaxSize int64 Via []*NodeId Addrs map[string]string RxRate int @@ -100,10 +101,12 @@ } func (nodeOur *NodeOur) Their() *Node { return &Node{ - Name: "self", - Id: nodeOur.Id, - ExchPub: nodeOur.ExchPub, - SignPub: nodeOur.SignPub, + Name: "self", + Id: nodeOur.Id, + ExchPub: nodeOur.ExchPub, + SignPub: nodeOur.SignPub, + FreqChunked: MaxFileSize, + FreqMaxSize: MaxFileSize, } } diff --git a/src/pkt.go b/src/pkt.go index f268c5a608708850766034711ec60dfb15a1a8b4f6c61158c458e9ee72da1828..972952cd010cf4da95da2f7043073957800ceaa396a37e5de8b4796e8ef838f1 100644 --- a/src/pkt.go +++ b/src/pkt.go @@ -25,7 +25,7 @@ "encoding/binary" "errors" "io" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "golang.org/x/crypto/blake2b" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/curve25519" diff --git a/src/pkt_test.go b/src/pkt_test.go index 42d3a01aa6bc21f285aafb024e1f0174ee3e2ddc2f54951c2abd47474f15c8ea..3aaf662529e5d46409ed91ba2933aa6c44df32bc7e65624920ff198628c1ecb3 100644 --- a/src/pkt_test.go +++ b/src/pkt_test.go @@ -22,7 +22,7 @@ "bytes" "testing" "testing/quick" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" ) func TestPktEncWrite(t *testing.T) { diff --git a/src/sp.go b/src/sp.go index 899d5c6268a60e5c71b9c2fa6ca38811afcfbfa4565a87e68b2586e21d2a0d5d..b901a24a679105c9c36fcba976457c3034deb6ec126d5a9cf6e8151775a29ce6 100644 --- a/src/sp.go +++ b/src/sp.go @@ -30,7 +30,7 @@ "strconv" "sync" "time" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "github.com/flynn/noise" ) diff --git a/src/toss.go b/src/toss.go index 2f312920a4d4ac3dd26bbe3a91328135ff18b72fd7b51ac00bd16dc2f7fe8415..e91dd5b5403978f9a2a55c03b0967b82ca8a7a16c831ba1fb53576d8a391614c 100644 --- a/src/toss.go +++ b/src/toss.go @@ -20,6 +20,7 @@ import ( "bufio" "bytes" + "encoding/base64" "fmt" "io" "io/ioutil" @@ -32,7 +33,7 @@ "path/filepath" "strconv" "strings" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "github.com/dustin/go-humanize" "github.com/klauspost/compress/zstd" "golang.org/x/crypto/blake2b" @@ -43,13 +44,22 @@ const ( SeenSuffix = ".seen" ) -func newNotification(fromTo *FromToJSON, subject string) io.Reader { - return strings.NewReader(fmt.Sprintf( - "From: %s\nTo: %s\nSubject: %s\n", - fromTo.From, - fromTo.To, - mime.BEncoding.Encode("UTF-8", subject), - )) +func newNotification(fromTo *FromToJSON, subject string, body []byte) io.Reader { + lines := []string{ + "From: " + fromTo.From, + "To: " + fromTo.To, + "Subject: " + mime.BEncoding.Encode("UTF-8", subject), + } + if len(body) > 0 { + lines = append(lines, []string{ + "MIME-Version: 1.0", + "Content-Type: text/plain; charset=utf-8", + "Content-Transfer-Encoding: base64", + "", + base64.StdEncoding.EncodeToString(body), + }...) + } + return strings.NewReader(strings.Join(lines, "\n")) } func (ctx *Ctx) Toss( @@ -58,6 +68,7 @@ nice uint8, dryRun, doSeen, noFile, noFreq, noExec, noTrns bool, ) bool { isBad := false + sendmail := ctx.Neigh[*ctx.SelfId].Exec["sendmail"] decompressor, err := zstd.NewReader(nil) if err != nil { panic(err) @@ -118,9 +129,10 @@ args := make([]string, 0, len(path)-1) for _, p := range path[1:] { args = append(args, string(p)) } + argsStr := strings.Join(append([]string{handle}, args...), " ") sds := SdsAdd(sds, SDS{ "type": "exec", - "dst": strings.Join(append([]string{handle}, args...), " "), + "dst": argsStr, }) sender := ctx.Neigh[*job.PktEnc.Sender] cmdline, exists := sender.Exec[handle] @@ -144,11 +156,28 @@ "NNCP_SENDER="+sender.Id.String(), "NNCP_NICE="+strconv.Itoa(int(pkt.Nice)), ) cmd.Stdin = decompressor - if err = cmd.Run(); err != nil { + output, err := cmd.Output() + if err != nil { ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "handle") isBad = true goto Closing } + if len(sendmail) > 0 && ctx.NotifyExec != nil { + notify, exists := ctx.NotifyExec[sender.Name+"."+handle] + if !exists { + notify, exists = ctx.NotifyExec["*."+handle] + } + if exists { + cmd := exec.Command( + sendmail[0], + append(sendmail[1:len(sendmail)], notify.To)..., + ) + cmd.Stdin = newNotification(notify, fmt.Sprintf( + "Exec from %s: %s", sender.Name, argsStr, + ), output) + cmd.Run() + } + } } ctx.LogI("rx", sds, "") if !dryRun { @@ -245,8 +274,7 @@ if err = os.Remove(job.Fd.Name()); err != nil { ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "remove") isBad = true } - sendmail, exists := ctx.Neigh[*ctx.SelfId].Exec["sendmail"] - if exists && len(sendmail) > 0 && ctx.NotifyFile != nil { + if len(sendmail) > 0 && ctx.NotifyFile != nil { cmd := exec.Command( sendmail[0], append(sendmail[1:len(sendmail)], ctx.NotifyFile.To)..., @@ -256,7 +284,7 @@ "File from %s: %s (%s)", ctx.Neigh[*job.PktEnc.Sender].Name, dst, humanize.IBytes(uint64(pktSize)), - )) + ), nil) cmd.Run() } } @@ -280,31 +308,22 @@ } dst := string(dstRaw) sds["dst"] = dst sender := ctx.Neigh[*job.PktEnc.Sender] - freq := sender.Freq - if freq == nil { + freqPath := sender.FreqPath + if freqPath == nil { ctx.LogE("rx", sds, "freqing is not allowed") isBad = true goto Closing } if !dryRun { - if sender.FreqChunked == 0 { - err = ctx.TxFile( - sender, - pkt.Nice, - filepath.Join(*freq, src), - dst, - sender.FreqMinSize, - ) - } else { - err = ctx.TxFileChunked( - sender, - pkt.Nice, - filepath.Join(*freq, src), - dst, - sender.FreqMinSize, - sender.FreqChunked, - ) - } + err = ctx.TxFile( + sender, + pkt.Nice, + filepath.Join(*freqPath, src), + dst, + sender.FreqChunked, + sender.FreqMinSize, + sender.FreqMaxSize, + ) if err != nil { ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "tx file") isBad = true @@ -322,17 +341,14 @@ if err = os.Remove(job.Fd.Name()); err != nil { ctx.LogE("rx", SdsAdd(sds, SDS{"err": err}), "remove") isBad = true } - sendmail, exists := ctx.Neigh[*ctx.SelfId].Exec["sendmail"] - if exists && len(sendmail) > 0 && ctx.NotifyFreq != nil { + if len(sendmail) > 0 && ctx.NotifyFreq != nil { cmd := exec.Command( sendmail[0], append(sendmail[1:len(sendmail)], ctx.NotifyFreq.To)..., ) cmd.Stdin = newNotification(ctx.NotifyFreq, fmt.Sprintf( - "Freq from %s: %s", - ctx.Neigh[*job.PktEnc.Sender].Name, - src, - )) + "Freq from %s: %s", sender.Name, src, + ), nil) cmd.Run() } } diff --git a/src/toss_test.go b/src/toss_test.go index 2ff29b5ec9611a7c0f0da76d2cb7450384ad8bbfd8117998ca68386ea8c17a4d..535ab95b7a5bf3bf7af2f4a4f2a0e4dd63d65a1bfa5e912bdbd2a607f717cb3d 100644 --- a/src/toss_test.go +++ b/src/toss_test.go @@ -30,7 +30,7 @@ "strings" "testing" "testing/quick" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "golang.org/x/crypto/blake2b" ) @@ -199,7 +199,9 @@ ctx.Neigh[*nodeOur.Id], DefaultNiceFile, src, fileName, + MaxFileSize, 1<<15, + MaxFileSize, ); err != nil { t.Error(err) return false @@ -273,7 +275,9 @@ ctx.Neigh[*nodeOur.Id], DefaultNiceFile, srcPath, "samefile", + MaxFileSize, 1<<15, + MaxFileSize, ); err != nil { t.Error(err) return false @@ -357,7 +361,7 @@ ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false, false, false, false, false, false) if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 { return false } - ctx.Neigh[*nodeOur.Id].Freq = &spool + ctx.Neigh[*nodeOur.Id].FreqPath = &spool ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false, false, false, false, false, false) if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 { return false diff --git a/src/tx.go b/src/tx.go index 7fb6bb93ee05f5b1081204a4ba8d357c848011d921bf3b15c8b9fe419cc2d42d..72e0868a171c8b66cb45d2fa775c72b137cbb5ceddf6eb69c82bcd3dcb0fb352 100644 --- a/src/tx.go +++ b/src/tx.go @@ -18,6 +18,7 @@ package nncp import ( + "archive/tar" "bufio" "bytes" "crypto/rand" @@ -29,13 +30,21 @@ "os" "path/filepath" "strconv" "strings" + "time" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "github.com/klauspost/compress/zstd" "golang.org/x/crypto/blake2b" "golang.org/x/crypto/chacha20poly1305" ) +const ( + MaxFileSize = 1 << 62 + + TarBlockSize = 512 + TarExt = ".tar" +) + func (ctx *Ctx) Tx( node *Node, pkt *Pkt, @@ -43,10 +52,6 @@ nice uint8, size, minSize int64, src io.Reader, ) (*Node, error) { - tmp, err := ctx.NewTmpFileWHash() - if err != nil { - return nil, err - } hops := make([]*Node, 0, 1+len(node.Via)) hops = append(hops, node) lastNode := node @@ -62,6 +67,14 @@ padSize := minSize - expectedSize if padSize < 0 { padSize = 0 } + if !ctx.IsEnoughSpace(size + padSize) { + return nil, errors.New("is not enough space") + } + tmp, err := ctx.NewTmpFileWHash() + if err != nil { + return nil, err + } + errs := make(chan error) curSize := size pipeR, pipeW := io.Pipe() @@ -109,121 +122,197 @@ os.Symlink(nodePath, filepath.Join(ctx.Spool, lastNode.Name)) return lastNode, err } -func prepareTxFile(srcPath string) (io.Reader, *os.File, int64, error) { - var reader io.Reader - var src *os.File - var fileSize int64 - var err error +type DummyCloser struct{} + +func (dc DummyCloser) Close() error { return nil } + +func prepareTxFile(srcPath string) (reader io.Reader, closer io.Closer, fileSize int64, archived bool, rerr error) { if srcPath == "-" { - src, err = ioutil.TempFile("", "nncp-file") + // Read content from stdin, saving to temporary file, encrypting + // on the fly + src, err := ioutil.TempFile("", "nncp-file") if err != nil { - return nil, nil, 0, err + rerr = err + return } os.Remove(src.Name()) tmpW := bufio.NewWriter(src) tmpKey := make([]byte, chacha20poly1305.KeySize) - if _, err = rand.Read(tmpKey[:]); err != nil { - return nil, nil, 0, err + if _, rerr = rand.Read(tmpKey[:]); rerr != nil { + return } aead, err := chacha20poly1305.New(tmpKey) if err != nil { - return nil, nil, 0, err + rerr = err + return } nonce := make([]byte, aead.NonceSize()) written, err := aeadProcess(aead, nonce, true, bufio.NewReader(os.Stdin), tmpW) if err != nil { - return nil, nil, 0, err + rerr = err + return } fileSize = int64(written) if err = tmpW.Flush(); err != nil { - return nil, nil, 0, err + return } src.Seek(0, io.SeekStart) r, w := io.Pipe() go func() { if _, err := aeadProcess(aead, nonce, false, bufio.NewReader(src), w); err != nil { - panic(err) + w.CloseWithError(err) } }() reader = r - } else { - src, err = os.Open(srcPath) + closer = src + return + } + + srcStat, err := os.Stat(srcPath) + if err != nil { + rerr = err + return + } + mode := srcStat.Mode() + + if mode.IsRegular() { + // It is regular file, just send it + src, err := os.Open(srcPath) if err != nil { - return nil, nil, 0, err - } - srcStat, err := src.Stat() - if err != nil { - return nil, nil, 0, err + rerr = err + return } fileSize = srcStat.Size() reader = bufio.NewReader(src) + closer = src + return } - return reader, src, fileSize, nil -} -func (ctx *Ctx) TxFile(node *Node, nice uint8, srcPath, dstPath string, minSize int64) error { - if dstPath == "" { - if srcPath == "-" { - return errors.New("Must provide destination filename") - } - dstPath = filepath.Base(srcPath) - } - dstPath = filepath.Clean(dstPath) - if filepath.IsAbs(dstPath) { - return errors.New("Relative destination path required") + if !mode.IsDir() { + rerr = errors.New("unsupported file type") + return } - pkt, err := NewPkt(PktTypeFile, nice, []byte(dstPath)) - if err != nil { - return err - } - reader, src, fileSize, err := prepareTxFile(srcPath) - if src != nil { - defer src.Close() - } + + // It is directory, create PAX archive with its contents + archived = true + basePath := filepath.Base(srcPath) + rootPath, err := filepath.Abs(srcPath) if err != nil { - return err + rerr = err + return } - _, err = ctx.Tx(node, pkt, nice, fileSize, minSize, reader) - sds := SDS{ - "type": "file", - "node": node.Id, - "nice": strconv.Itoa(int(nice)), - "src": srcPath, - "dst": dstPath, - "size": strconv.FormatInt(fileSize, 10), + type einfo struct { + path string + modTime time.Time + size int64 } - if err == nil { - ctx.LogI("tx", sds, "sent") - } else { - sds["err"] = err - ctx.LogE("tx", sds, "sent") + dirs := make([]einfo, 0, 1<<10) + files := make([]einfo, 0, 1<<10) + rerr = filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + // directory header, PAX record header+contents + fileSize += TarBlockSize + 2*TarBlockSize + dirs = append(dirs, einfo{path: path, modTime: info.ModTime()}) + } else { + // file header, PAX record header+contents, file content + fileSize += TarBlockSize + 2*TarBlockSize + info.Size() + if n := info.Size() % TarBlockSize; n != 0 { + fileSize += TarBlockSize - n // padding + } + files = append(files, einfo{ + path: path, + modTime: info.ModTime(), + size: info.Size(), + }) + } + return nil + }) + if rerr != nil { + return } - return err + + r, w := io.Pipe() + reader = r + closer = DummyCloser{} + fileSize += 2 * TarBlockSize // termination block + + go func() { + tarWr := tar.NewWriter(w) + hdr := tar.Header{ + Typeflag: tar.TypeDir, + Mode: 0777, + PAXRecords: map[string]string{ + "comment": "Autogenerated by " + VersionGet(), + }, + Format: tar.FormatPAX, + } + for _, e := range dirs { + hdr.Name = basePath + e.path[len(rootPath):] + hdr.ModTime = e.modTime + if err = tarWr.WriteHeader(&hdr); err != nil { + w.CloseWithError(err) + } + } + hdr.Typeflag = tar.TypeReg + hdr.Mode = 0666 + for _, e := range files { + hdr.Name = basePath + e.path[len(rootPath):] + hdr.ModTime = e.modTime + hdr.Size = e.size + if err = tarWr.WriteHeader(&hdr); err != nil { + w.CloseWithError(err) + } + fd, err := os.Open(e.path) + if err != nil { + w.CloseWithError(err) + } + _, err = io.Copy(tarWr, bufio.NewReader(fd)) + if err != nil { + w.CloseWithError(err) + } + fd.Close() + } + tarWr.Close() + w.Close() + }() + return } -func (ctx *Ctx) TxFileChunked( +func (ctx *Ctx) TxFile( node *Node, nice uint8, srcPath, dstPath string, - minSize int64, chunkSize int64, + minSize, maxSize int64, ) error { + dstPathSpecified := false if dstPath == "" { if srcPath == "-" { return errors.New("Must provide destination filename") } dstPath = filepath.Base(srcPath) + } else { + dstPathSpecified = true } dstPath = filepath.Clean(dstPath) if filepath.IsAbs(dstPath) { return errors.New("Relative destination path required") } - reader, src, fileSize, err := prepareTxFile(srcPath) - if src != nil { - defer src.Close() + reader, closer, fileSize, archived, err := prepareTxFile(srcPath) + if closer != nil { + defer closer.Close() } if err != nil { return err + } + if fileSize > maxSize { + return errors.New("Too big than allowed") + } + if archived && !dstPathSpecified { + dstPath += TarExt } if fileSize <= chunkSize { @@ -243,8 +332,7 @@ } if err == nil { ctx.LogI("tx", sds, "sent") } else { - sds["err"] = err - ctx.LogE("tx", sds, "sent") + ctx.LogE("tx", SdsAdd(sds, SDS{"err": err}), "sent") } return err } @@ -299,8 +387,7 @@ } if err == nil { ctx.LogI("tx", sds, "sent") } else { - sds["err"] = err - ctx.LogE("tx", sds, "sent") + ctx.LogE("tx", SdsAdd(sds, SDS{"err": err}), "sent") return err } hsh.Sum(metaPkt.Checksums[chunkNum][:0]) @@ -333,8 +420,7 @@ } if err == nil { ctx.LogI("tx", sds, "sent") } else { - sds["err"] = err - ctx.LogE("tx", sds, "sent") + ctx.LogE("tx", SdsAdd(sds, SDS{"err": err}), "sent") } return err } @@ -370,8 +456,7 @@ } if err == nil { ctx.LogI("tx", sds, "sent") } else { - sds["err"] = err - ctx.LogE("tx", sds, "sent") + ctx.LogE("tx", SdsAdd(sds, SDS{"err": err}), "sent") } return err } @@ -419,8 +504,7 @@ } if err == nil { ctx.LogI("tx", sds, "sent") } else { - sds["err"] = err - ctx.LogE("tx", sds, "sent") + ctx.LogE("tx", SdsAdd(sds, SDS{"err": err}), "sent") } return err } @@ -433,6 +517,11 @@ "nice": strconv.Itoa(int(nice)), "size": strconv.FormatInt(size, 10), } ctx.LogD("tx", sds, "taken") + if !ctx.IsEnoughSpace(size) { + err := errors.New("is not enough space") + ctx.LogE("tx", SdsAdd(sds, SDS{"err": err}), err.Error()) + return err + } tmp, err := ctx.NewTmpFileWHash() if err != nil { return err @@ -445,8 +534,7 @@ err = tmp.Commit(filepath.Join(nodePath, string(TTx))) if err == nil { ctx.LogI("tx", sds, "sent") } else { - sds["err"] = err - ctx.LogI("tx", sds, "sent") + ctx.LogI("tx", SdsAdd(sds, SDS{"err": err}), "sent") } os.Symlink(nodePath, filepath.Join(ctx.Spool, node.Name)) return err diff --git a/src/tx_test.go b/src/tx_test.go index 001c3e18b166efe0bf5604a126d0fa2ed670f34fceb2de938cadc8de101d133b..1749d1dd70738450fee6ca2eac066afa555b55c191b13b7a9fa2723f322ba71d 100644 --- a/src/tx_test.go +++ b/src/tx_test.go @@ -27,7 +27,7 @@ "strings" "testing" "testing/quick" - "github.com/davecgh/go-xdr/xdr2" + xdr "github.com/davecgh/go-xdr/xdr2" "golang.org/x/crypto/blake2b" )