nncp.html: *.texi sp.utxt pedro.txt
rm -f nncp.html/*.html
$(MAKEINFO) --html \
- --set-customization-variable EXTRA_HEAD='<link rev="made" href="mailto:stargrave+webmaster@nncpgo.org">' \
+ --set-customization-variable EXTRA_HEAD='<link rev="made" href="mailto:webmaster@nncpgo.org">' \
--set-customization-variable CSS_LINES='$(CSS)' \
--set-customization-variable SHOW_TITLE=0 \
--set-customization-variable USE_ACCESSKEY=0 \
bind to and listen.
It could be run as @command{inetd} service, by specifying
-@option{-inetd} option. Example inetd-entry:
+@option{-inetd} option. Pay attention that because it uses stdin/stdout,
+it can not effectively work with IO timeouts and connection closing can
+propagate up to 5 minutes in practice. Example inetd-entry:
@verbatim
-uucp stream tcp6 nowait nncpuser /usr/local/bin/nncp-daemon nncp-daemon -inetd
+uucp stream tcp6 nowait nncpuser /usr/local/bin/nncp-daemon nncp-daemon -quiet -inetd
@end verbatim
@node nncp-exec
@multitable {XXXXX} {XXXX-XX-XX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
@headitem Version @tab Date @tab Size @tab Tarball @tab SHA256 checksum
+@item @ref{Release 5.2.1, 5.2.1} @tab 2019-12-15 @tab 1109 KiB
+@tab @url{download/nncp-5.2.1.tar.xz, link} @url{download/nncp-5.2.1.tar.xz.sig, sign}
+@tab @code{983D1A8A 4398C281 76356AE1 C5541124 B0755555 D115063B D1388F85 9C4A6B3E}
+
@item @ref{Release 5.2.0, 5.2.0} @tab 2019-12-14 @tab 1109 KiB
@tab @url{download/nncp-5.2.0.tar.xz, link} @url{download/nncp-5.2.0.tar.xz.sig, sign}
@tab @code{FFC55467 8B4ECCA6 92D90F42 ACC0286D 209E054E EA1CBF87 0307003E CF219610}
This manual is for NNCP (Node to Node copy) -- collection of utilities
simplifying secure store-and-forward files and mail exchanging.
-Copyright @copyright{} 2016-2019 @email{stargrave@@stargrave.org, Sergey Matveev}
+Copyright @copyright{} 2016-2020 @email{stargrave@@stargrave.org, Sergey Matveev}
@quotation
Permission is granted to copy, distribute and/or modify this document
@node Installation
@unnumbered Installation
-@set VERSION 5.2.1
+@set VERSION 5.3.0
Possibly NNCP package already exists for your distribution:
@item @url{https://www.freshports.org/net/nncp/, FreeBSD ports}
@item @url{https://github.com/DragonFlyBSD/DPorts/tree/master/net/nncp, DragonFly BSD ports}
@item @url{https://github.com/void-linux/void-packages/blob/master/srcpkgs/nncp/template, Void Linux}
+@item @url{https://qa.debian.org/developer.php?login=jgoerzen@@complete.org, Debian packages} (pending inclusion, maintainer's page)
+@item @url{https://github.com/NixOS/nixpkgs/pull/75772, NixOS packages} (pending inclusion, maintainer's page)
@end itemize
NNCP should run on any POSIX-compatible operating system.
@node Новости
@section Новости
+@node Релиз 5.3.0
+@subsection Релиз 5.3.0
+@itemize
+
+@item
+Сообщения прогресса содержат префикс, поясняющий выполняемое действие.
+
+@item
+Исправлено не происходящее дополнение (padding) handshake сообщений.
+
+@item
+Завершать все порождаемые в SP протоколе горутины, меньше утечек памяти.
+
+@item
+SP протокол порождает меньше вызовов записей (соответственно, и TCP
+пакетов) в сокет.
+
+@item
+Проверять @option{onlinedeadline} и @option{maxonlinetime} ежесекундно,
+независимо от чтения из сокета (раз в 10 секунд в худшем случае).
+
+@item
+Раз в минуту, если нет более никакого другого трафика, посылаются PING
+пакеты в SP-соединении. Это позволит быстрее понимать что соединение
+более не работоспособно.
+
+@item
+@command{nncp-toss} использует lock-file для предотвращения
+одновременной обработки зашифрованных пакетов.
+
+@end itemize
+
@node Релиз 5.2.1
@subsection Релиз 5.2.1
@itemize
See also this page @ref{Новости, on russian}.
+@node Release 5.3.0
+@section Release 5.3.0
+@itemize
+
+@item
+Progress messages contain prefix, describing the running action.
+
+@item
+Fixed not occurring handshake messages padding.
+
+@item
+Finish all SP protocol related goroutines, less memory leak.
+
+@item
+SP protocol generates less socket write calls, thus generating less TCP
+packets.
+
+@item
+Check @option{onlinedeadline} and @option{maxonlinetime} options every
+second, independently from socket reads (up to 10 seconds).
+
+@item
+Once per minute, if no other traffic exists, PING packets are sent in
+SP-connection. That allows faster determining of connection unworkability.
+
+@item
+@command{nncp-toss} uses lock-file to prevent simultaneous tossing.
+
+@end itemize
+
@node Release 5.2.1
@section Release 5.2.1
@itemize
+------+
@end verbatim
+@item PING
+ Dummy packet only used for determining workability of the connection.
+
+@verbatim
++------+
+| PING |
++------+
+@end verbatim
+
@item INFO
Information about the file we have for transmission.
delete @file{.part} suffix from file's name and send @emph{DONE} packet.
@item When @emph{DONE} packet received, delete corresponding file.
+
@item When @emph{HALT} packet received, empty file sending queue.
@item Each second, node checks: are there any new @emph{tx} packets
appeared and queues corresponding @emph{INFO} packets.
-@item If no packets are sent and received during @ref{CfgOnlineDeadline,
-onlinedeadline} duration, then close the connection. There is no
-explicit indication that session is over.
+@item Each minute, if no packets were sent, node sends @emph{PING}
+packet.
+
+@item If no non-PING packets are sent and received during
+@ref{CfgOnlineDeadline, onlinedeadline} duration, then close the
+connection. There is no explicit indication that session is over.
+
+@item If no packets are received during two minutes (two PING timeouts),
+then close the connection.
@end enumerate
Initiator <- Responder : [e, ee, se], INFO..., HALT...
Initiator -> Responder : INFO..., FREQ..., DONE...
Initiator <- Responder : INFO..., FREQ..., DONE...
-Initiator -> Responder : FILE..., INFO..., DONE...
-Initiator <- Responder : FILE..., INFO..., DONE...
+Initiator -> Responder : FILE..., INFO..., DONE..., PING
+Initiator <- Responder : FILE..., INFO..., DONE..., PING
@enduml
spool/2WHB...OABQ/rx.lock
spool/2WHB...OABQ/rx/5ZIB...UMKW.part
spool/2WHB...OABQ/tx.lock
+spool/2WHB...OABQ/toss.lock
spool/BYRR...CG6Q/rx.lock
spool/BYRR...CG6Q/rx/
spool/BYRR...CG6Q/tx.lock
There are people deserving to be thanked for helping this project:
@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
@url{http://www.nncpgo.org/}.
EOF
perl -i -ne 'print unless /include pedro/' doc/index.texi doc/about.ru.texi
+perl -p -i -e 's/^(.verbatiminclude) .*$/$1 PUBKEY.asc/g' doc/integrity.texi
+mv doc/.well-known/openpgpkey/hu/i4cdqgcarfjdjnba6y4jnf498asg8c6p.asc PUBKEY.asc
+ln -s ../PUBKEY.asc doc
make -C doc
########################################################################
# Supplementary files autogeneration
########################################################################
-texi=`mktemp`
+texi=$(TMPDIR=doc mktemp)
cat > $texi <<EOF
\input texinfo
@documentencoding UTF-8
@settitle NEWS
-
@node News
@unnumbered News
-
`sed -n '5,$p' < doc/news.texi`
-
@bye
EOF
makeinfo --plaintext -o NEWS $texi
\input texinfo
@documentencoding UTF-8
@settitle NEWS.RU
-
@node Новости
@unnumbered Новости
-
`sed -n '3,$p' < doc/news.ru.texi | sed 's/^@subsection/@section/'`
-
@bye
EOF
makeinfo --plaintext -o NEWS.RU $texi
rm -f $texi
-texi=$(TMPDIR=doc mktemp)
cat > $texi <<EOF
\input texinfo
@documentencoding UTF-8
@settitle INSTALL
-
@include install.texi
-
@bye
EOF
makeinfo --plaintext -o INSTALL $texi
rm -f $texi
-texi=`mktemp`
-
cat > $texi <<EOF
\input texinfo
@documentencoding UTF-8
@settitle THANKS
-
`cat doc/thanks.texi`
-
@bye
EOF
makeinfo --plaintext -o THANKS $texi
########################################################################
-mv doc/.well-known/openpgpkey/hu/i4cdqgcarfjdjnba6y4jnf498asg8c6p.asc PUBKEY.asc
rm -r doc/.gitignore doc/.well-known doc/nncp.html/.well-known
find . -type d -exec chmod 755 {} \;
# $FreeBSD: head/net/nncp/Makefile 517819 2019-11-17 11:51:56Z dmgk $
PORTNAME= nncp
-DISTVERSION= 5.2.1
+DISTVERSION= 5.3.0
CATEGORIES= net
MASTER_SITES= http://www.nncpgo.org/download/
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
import (
"net"
+ "time"
"github.com/gorhill/cronexpr"
)
RxRate int
TxRate int
Addr *string
- OnlineDeadline uint
- MaxOnlineTime uint
+ OnlineDeadline time.Duration
+ MaxOnlineTime time.Duration
}
func (ctx *Ctx) CallNode(
nice uint8,
xxOnly TRxTx,
rxRate, txRate int,
- onlineDeadline, maxOnlineTime uint,
+ onlineDeadline, maxOnlineTime time.Duration,
listOnly bool,
onlyPkts map[[32]byte]bool,
) (isGood bool) {
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
"os"
"path"
"strconv"
+ "time"
"github.com/gorhill/cronexpr"
"github.com/hjson/hjson-go"
Neigh map[string]NodeJSON `json:"neigh"`
}
-func NewNode(name string, yml NodeJSON) (*Node, error) {
- nodeId, err := NodeIdFromString(yml.Id)
+func NewNode(name string, cfg NodeJSON) (*Node, error) {
+ nodeId, err := NodeIdFromString(cfg.Id)
if err != nil {
return nil, err
}
- exchPub, err := FromBase32(yml.ExchPub)
+ exchPub, err := FromBase32(cfg.ExchPub)
if err != nil {
return nil, err
}
return nil, errors.New("Invalid exchPub size")
}
- signPub, err := FromBase32(yml.SignPub)
+ signPub, err := FromBase32(cfg.SignPub)
if err != nil {
return nil, err
}
}
var noisePub []byte
- if yml.NoisePub != nil {
- noisePub, err = FromBase32(*yml.NoisePub)
+ if cfg.NoisePub != nil {
+ noisePub, err = FromBase32(*cfg.NoisePub)
if err != nil {
return nil, err
}
}
var incoming *string
- if yml.Incoming != nil {
- inc := path.Clean(*yml.Incoming)
+ if cfg.Incoming != nil {
+ inc := path.Clean(*cfg.Incoming)
if !path.IsAbs(inc) {
return nil, errors.New("Incoming path must be absolute")
}
freqChunked := int64(MaxFileSize)
var freqMinSize int64
freqMaxSize := int64(MaxFileSize)
- if yml.Freq != nil {
- f := yml.Freq
+ if cfg.Freq != nil {
+ f := cfg.Freq
if f.Path != nil {
fPath := path.Clean(*f.Path)
if !path.IsAbs(fPath) {
}
defRxRate := 0
- if yml.RxRate != nil && *yml.RxRate > 0 {
- defRxRate = *yml.RxRate
+ if cfg.RxRate != nil && *cfg.RxRate > 0 {
+ defRxRate = *cfg.RxRate
}
defTxRate := 0
- if yml.TxRate != nil && *yml.TxRate > 0 {
- defTxRate = *yml.TxRate
+ if cfg.TxRate != nil && *cfg.TxRate > 0 {
+ defTxRate = *cfg.TxRate
}
- defOnlineDeadline := uint(DefaultDeadline)
- if yml.OnlineDeadline != nil {
- if *yml.OnlineDeadline <= 0 {
+ defOnlineDeadline := DefaultDeadline
+ if cfg.OnlineDeadline != nil {
+ if *cfg.OnlineDeadline <= 0 {
return nil, errors.New("OnlineDeadline must be at least 1 second")
}
- defOnlineDeadline = *yml.OnlineDeadline
+ defOnlineDeadline = time.Duration(*cfg.OnlineDeadline) * time.Second
}
- var defMaxOnlineTime uint
- if yml.MaxOnlineTime != nil {
- defMaxOnlineTime = *yml.MaxOnlineTime
+ var defMaxOnlineTime time.Duration
+ if cfg.MaxOnlineTime != nil {
+ defMaxOnlineTime = time.Duration(*cfg.MaxOnlineTime) * time.Second
}
var calls []*Call
- for _, callYml := range yml.Calls {
- expr, err := cronexpr.Parse(callYml.Cron)
+ for _, callCfg := range cfg.Calls {
+ expr, err := cronexpr.Parse(callCfg.Cron)
if err != nil {
return nil, err
}
nice := uint8(255)
- if callYml.Nice != nil {
- nice, err = NicenessParse(*callYml.Nice)
+ if callCfg.Nice != nil {
+ nice, err = NicenessParse(*callCfg.Nice)
if err != nil {
return nil, err
}
}
var xx TRxTx
- if callYml.Xx != nil {
- switch *callYml.Xx {
+ if callCfg.Xx != nil {
+ switch *callCfg.Xx {
case "rx":
xx = TRx
case "tx":
}
rxRate := defRxRate
- if callYml.RxRate != nil {
- rxRate = *callYml.RxRate
+ if callCfg.RxRate != nil {
+ rxRate = *callCfg.RxRate
}
txRate := defTxRate
- if callYml.TxRate != nil {
- txRate = *callYml.TxRate
+ if callCfg.TxRate != nil {
+ txRate = *callCfg.TxRate
}
var addr *string
- if callYml.Addr != nil {
- if a, exists := yml.Addrs[*callYml.Addr]; exists {
+ if callCfg.Addr != nil {
+ if a, exists := cfg.Addrs[*callCfg.Addr]; exists {
addr = &a
} else {
- addr = callYml.Addr
+ addr = callCfg.Addr
}
}
onlineDeadline := defOnlineDeadline
- if callYml.OnlineDeadline != nil {
- if *callYml.OnlineDeadline == 0 {
+ if callCfg.OnlineDeadline != nil {
+ if *callCfg.OnlineDeadline == 0 {
return nil, errors.New("OnlineDeadline must be at least 1 second")
}
- onlineDeadline = *callYml.OnlineDeadline
+ onlineDeadline = time.Duration(*callCfg.OnlineDeadline) * time.Second
}
- var maxOnlineTime uint
- if callYml.MaxOnlineTime != nil {
- maxOnlineTime = *callYml.MaxOnlineTime
+ var maxOnlineTime time.Duration
+ if callCfg.MaxOnlineTime != nil {
+ maxOnlineTime = time.Duration(*callCfg.MaxOnlineTime) * time.Second
}
calls = append(calls, &Call{
Id: nodeId,
ExchPub: new([32]byte),
SignPub: ed25519.PublicKey(signPub),
- Exec: yml.Exec,
+ Exec: cfg.Exec,
Incoming: incoming,
FreqPath: freqPath,
FreqChunked: freqChunked,
FreqMinSize: freqMinSize,
FreqMaxSize: freqMaxSize,
Calls: calls,
- Addrs: yml.Addrs,
+ Addrs: cfg.Addrs,
RxRate: defRxRate,
TxRate: defTxRate,
OnlineDeadline: defOnlineDeadline,
return &node, nil
}
-func NewNodeOur(yml *NodeOurJSON) (*NodeOur, error) {
- id, err := NodeIdFromString(yml.Id)
+func NewNodeOur(cfg *NodeOurJSON) (*NodeOur, error) {
+ id, err := NodeIdFromString(cfg.Id)
if err != nil {
return nil, err
}
- exchPub, err := FromBase32(yml.ExchPub)
+ exchPub, err := FromBase32(cfg.ExchPub)
if err != nil {
return nil, err
}
return nil, errors.New("Invalid exchPub size")
}
- exchPrv, err := FromBase32(yml.ExchPrv)
+ exchPrv, err := FromBase32(cfg.ExchPrv)
if err != nil {
return nil, err
}
return nil, errors.New("Invalid exchPrv size")
}
- signPub, err := FromBase32(yml.SignPub)
+ signPub, err := FromBase32(cfg.SignPub)
if err != nil {
return nil, err
}
return nil, errors.New("Invalid signPub size")
}
- signPrv, err := FromBase32(yml.SignPrv)
+ signPrv, err := FromBase32(cfg.SignPrv)
if err != nil {
return nil, err
}
return nil, errors.New("Invalid signPrv size")
}
- noisePub, err := FromBase32(yml.NoisePub)
+ noisePub, err := FromBase32(cfg.NoisePub)
if err != nil {
return nil, err
}
return nil, errors.New("Invalid noisePub size")
}
- noisePrv, err := FromBase32(yml.NoisePrv)
+ noisePrv, err := FromBase32(cfg.NoisePrv)
if err != nil {
return nil, err
}
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
if err != nil {
log.Fatalln(err)
}
- if _, err = CopyProgressed(hsh, bufio.NewReader(src), sds, showPrgrs); err != nil {
+ if _, err = CopyProgressed(hsh, bufio.NewReader(src), "check", sds, showPrgrs); err != nil {
return false, err
}
return bytes.Compare(hsh.Sum(nil), checksum) == 0, nil
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
log.Fatalln("Error writing tar header:", err)
}
if _, err = nncp.CopyProgressed(
- tarWr, job.Fd,
+ tarWr, job.Fd, "Tx",
nncp.SdsAdd(sds, nncp.SDS{
"pkt": nncp.ToBase32(job.HshValue[:]),
"fullsize": job.Size,
log.Fatalln("Error during writing:", err)
}
if _, err = nncp.CopyProgressed(
- hsh, tarR,
+ hsh, tarR, "Rx",
nncp.SdsAdd(sds, nncp.SDS{"fullsize": entry.Size}),
ctx.ShowPrgrs,
); err != nil {
if _, err = hsh.Write(pktEncBuf); err != nil {
log.Fatalln("Error during writing:", err)
}
- if _, err = nncp.CopyProgressed(hsh, tarR, sds, ctx.ShowPrgrs); err != nil {
+ if _, err = nncp.CopyProgressed(hsh, tarR, "check", sds, ctx.ShowPrgrs); err != nil {
log.Fatalln("Error during copying:", err)
}
if nncp.ToBase32(hsh.Sum(nil)) != pktName {
if _, err = tmp.W.Write(pktEncBuf); err != nil {
log.Fatalln("Error during writing:", err)
}
- if _, err = nncp.CopyProgressed(tmp.W, tarR, sds, ctx.ShowPrgrs); err != nil {
+ if _, err = nncp.CopyProgressed(tmp.W, tarR, "check", sds, ctx.ShowPrgrs); err != nil {
log.Fatalln("Error during copying:", err)
}
if err = tmp.W.Flush(); err != nil {
}
} else {
if *dryRun {
- if _, err = nncp.CopyProgressed(ioutil.Discard, tarR, sds, ctx.ShowPrgrs); err != nil {
+ if _, err = nncp.CopyProgressed(ioutil.Discard, tarR, "Rx", sds, ctx.ShowPrgrs); err != nil {
log.Fatalln("Error during copying:", err)
}
} else {
if _, err = bufTmp.Write(pktEncBuf); err != nil {
log.Fatalln("Error during writing:", err)
}
- if _, err = nncp.CopyProgressed(bufTmp, tarR, sds, ctx.ShowPrgrs); err != nil {
+ if _, err = nncp.CopyProgressed(bufTmp, tarR, "Rx", sds, ctx.ShowPrgrs); err != nil {
log.Fatalln("Error during copying:", err)
}
if err = bufTmp.Flush(); err != nil {
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
"log"
"os"
"strings"
+ "time"
"go.cypherpunks.ru/nncp/v5"
)
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")
+ onlineDeadlineSec = flag.Uint("onlinedeadline", 0, "Override onlinedeadline option")
+ maxOnlineTimeSec = flag.Uint("maxonlinetime", 0, "Override maxonlinetime option")
)
flag.Usage = usage
flag.Parse()
log.Fatalln("Node does not have online communication capability")
}
- if *onlineDeadline == 0 {
- onlineDeadline = &node.OnlineDeadline
+ onlineDeadline := node.OnlineDeadline
+ if *onlineDeadlineSec != 0 {
+ onlineDeadline = time.Duration(*onlineDeadlineSec) * time.Second
}
- if *maxOnlineTime == 0 {
- maxOnlineTime = &node.MaxOnlineTime
+ maxOnlineTime := node.MaxOnlineTime
+ if *maxOnlineTimeSec != 0 {
+ maxOnlineTime = time.Duration(*maxOnlineTimeSec) * time.Second
}
var xxOnly nncp.TRxTx
xxOnly,
*rxRate,
*txRate,
- *onlineDeadline,
- *maxOnlineTime,
+ onlineDeadline,
+ maxOnlineTime,
*listOnly,
onlyPkts,
) {
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
}
func (c InetdConn) Close() error {
+ if err := c.r.Close(); err != nil {
+ c.w.Close()
+ return err
+ }
return c.w.Close()
}
state.Wait()
ctx.LogI("call-finish", nncp.SDS{
"node": state.Node.Id,
- "duration": state.Duration.Seconds(),
+ "duration": int64(state.Duration.Seconds()),
"rxbytes": state.RxBytes,
"txbytes": state.TxBytes,
"rxspeed": state.RxSpeed,
os.Stderr.Close()
conn := &InetdConn{os.Stdin, os.Stdout}
performSP(ctx, conn, nice)
+ conn.Close()
return
}
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
if err != nil {
log.Fatalln(err)
}
- if _, err = nncp.CopyProgressed(hsh, bufio.NewReader(fd), nncp.SDS{
- "pkt": chunkPath,
- "fullsize": fi.Size(),
- }, ctx.ShowPrgrs); err != nil {
+ if _, err = nncp.CopyProgressed(
+ hsh, bufio.NewReader(fd), "check",
+ nncp.SDS{
+ "pkt": chunkPath,
+ "fullsize": fi.Size(),
+ },
+ ctx.ShowPrgrs,
+ ); err != nil {
log.Fatalln(err)
}
fd.Close()
if err != nil {
log.Fatalln("Can not stat file:", err)
}
- if _, err = nncp.CopyProgressed(dstW, bufio.NewReader(fd), nncp.SDS{
- "pkt": chunkPath,
- "fullsize": fi.Size(),
- }, ctx.ShowPrgrs); err != nil {
+ if _, err = nncp.CopyProgressed(
+ dstW, bufio.NewReader(fd), "reass",
+ nncp.SDS{
+ "pkt": chunkPath,
+ "fullsize": fi.Size(),
+ },
+ ctx.ShowPrgrs,
+ ); err != nil {
log.Fatalln(err)
}
fd.Close()
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
ctx.LogE("nncp-xfer", sds, err, "copy")
w.CloseWithError(err)
}()
- if _, err = nncp.CopyProgressed(tmp.W, r, nncp.SdsAdd(sds, nncp.SDS{
- "pkt": filename,
- "fullsize": sds["size"],
- }), ctx.ShowPrgrs); err != nil {
+ if _, err = nncp.CopyProgressed(
+ tmp.W, r, "Rx",
+ nncp.SdsAdd(sds, nncp.SDS{
+ "pkt": filename,
+ "fullsize": sds["size"],
+ }),
+ ctx.ShowPrgrs,
+ ); err != nil {
ctx.LogE("nncp-xfer", sds, err, "copy")
isBad = true
}
ctx.LogD("nncp-xfer", sds, "skip")
continue
}
- dirLock, err := ctx.LockDir(&nodeId, nncp.TTx)
+ dirLock, err := ctx.LockDir(&nodeId, string(nncp.TTx))
if err != nil {
continue
}
ctx.LogD("nncp-xfer", sds, "created")
bufW := bufio.NewWriter(tmp)
copied, err := nncp.CopyProgressed(
- bufW,
- bufio.NewReader(job.Fd),
+ bufW, bufio.NewReader(job.Fd), "Tx",
nncp.SdsAdd(sds, nncp.SDS{"fullsize": job.Size}),
ctx.ShowPrgrs,
)
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
github.com/hjson/hjson-go v3.0.1+incompatible
github.com/klauspost/compress v1.9.2
github.com/kr/pretty v0.1.0 // indirect
- go.cypherpunks.ru/balloon v1.1.0
- golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708
- golang.org/x/net v0.0.0-20191112182307-2180aed22343
- golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056
+ go.cypherpunks.ru/balloon v1.1.1
+ golang.org/x/crypto v0.0.0-20191219195013-becbf705a915
+ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
+ golang.org/x/sys v0.0.0-20191219235734-af0d71d358ab
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
)
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-go.cypherpunks.ru/balloon v1.1.0 h1:tKwBeS1xrZYS/vn87Hm/4EvgNeHKyU1uC099aPRa2JQ=
-go.cypherpunks.ru/balloon v1.1.0/go.mod h1:k4s4ozrIrhpBjj78Z7LX8ZHxMQ+XE7DZUWl8gP2ojCo=
+go.cypherpunks.ru/balloon v1.1.1 h1:ypHM1DRf/XuCrp9pDkTHg00CqZX/Np/APb//iHvDJTA=
+go.cypherpunks.ru/balloon v1.1.1/go.mod h1:k4s4ozrIrhpBjj78Z7LX8ZHxMQ+XE7DZUWl8gP2ojCo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4=
-golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20191219195013-becbf705a915 h1:aJ0ex187qoXrJHPo8ZasVTASQB7llQP6YeNzgDALPRk=
+golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20191112182307-2180aed22343 h1:00ohfJ4K98s3m6BGUoBd8nyfp4Yl0GoIKvw5abItTjI=
-golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 h1:dHtDnRWQtSx0Hjq9kvKFpBh9uPPKfQN70NZZmvssGwk=
-golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191219235734-af0d71d358ab h1:j8r8g0V3tVdbo274kyTmC+yEsChru2GfvdiV84wm5T8=
+golang.org/x/sys v0.0.0-20191219235734-af0d71d358ab/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
/*
NNCP -- Node to Node copy
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
"golang.org/x/sys/unix"
)
-func (ctx *Ctx) LockDir(nodeId *NodeId, xx TRxTx) (*os.File, error) {
+func (ctx *Ctx) LockDir(nodeId *NodeId, lockCtx string) (*os.File, error) {
ctx.ensureRxDir(nodeId)
- lockPath := filepath.Join(ctx.Spool, nodeId.String(), string(xx)) + ".lock"
+ lockPath := filepath.Join(ctx.Spool, nodeId.String(), lockCtx) + ".lock"
dirLock, err := os.OpenFile(
lockPath,
os.O_CREATE|os.O_WRONLY,
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
func UsageHeader() string {
return VersionGet() + `
-Copyright (C) 2016-2019 Sergey Matveev
+Copyright (C) 2016-2020 Sergey Matveev
License GPLv3: GNU GPL version 3 <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
"crypto/rand"
"errors"
"sync"
+ "time"
"github.com/flynn/noise"
"golang.org/x/crypto/blake2b"
Addrs map[string]string
RxRate int
TxRate int
- OnlineDeadline uint
- MaxOnlineTime uint
+ OnlineDeadline time.Duration
+ MaxOnlineTime time.Duration
Calls []*Call
Busy bool
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
"fmt"
"io"
"os"
- "strings"
"sync"
"time"
func CopyProgressed(
dst io.Writer,
src io.Reader,
+ prgrsPrefix string,
sds SDS,
showPrgrs bool,
) (written int64, err error) {
written += int64(nw)
if showPrgrs {
sds["size"] = written
- Progress(sds)
+ Progress(prgrsPrefix, sds)
}
}
if ew != nil {
return
}
-func Progress(sds SDS) {
- pkt := sds["pkt"].(string)
+func Progress(prefix string, sds SDS) {
var size int64
if sizeI, exists := sds["size"]; exists {
size = sizeI.(int64)
}
fullsize := sds["fullsize"].(int64)
+ pkt := sds["pkt"].(string)
progressBarsLock.RLock()
pb, exists := progressBars[pkt]
progressBarsLock.RUnlock()
if len(what) >= 52 { // Base32 encoded
what = what[:16] + ".." + what[len(what)-16:]
}
- if xx, exists := sds["xx"]; exists {
- what = strings.Title(xx.(string)) + " " + what
- }
+ what = prefix + " " + what
pb.Render(what, size)
if size >= fullsize {
pb.Kill()
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
)
const (
- MaxSPSize = 1<<16 - 256
- PartSuffix = ".part"
- DefaultDeadline = 10
+ MaxSPSize = 1<<16 - 256
+ PartSuffix = ".part"
+ SPHeadOverhead = 4
)
var (
MagicNNCPLv1 [8]byte = [8]byte{'N', 'N', 'C', 'P', 'S', 0, 0, 1}
- SPHeadOverhead int
SPInfoOverhead int
SPFreqOverhead int
SPFileOverhead int
SPHaltMarshalized []byte
+ SPPingMarshalized []byte
NoiseCipherSuite noise.CipherSuite = noise.NewCipherSuite(
noise.DH25519,
noise.HashBLAKE2b,
)
+ DefaultDeadline = 10 * time.Second
+ PingTimeout = time.Minute
+
spWorkersGroup sync.WaitGroup
)
SPTypeFile SPType = iota
SPTypeDone SPType = iota
SPTypeHalt SPType = iota
+ SPTypePing SPType = iota
)
type SPHead struct {
if _, err := xdr.Marshal(&buf, spHead); err != nil {
panic(err)
}
+ SPHaltMarshalized = make([]byte, SPHeadOverhead)
copy(SPHaltMarshalized, buf.Bytes())
- SPHeadOverhead = buf.Len()
+ buf.Reset()
+
+ spHead = SPHead{Type: SPTypePing}
+ if _, err := xdr.Marshal(&buf, spHead); err != nil {
+ panic(err)
+ }
+ SPPingMarshalized = make([]byte, SPHeadOverhead)
+ copy(SPPingMarshalized, buf.Bytes())
buf.Reset()
spInfo := SPInfo{Nice: 123, Size: 123, Hash: new([32]byte)}
func MarshalSP(typ SPType, sp interface{}) []byte {
var buf bytes.Buffer
- var err error
- if _, err = xdr.Marshal(&buf, SPHead{typ}); err != nil {
+ if _, err := xdr.Marshal(&buf, SPHead{typ}); err != nil {
panic(err)
}
- if _, err = xdr.Marshal(&buf, sp); err != nil {
+ if _, err := xdr.Marshal(&buf, sp); err != nil {
panic(err)
}
return buf.Bytes()
Ctx *Ctx
Node *Node
Nice uint8
- onlineDeadline uint
- maxOnlineTime uint
+ onlineDeadline time.Duration
+ maxOnlineTime time.Duration
hs *noise.HandshakeState
csOur *noise.CipherState
csTheir *noise.CipherState
payloads chan []byte
+ pings chan struct{}
infosTheir map[[32]byte]*SPInfo
infosOurSeen map[[32]byte]uint8
queueTheir []*FreqWithNice
wg sync.WaitGroup
RxBytes int64
RxLastSeen time.Time
+ RxLastNonPing time.Time
TxBytes int64
TxLastSeen time.Time
+ TxLastNonPing time.Time
started time.Time
+ mustFinishAt time.Time
Duration time.Duration
RxSpeed int64
TxSpeed int64
xxOnly TRxTx
rxRate int
txRate int
- isDead bool
+ isDead chan struct{}
listOnly bool
onlyPkts map[[32]byte]bool
+ writeSPBuf bytes.Buffer
sync.RWMutex
}
-func (state *SPState) NotAlive() bool {
- if state.isDead {
- return true
+func (state *SPState) SetDead() {
+ state.Lock()
+ defer state.Unlock()
+ select {
+ case <-state.isDead:
+ // Already closed channel, dead
+ return
+ default:
}
- now := time.Now()
- if state.maxOnlineTime > 0 && state.started.Add(time.Duration(state.maxOnlineTime)*time.Second).Before(now) {
+ close(state.isDead)
+ go func() {
+ for _ = range state.payloads {
+ }
+ }()
+ go func() {
+ for _ = range state.pings {
+ }
+ }()
+}
+
+func (state *SPState) NotAlive() bool {
+ select {
+ case <-state.isDead:
return true
+ default:
}
- return uint(now.Sub(state.RxLastSeen).Seconds()) >= state.onlineDeadline &&
- uint(now.Sub(state.TxLastSeen).Seconds()) >= state.onlineDeadline
+ return false
}
func (state *SPState) dirUnlock() {
state.Ctx.UnlockDir(state.txLock)
}
-func (state *SPState) WriteSP(dst io.Writer, payload []byte) error {
- n, err := xdr.Marshal(dst, SPRaw{Magic: MagicNNCPLv1, Payload: payload})
- if err == nil {
+func (state *SPState) WriteSP(dst io.Writer, payload []byte, ping bool) error {
+ state.writeSPBuf.Reset()
+ n, err := xdr.Marshal(&state.writeSPBuf, SPRaw{
+ Magic: MagicNNCPLv1,
+ Payload: payload,
+ })
+ if err != nil {
+ return err
+ }
+ if n, err = dst.Write(state.writeSPBuf.Bytes()); err == nil {
state.TxLastSeen = time.Now()
state.TxBytes += int64(n)
+ if !ping {
+ state.TxLastNonPing = state.TxLastSeen
+ }
}
return err
}
}
var rxLock *os.File
if !state.listOnly && (state.xxOnly == "" || state.xxOnly == TRx) {
- rxLock, err = state.Ctx.LockDir(nodeId, TRx)
+ rxLock, err = state.Ctx.LockDir(nodeId, string(TRx))
if err != nil {
return err
}
}
var txLock *os.File
if !state.listOnly && (state.xxOnly == "" || state.xxOnly == TTx) {
- txLock, err = state.Ctx.LockDir(nodeId, TTx)
+ txLock, err = state.Ctx.LockDir(nodeId, string(TTx))
if err != nil {
return err
}
}
state.hs = hs
state.payloads = make(chan []byte)
+ state.pings = make(chan struct{})
state.infosTheir = make(map[[32]byte]*SPInfo)
state.infosOurSeen = make(map[[32]byte]uint8)
state.started = started
}
sds := SDS{"node": nodeId, "nice": int(state.Nice)}
state.Ctx.LogD("sp-start", sds, "sending first message")
- conn.SetWriteDeadline(time.Now().Add(DefaultDeadline * time.Second))
- if err = state.WriteSP(conn, buf); err != nil {
+ conn.SetWriteDeadline(time.Now().Add(DefaultDeadline))
+ if err = state.WriteSP(conn, buf, false); err != nil {
state.Ctx.LogE("sp-start", sds, err, "")
state.dirUnlock()
return err
}
state.Ctx.LogD("sp-start", sds, "waiting for first message")
- conn.SetReadDeadline(time.Now().Add(DefaultDeadline * time.Second))
+ conn.SetReadDeadline(time.Now().Add(DefaultDeadline))
if buf, err = state.ReadSP(conn); err != nil {
state.Ctx.LogE("sp-start", sds, err, "")
state.dirUnlock()
if err != nil {
state.Ctx.LogE("sp-start", sds, err, "")
state.dirUnlock()
- return err
}
return err
}
xxOnly := TRxTx("")
state.hs = hs
state.payloads = make(chan []byte)
+ state.pings = make(chan struct{})
state.infosOurSeen = make(map[[32]byte]uint8)
state.infosTheir = make(map[[32]byte]*SPInfo)
state.started = started
var buf []byte
var payload []byte
state.Ctx.LogD("sp-start", SDS{"nice": int(state.Nice)}, "waiting for first message")
- conn.SetReadDeadline(time.Now().Add(DefaultDeadline * time.Second))
+ conn.SetReadDeadline(time.Now().Add(DefaultDeadline))
if buf, err = state.ReadSP(conn); err != nil {
state.Ctx.LogE("sp-start", SDS{}, err, "")
return err
}
var rxLock *os.File
if xxOnly == "" || xxOnly == TRx {
- rxLock, err = state.Ctx.LockDir(node.Id, TRx)
+ rxLock, err = state.Ctx.LockDir(node.Id, string(TRx))
if err != nil {
return err
}
state.rxLock = rxLock
var txLock *os.File
if xxOnly == "" || xxOnly == TTx {
- txLock, err = state.Ctx.LockDir(node.Id, TTx)
+ txLock, err = state.Ctx.LockDir(node.Id, string(TTx))
if err != nil {
return err
}
state.dirUnlock()
return err
}
- conn.SetWriteDeadline(time.Now().Add(DefaultDeadline * time.Second))
- if err = state.WriteSP(conn, buf); err != nil {
+ conn.SetWriteDeadline(time.Now().Add(DefaultDeadline))
+ if err = state.WriteSP(conn, buf, false); err != nil {
state.Ctx.LogE("sp-start", sds, err, "")
state.dirUnlock()
return err
err = state.StartWorkers(conn, infosPayloads, payload)
if err != nil {
state.dirUnlock()
- return err
}
return err
}
func (state *SPState) StartWorkers(
conn ConnDeadlined,
infosPayloads [][]byte,
- payload []byte) error {
+ payload []byte,
+) error {
sds := SDS{"node": state.Node.Id, "nice": int(state.Nice)}
+ state.isDead = make(chan struct{})
+ if state.maxOnlineTime > 0 {
+ state.mustFinishAt = state.started.Add(state.maxOnlineTime)
+ }
+
+ // Remaining handshake payload sending
if len(infosPayloads) > 1 {
+ state.wg.Add(1)
go func() {
for _, payload := range infosPayloads[1:] {
state.Ctx.LogD(
)
state.payloads <- payload
}
+ state.wg.Done()
}()
}
+
+ // Processing of first payload and queueing its responses
state.Ctx.LogD(
"sp-work",
SdsAdd(sds, SDS{"size": len(payload)}),
state.Ctx.LogE("sp-work", sds, err, "")
return err
}
-
+ state.wg.Add(1)
go func() {
for _, reply := range replies {
state.Ctx.LogD(
)
state.payloads <- reply
}
+ state.wg.Done()
+ }()
+
+ // Periodic jobs
+ state.wg.Add(1)
+ go func() {
+ deadlineTicker := time.NewTicker(time.Second)
+ pingTicker := time.NewTicker(PingTimeout)
+ for {
+ select {
+ case <-state.isDead:
+ state.wg.Done()
+ deadlineTicker.Stop()
+ pingTicker.Stop()
+ return
+ case now := <-deadlineTicker.C:
+ if (now.Sub(state.RxLastNonPing) >= state.onlineDeadline &&
+ now.Sub(state.TxLastNonPing) >= state.onlineDeadline) ||
+ (state.maxOnlineTime > 0 && state.mustFinishAt.Before(now)) ||
+ (now.Sub(state.RxLastSeen) >= 2*PingTimeout) {
+ state.SetDead()
+ conn.Close()
+ }
+ case now := <-pingTicker.C:
+ if now.After(state.TxLastSeen.Add(PingTimeout)) {
+ state.wg.Add(1)
+ go func() {
+ state.pings <- struct{}{}
+ state.wg.Done()
+ state.Ctx.LogD("HERE", SDS{}, "PING GOROUTINE QUIT")
+ }()
+ }
+ }
+ }
}()
+ // Spool checker and INFOs sender of appearing files
if !state.listOnly && (state.xxOnly == "" || state.xxOnly == TTx) {
+ state.wg.Add(1)
go func() {
- for range time.Tick(time.Second) {
- if state.NotAlive() {
+ ticker := time.NewTicker(time.Second)
+ for {
+ select {
+ case <-state.isDead:
+ state.wg.Done()
+ ticker.Stop()
return
- }
- for _, payload := range state.Ctx.infosOur(
- state.Node.Id,
- state.Nice,
- &state.infosOurSeen,
- ) {
- state.Ctx.LogD(
- "sp-work",
- SdsAdd(sds, SDS{"size": len(payload)}),
- "queuing new info",
- )
- state.payloads <- payload
+ case <-ticker.C:
+ for _, payload := range state.Ctx.infosOur(
+ state.Node.Id,
+ state.Nice,
+ &state.infosOurSeen,
+ ) {
+ state.Ctx.LogD(
+ "sp-work",
+ SdsAdd(sds, SDS{"size": len(payload)}),
+ "queuing new info",
+ )
+ state.payloads <- payload
+ }
}
}
}()
}
+ // Sender
state.wg.Add(1)
go func() {
- defer func() {
- state.isDead = true
- state.wg.Done()
- }()
+ defer conn.Close()
+ defer state.SetDead()
+ defer state.wg.Done()
for {
if state.NotAlive() {
return
}
var payload []byte
+ var ping bool
select {
+ case <-state.pings:
+ state.Ctx.LogD("sp-xmit", sds, "got ping")
+ payload = SPPingMarshalized
+ ping = true
case payload = <-state.payloads:
state.Ctx.LogD(
"sp-xmit",
"got payload",
)
default:
- }
- if payload == nil {
state.RLock()
if len(state.queueTheir) == 0 {
- state.Ctx.LogD("sp-xmit", sds, "file queue is empty")
state.RUnlock()
time.Sleep(100 * time.Millisecond)
continue
}
freq := state.queueTheir[0].freq
state.RUnlock()
-
if state.txRate > 0 {
time.Sleep(time.Second / time.Duration(state.txRate))
}
-
sdsp := SdsAdd(sds, SDS{
"xx": string(TTx),
"pkt": ToBase32(freq.Hash[:]),
))
if err != nil {
state.Ctx.LogE("sp-file", sdsp, err, "")
- break
+ return
}
fi, err := fd.Stat()
if err != nil {
state.Ctx.LogE("sp-file", sdsp, err, "")
- break
+ return
}
fullSize := fi.Size()
var buf []byte
state.Ctx.LogD("sp-file", sdsp, "seeking")
if _, err = fd.Seek(int64(freq.Offset), io.SeekStart); err != nil {
state.Ctx.LogE("sp-file", sdsp, err, "")
- break
+ return
}
buf = make([]byte, MaxSPSize-SPHeadOverhead-SPFileOverhead)
n, err := fd.Read(buf)
if err != nil {
state.Ctx.LogE("sp-file", sdsp, err, "")
- break
+ return
}
buf = buf[:n]
- state.Ctx.LogD(
- "sp-file",
- SdsAdd(sdsp, SDS{"size": n}),
- "read",
- )
+ state.Ctx.LogD("sp-file", SdsAdd(sdsp, SDS{"size": n}), "read")
}
fd.Close()
payload = MarshalSP(SPTypeFile, SPFile{
sdsp["size"] = int64(ourSize)
sdsp["fullsize"] = fullSize
if state.Ctx.ShowPrgrs {
- Progress(sdsp)
+ Progress("Tx", sdsp)
}
state.Lock()
if len(state.queueTheir) > 0 && *state.queueTheir[0].freq.Hash == *freq.Hash {
}
state.Unlock()
}
- state.Ctx.LogD(
- "sp-xmit",
- SdsAdd(sds, SDS{"size": len(payload)}),
- "sending",
- )
- conn.SetWriteDeadline(time.Now().Add(DefaultDeadline * time.Second))
- if err := state.WriteSP(conn, state.csOur.Encrypt(nil, nil, payload)); err != nil {
+ state.Ctx.LogD("sp-xmit", SdsAdd(sds, SDS{"size": len(payload)}), "sending")
+ conn.SetWriteDeadline(time.Now().Add(DefaultDeadline))
+ if err := state.WriteSP(conn, state.csOur.Encrypt(nil, nil, payload), ping); err != nil {
state.Ctx.LogE("sp-xmit", sds, err, "")
- break
+ return
}
}
}()
+ // Receiver
state.wg.Add(1)
go func() {
- defer func() {
- state.isDead = true
- state.wg.Done()
- }()
for {
if state.NotAlive() {
- return
+ break
}
state.Ctx.LogD("sp-recv", sds, "waiting for payload")
- conn.SetReadDeadline(time.Now().Add(DefaultDeadline * time.Second))
+ conn.SetReadDeadline(time.Now().Add(DefaultDeadline))
payload, err := state.ReadSP(conn)
if err != nil {
if err == io.EOF {
state.Ctx.LogE("sp-recv", sds, err, "")
break
}
+ state.wg.Add(1)
go func() {
for _, reply := range replies {
state.Ctx.LogD(
)
state.payloads <- reply
}
+ state.wg.Done()
}()
if state.rxRate > 0 {
time.Sleep(time.Second / time.Duration(state.rxRate))
}
}
+ state.SetDead()
+ state.wg.Done()
+ state.SetDead()
+ conn.Close()
}()
return nil
func (state *SPState) Wait() {
state.wg.Wait()
+ close(state.payloads)
+ close(state.pings)
state.dirUnlock()
state.Duration = time.Now().Sub(state.started)
state.RxSpeed = state.RxBytes
return nil, err
}
switch head.Type {
+ case SPTypeHalt:
+ state.Ctx.LogD("sp-process", SdsAdd(sds, SDS{"type": "halt"}), "")
+ state.Lock()
+ state.queueTheir = nil
+ state.Unlock()
+ case SPTypePing:
+ state.Ctx.LogD("sp-process", SdsAdd(sds, SDS{"type": "ping"}), "")
case SPTypeInfo:
infosGot = true
sdsp := SdsAdd(sds, SDS{"type": "info"})
fd.Close()
return nil, err
}
- ourSize := file.Offset + uint64(len(file.Payload))
+ ourSize := int64(file.Offset + uint64(len(file.Payload)))
+ sdsp["size"] = ourSize
+ fullsize := int64(0)
state.RLock()
- sdsp["size"] = int64(ourSize)
- sdsp["fullsize"] = int64(state.infosTheir[*file.Hash].Size)
+ infoTheir, ok := state.infosTheir[*file.Hash]
+ state.RUnlock()
+ if ok {
+ fullsize = int64(infoTheir.Size)
+ }
+ sdsp["fullsize"] = fullsize
if state.Ctx.ShowPrgrs {
- Progress(sdsp)
+ Progress("Rx", sdsp)
}
- if state.infosTheir[*file.Hash].Size != ourSize {
- state.RUnlock()
+ if fullsize != ourSize {
fd.Close()
continue
}
- state.RUnlock()
spWorkersGroup.Wait()
spWorkersGroup.Add(1)
go func() {
delete(state.infosTheir, *file.Hash)
state.Unlock()
spWorkersGroup.Done()
+ state.wg.Add(1)
go func() {
state.payloads <- MarshalSP(SPTypeDone, SPDone{file.Hash})
+ state.wg.Done()
}()
}()
case SPTypeDone:
} else {
state.Ctx.LogD("sp-process", sdsp, "unknown")
}
- case SPTypeHalt:
- state.Ctx.LogD("sp-process", SdsAdd(sds, SDS{"type": "halt"}), "")
- state.Lock()
- state.queueTheir = nil
- state.Unlock()
default:
state.Ctx.LogE(
"sp-process",
)
return nil, BadPktType
}
+ if head.Type != SPTypePing {
+ state.RxLastNonPing = state.RxLastSeen
+ }
}
if infosGot {
var pkts int
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
nice uint8,
dryRun, doSeen, noFile, noFreq, noExec, noTrns bool,
) bool {
+ dirLock, err := ctx.LockDir(nodeId, "toss")
+ if err != nil {
+ ctx.LogE("rx", SDS{}, err, "lock")
+ return false
+ }
+ defer ctx.UnlockDir(dirLock)
isBad := false
sendmail := ctx.Neigh[*ctx.SelfId].Exec["sendmail"]
decompressor, err := zstd.NewReader(nil)
ctx.LogD("rx", sds, "created")
bufW := bufio.NewWriter(tmp)
if _, err = CopyProgressed(
- bufW,
- pipeR,
+ bufW, pipeR, "Rx file",
SdsAdd(sds, SDS{"fullsize": sds["size"]}),
ctx.ShowPrgrs,
); err != nil {
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
}
go func() {
_, err := CopyProgressed(
- tmp.W, pipeR,
- SDS{"xx": string(TTx), "pkt": pktName, "fullsize": curSize},
+ tmp.W, pipeR, "Tx",
+ SDS{"pkt": pktName, "fullsize": curSize},
ctx.ShowPrgrs,
)
errs <- err
if err != nil {
return err
}
- if _, err = CopyProgressed(tmp.W, src, SDS{
- "xx": string(TTx),
- "pkt": node.Id.String(),
- "fullsize": size,
- }, ctx.ShowPrgrs); err != nil {
+ if _, err = CopyProgressed(
+ tmp.W, src, "Tx trns",
+ SDS{"pkt": node.Id.String(), "fullsize": size},
+ ctx.ShowPrgrs,
+ ); err != nil {
return err
}
nodePath := filepath.Join(ctx.Spool, node.Id.String())
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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
/*
NNCP -- Node to Node copy, utilities for store-and-forward data exchange
-Copyright (C) 2016-2019 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2016-2020 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