$ nncp-ack [options] -all
$ nncp-ack [options] -node NODE[,@dots{}]
$ nncp-ack [options] -node NODE -pkt PKT
+
+$ nncp-ack [@dots{}] 4>&1 >&2 | nncp-rm [@dots{}] -pkt
@end example
Send acknowledgement of successful @option{PKT} (Base32-encoded hash)
specified, then acknowledge all packet in node's @code{rx} outbound
spool. If @option{-all} is specified, then do that for all nodes.
+That commands outputs list of created encrypted ACK packets
+(@code{NODE/PKT}) to @strong{4}th file descriptor. That output can be
+passed for example to @command{@ref{nncp-rm}} to remove them after
+transmission to not wait for acknowledgement and retransmission.
+
General workflow with acknowledgement is following, assuming that
Alice has some outbound packets for Bob:
That will also check if copied packets checksum is not mismatched.
-@item Create ACK packets of received ones:
+@item Create ACK packets of received ones, saving the list of encrypted
+ ACK packets:
@example
-bob$ nncp-ack -node alice
+bob$ nncp-ack -node alice 4>acks
@end example
@item Send those newly created packets back to Alice:
@example
-bob$ nncp-xfer -tx /mnt/shared
+bob$ nncp-xfer [-keep] -tx /mnt/shared
+@end example
+
+@item Remove them from outbound spool, because we expect no
+ acknowledgement for them:
+
+@example
+bob$ nncp-rm -node alice -pkt <acks
@end example
@item Get those acknowledgement packets and @ref{nncp-toss, toss} them:
@option{-dump} option dumps current configuration file to the
@ref{Configuration directory, directory layout} at @file{/path/to/dir}.
@option{-load} loads it and parses, outputing the resulting Hjson to
-stdout.
+@code{stdout}.
$ nncp-hash [-file @dots{}] [-seek X] [-debug] [-progress]
@end example
-Calculate @ref{MTH} hash of either stdin, or @option{-file} if
+Calculate @ref{MTH} hash of either @code{stdin}, or @option{-file} if
specified.
You can optionally force seeking the file first, reading only part of
$ nncp-rm [options] @{-all|-node NODE@} -hdr
$ nncp-rm [options] @{-all|-node NODE@} -area
$ nncp-rm [options] @{-all|-node NODE@} [-rx] [-tx]
-$ nncp-rm [options] @{-all|-node NODE@} -pkt PKT
+$ nncp-rm [options] @{-all|-node NODE@} -pkt <<EOF
+PKT1
+PKT2
+NODEx/PKT3
+@dots{}
+EOF
@end example
This command is aimed to delete various files from your spool directory:
@item If @option{-lock} option is specified, then all @file{.lock} files
will be deleted in your spool directory.
-@item If @option{-pkt} option is specified, then @file{PKT} packet (its
-Base32 name) will be deleted. This is useful when you see some packet
-failing to be processed.
+@item If @option{-pkt} option is specified, then list of packets (Base32
+names) toe be deleted is read from @code{stdin}. This could be useful
+when you see some packet failing to be processed. Packet identifiers
+could have "directories" prepended, that are ignored.
@item When either @option{-rx} or @option{-tx} options are specified
(maybe both of them), then delete all packets from that given queues.
@multitable {XXXXX} {XXXX-XX-XX} {XXXX KiB} {meta4 link sig} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
@headitem Version @tab Date @tab Size @tab Tarball @tab SHA256 checksum
+@item @ref{Release 8_6_0, 8.6.0} @tab 2022-03-02 @tab 1670 KiB
+@tab
+ @url{download/nncp-8.6.0.tar.xz.meta4, meta4}
+ @url{download/nncp-8.6.0.tar.xz, link}
+ @url{download/nncp-8.6.0.tar.xz.sig, sig}
+@tab @code{AE16F0A0 9C1B7D1D A3767E1C 0F410D9C 29ACCAE6 32A448A9 55DB4A0F 15BF838F}
+
@item @ref{Release 8_5_0, 8.5.0} @tab 2022-01-26 @tab 1685 KiB
@tab
@url{download/nncp-8.5.0.tar.xz.meta4, meta4}
@node Новости
@section Новости
+@node Релиз 8.7.0
+@subsection Релиз 8.7.0
+@itemize
+
+@item
+@command{nncp-ack} не подтверждает ACK-пакеты, предотвращая бесконечную
+петлю из ACK-ов.
+
+@item
+В прошлом, @command{nncp-ack} не удаляла соответствующие @file{hdr/} файлы.
+
+@item
+@command{nncp-rm} теперь берёт список пакетов из @code{stdin}, при
+использовании @option{-pkt} опции.
+
+@item
+@command{nncp-ack} теперь генерирует список ACK пакетов, которые были
+созданы, что может использоваться в качестве ввода для @command{nncp-rm}
+команды, чтобы удалить исходящие ACK пакеты.
+
+@end itemize
+
@node Релиз 8.6.0
@subsection Релиз 8.6.0
@itemize
@item
Исправлена работоспособность @command{nncp-file} и @command{nncp-exec}
-команд использующих временный файл (stdin и @option{-use-tmp}).
+команд использующих временный файл (@code{stdin} и @option{-use-tmp}).
@item
Исправлен пропадающий плохой код возврата в @command{nncp-exec} команде.
See also this page @ref{Новости, on russian}.
+@node Release 8_7_0
+@section Release 8.7.0
+@itemize
+
+@item
+@command{nncp-ack} does not acknowledge ACK-packets, preventing an
+endless loop of ACKs.
+
+@item
+@command{nncp-ack} previously did not remove corresponding @file{hdr/} files.
+
+@item
+@command{nncp-rm} now takes list of packet from @code{stdin} when
+@option{-pkt} option is used.
+
+@item
+@command{nncp-ack} now generates list of ACK packets it created, that
+could be used as an input to @command{nncp-rm} to remove outbound ACK
+packets.
+
+@end itemize
+
@node Release 8_6_0
@section Release 8.6.0
@itemize
@item
Fixed workability of @command{nncp-file} and @command{nncp-exec}
-commands, that use temporary file (stdin and @option{-use-tmp}).
+commands, that use temporary file (@code{stdin} and @option{-use-tmp}).
@item
Fixed disappearing bad return code in @command{nncp-exec} command.
PORTNAME= nncp
-DISTVERSION= 8.6.0
+DISTVERSION= 8.7.0
CATEGORIES= net
MASTER_SITES= http://www.nncpgo.org/download/
package main
import (
+ "bufio"
+ "errors"
"flag"
"fmt"
+ "io"
"log"
"os"
"path/filepath"
"strings"
+ xdr "github.com/davecgh/go-xdr/xdr2"
"go.cypherpunks.ru/nncp/v8"
)
fmt.Fprintf(os.Stderr, nncp.UsageHeader())
fmt.Fprintf(os.Stderr, "nncp-ack -- send packet receipt acknowledgement\n\n")
fmt.Fprintf(os.Stderr, "Usage: %s [options] -all\n", os.Args[0])
- fmt.Fprintf(os.Stderr, "Usage: %s -node NODE[,...]\n", os.Args[0])
- fmt.Fprintf(os.Stderr, "Usage: %s -node NODE -pkt PKT\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, "Usage: %s [options] -node NODE[,...]\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, "Usage: %s [options] -node NODE -pkt PKT\n", os.Args[0])
fmt.Fprintln(os.Stderr, "Options:")
flag.PrintDefaults()
}
os.Exit(1)
}
+ acksCreated := os.NewFile(uintptr(4), "ACKsCreated")
+ if acksCreated == nil {
+ log.Fatalln("can not open FD:4")
+ }
+
if *pktRaw != "" {
if len(nodes) != 1 {
usage()
os.Exit(1)
}
nncp.ViaOverride(*viaOverride, ctx, nodes[0])
- if err = ctx.TxACK(nodes[0], nice, *pktRaw, minSize); err != nil {
+ pktName, err := ctx.TxACK(nodes[0], nice, *pktRaw, minSize)
+ if err != nil {
log.Fatalln(err)
}
+ acksCreated.WriteString(nodes[0].Id.String() + "/" + pktName + "\n")
return
}
+ isBad := false
for _, node := range nodes {
for job := range ctx.Jobs(node.Id, nncp.TRx) {
pktName := filepath.Base(job.Path)
- if err = ctx.TxACK(node, nice, pktName, minSize); err != nil {
+ sender := ctx.Neigh[*job.PktEnc.Sender]
+ les := nncp.LEs{
+ {K: "Node", V: job.PktEnc.Sender},
+ {K: "Pkt", V: pktName},
+ }
+ logMsg := func(les nncp.LEs) string {
+ return fmt.Sprintf(
+ "ACKing %s/%s",
+ ctx.NodeName(job.PktEnc.Sender), pktName,
+ )
+ }
+ if sender == nil {
+ err := errors.New("unknown node")
+ ctx.LogE("ack-read", les, err, logMsg)
+ isBad = true
+ continue
+ }
+ fd, err := os.Open(job.Path)
+ if err != nil {
+ ctx.LogE("ack-read-open", les, err, func(les nncp.LEs) string {
+ return logMsg(les) + ": opening" + job.Path
+ })
+ isBad = true
+ continue
+ }
+ pktEnc, _, err := ctx.HdrRead(fd)
+ if err != nil {
+ fd.Close()
+ ctx.LogE("ack-read-read", les, err, func(les nncp.LEs) string {
+ return logMsg(les) + ": reading" + job.Path
+ })
+ isBad = true
+ continue
+ }
+ switch pktEnc.Magic {
+ case nncp.MagicNNCPEv1.B:
+ err = nncp.MagicNNCPEv1.TooOld()
+ case nncp.MagicNNCPEv2.B:
+ err = nncp.MagicNNCPEv2.TooOld()
+ case nncp.MagicNNCPEv3.B:
+ err = nncp.MagicNNCPEv3.TooOld()
+ case nncp.MagicNNCPEv4.B:
+ err = nncp.MagicNNCPEv4.TooOld()
+ case nncp.MagicNNCPEv5.B:
+ err = nncp.MagicNNCPEv5.TooOld()
+ case nncp.MagicNNCPEv6.B:
+ default:
+ err = errors.New("is not an encrypted packet")
+ }
+ if err != nil {
+ fd.Close()
+ ctx.LogE("ack-read-magic", les, err, logMsg)
+ isBad = true
+ continue
+ }
+ if _, err = fd.Seek(0, io.SeekStart); err != nil {
+ fd.Close()
+ ctx.LogE("ack-read-seek", les, err, func(les nncp.LEs) string {
+ return logMsg(les) + ": seeking"
+ })
+ isBad = true
+ continue
+ }
+ pipeR, pipeW := io.Pipe()
+ go nncp.PktEncRead(ctx.Self, ctx.Neigh, bufio.NewReader(fd), pipeW, true, nil)
+ var pkt nncp.Pkt
+ _, err = xdr.Unmarshal(pipeR, &pkt)
+ fd.Close()
+ pipeW.Close()
+ if err != nil {
+ ctx.LogE("ack-read-unmarshal", les, err, func(les nncp.LEs) string {
+ return logMsg(les) + ": unmarshal"
+ })
+ isBad = true
+ continue
+ }
+ if pkt.Type == nncp.PktTypeACK {
+ ctx.LogI("ack-read-if-ack", les, func(les nncp.LEs) string {
+ return logMsg(les) + ": it is ACK, skipping"
+ })
+ continue
+ }
+ newPktName, err := ctx.TxACK(node, nice, pktName, minSize)
+ if err != nil {
log.Fatalln(err)
}
+ acksCreated.WriteString(node.Id.String() + "/" + newPktName + "\n")
}
}
+ if isBad {
+ os.Exit(1)
+ }
}
import (
"flag"
"fmt"
+ "io"
"log"
"os"
"path/filepath"
fmt.Fprintf(os.Stderr, " %s [options] {-all|-node NODE} -hdr\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s [options] {-all|-node NODE} -area\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s [options] {-all|-node NODE} {-rx|-tx}\n", os.Args[0])
- fmt.Fprintf(os.Stderr, " %s [options] {-all|-node NODE} -pkt PKT\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, " %s [options] {-all|-node NODE} -pkt < ...\n", os.Args[0])
fmt.Fprintln(os.Stderr, "-older option's time units are: (s)econds, (m)inutes, (h)ours, (d)ays")
fmt.Fprintln(os.Stderr, "Options:")
flag.PrintDefaults()
doArea = flag.Bool("area", false, "Remove only area/* seen files")
older = flag.String("older", "", "XXX{smhd}: only older than XXX number of time units")
dryRun = flag.Bool("dryrun", false, "Do not actually remove files")
- pktRaw = flag.String("pkt", "", "Packet to remove")
+ doPkt = flag.Bool("pkt", false, "Packet to remove TODO")
spoolPath = flag.String("spool", "", "Override path to spool")
quiet = flag.Bool("quiet", false, "Print only errors")
debug = flag.Bool("debug", false, "Print debug messages")
}
oldBoundary := time.Second * time.Duration(oldBoundaryRaw)
+ pkts := make(map[string]struct{})
+ if *doPkt {
+ raw, err := io.ReadAll(os.Stdin)
+ if err != nil {
+ log.Fatalln("can not read -pkt from stdin:", err)
+ }
+ for _, line := range strings.Split(string(raw), "\n") {
+ if len(line) == 0 {
+ continue
+ }
+ cols := strings.Split(line, "/")
+ pkts[cols[len(cols)-1]] = struct{}{}
+ }
+ }
+
now := time.Now()
if *doTmp {
err = filepath.Walk(
}
return os.Remove(path)
}
- if *pktRaw != "" && filepath.Base(info.Name()) == *pktRaw {
- ctx.LogI("rm", nncp.LEs{{K: "File", V: path}}, logMsg)
- if *dryRun {
- return nil
+ if len(pkts) > 0 {
+ if _, exists := pkts[filepath.Base(info.Name())]; exists {
+ ctx.LogI("rm", nncp.LEs{{K: "File", V: path}}, logMsg)
+ if *dryRun {
+ return nil
+ }
+ return os.Remove(path)
}
- return os.Remove(path)
}
if !*doSeen && !*doNoCK && !*doHdr && !*doPart &&
(*doRx || *doTx) &&
return nil
})
}
- if *pktRaw != "" || *doRx || *doNoCK || *doPart {
+ if len(pkts) > 0 || *doRx || *doNoCK || *doPart {
if err = remove(nncp.TRx); err != nil {
log.Fatalln("Can not remove:", err)
}
}
- if *pktRaw != "" || *doTx || *doHdr {
+ if len(pkts) > 0 || *doTx || *doHdr {
if err = remove(nncp.TTx); err != nil {
log.Fatalln("Can not remove:", err)
}
return nil
}
if now.Sub(info.ModTime()) < oldBoundary {
- ctx.LogD("rm-skip", nncp.LEs{{K: "File", V: path}}, func(les nncp.LEs) string {
- return fmt.Sprintf("File %s: too fresh, skipping", path)
- })
+ ctx.LogD(
+ "rm-skip", nncp.LEs{{K: "File", V: path}},
+ func(les nncp.LEs) string {
+ return fmt.Sprintf("File %s: too fresh, skipping", path)
+ },
+ )
return nil
}
ctx.LogI(
if err != nil {
panic(err)
}
- if _, _, err = ctx.Tx(
+ if _, _, _, err = ctx.Tx(
node,
pktTrns,
nice,
const Base32Encoded32Len = 52
var (
- Version string = "8.6.0"
+ Version string = "8.7.0"
Base32Codec *base32.Encoding = base32.StdEncoding.WithPadding(base32.NoPadding)
)
if err != nil {
panic(err)
}
- if _, _, err = ctx.Tx(
+ if _, _, _, err = ctx.Tx(
node,
pktTrns,
nice,
}
if nodeId != sender.Id && nodeId != pktEnc.Sender {
ctx.LogI("rx-area-echo", lesEcho, logMsgNode)
- if _, _, err = ctx.Tx(
+ if _, _, _, err = ctx.Tx(
node,
&pkt,
nice,
return logMsg(les) + ": removing packet"
})
return err
+ } else if ctx.HdrUsage {
+ os.Remove(JobPath2Hdr(pktPath))
}
}
} else {
src io.Reader,
pktName string,
areaId *AreaId,
-) (*Node, int64, error) {
+) (*Node, int64, string, error) {
var area *Area
if areaId != nil {
area = ctx.AreaId2Area[*areaId]
if area.Prv == nil {
- return nil, 0, errors.New("area has no encryption keys")
+ return nil, 0, "", errors.New("area has no encryption keys")
}
}
hops := make([]*Node, 0, 1+len(node.Via))
expectedSize += sizePadCalc(expectedSize, minSize, wrappers)
expectedSize = PktEncOverhead + sizeWithTags(expectedSize)
if maxSize != 0 && expectedSize > maxSize {
- return nil, 0, TooBig
+ return nil, 0, "", TooBig
}
if !ctx.IsEnoughSpace(expectedSize) {
- return nil, 0, errors.New("is not enough space")
+ return nil, 0, "", errors.New("is not enough space")
}
}
tmp, err := ctx.NewTmpFileWHash()
if err != nil {
- return nil, 0, err
+ return nil, 0, "", err
}
results := make(chan PktEncWriteResult)
r := <-results
if r.err != nil {
tmp.Fd.Close()
- return nil, 0, r.err
+ return nil, 0, "", r.err
}
if r.pktEncRaw != nil {
pktEncRaw = r.pktEncRaw
err = tmp.Commit(filepath.Join(nodePath, string(TTx)))
os.Symlink(nodePath, filepath.Join(ctx.Spool, lastNode.Name))
if err != nil {
- return lastNode, 0, err
+ return lastNode, 0, "", err
}
if ctx.HdrUsage {
ctx.HdrWrite(pktEncRaw, filepath.Join(nodePath, string(TTx), tmp.Checksum()))
}
if err = ensureDir(seenDir); err != nil {
ctx.LogE("tx-mkdir", les, err, logMsg)
- return lastNode, 0, err
+ return lastNode, 0, "", err
}
if fd, err := os.Create(seenPath); err == nil {
fd.Close()
if err = DirSync(seenDir); err != nil {
ctx.LogE("tx-dirsync", les, err, logMsg)
- return lastNode, 0, err
+ return lastNode, 0, "", err
}
}
ctx.LogI("tx-area", les, logMsg)
}
- return lastNode, payloadSize, err
+ return lastNode, payloadSize, tmp.Checksum(), err
}
type DummyCloser struct{}
if err != nil {
return err
}
- _, finalSize, err := ctx.Tx(
+ _, finalSize, pktName, err := ctx.Tx(
node, pkt, nice,
srcSize, minSize, maxSize,
bufio.NewReader(reader), dstPath, areaId,
{"Src", srcPath},
{"Dst", dstPath},
{"Size", finalSize},
+ {"Pkt", pktName},
}
logMsg := func(les LEs) string {
return fmt.Sprintf(
return err
}
hsh := MTHNew(0, 0)
- _, size, err := ctx.Tx(
+ _, size, pktName, err := ctx.Tx(
node, pkt, nice,
0, minSize, maxSize,
io.TeeReader(lr, hsh),
{"Src", srcPath},
{"Dst", path},
{"Size", size},
+ {"Pkt", pktName},
}
logMsg := func(les LEs) string {
return fmt.Sprintf(
return err
}
metaPktSize := int64(buf.Len())
- _, _, err = ctx.Tx(
+ _, _, pktName, err := ctx.Tx(
node,
pkt,
nice,
{"Src", srcPath},
{"Dst", path},
{"Size", metaPktSize},
+ {"Pkt", pktName},
}
logMsg := func(les LEs) string {
return fmt.Sprintf(
}
src := strings.NewReader(dstPath)
size := int64(src.Len())
- _, _, err = ctx.Tx(node, pkt, nice, size, minSize, MaxFileSize, src, srcPath, nil)
+ _, _, pktName, err := ctx.Tx(
+ node, pkt, nice, size, minSize, MaxFileSize, src, srcPath, nil,
+ )
les := LEs{
{"Type", "freq"},
{"Node", node.Id},
{"ReplyNice", int(replyNice)},
{"Src", srcPath},
{"Dst", dstPath},
+ {"Pkt", pktName},
}
logMsg := func(les LEs) string {
return fmt.Sprintf(
}(in)
in = pr
}
- _, size, err := ctx.Tx(node, pkt, nice, 0, minSize, maxSize, in, handle, areaId)
+ _, size, pktName, err := ctx.Tx(
+ node, pkt, nice, 0, minSize, maxSize, in, handle, areaId,
+ )
if !noCompress {
e := <-compressErr
if err == nil {
{"ReplyNice", int(replyNice)},
{"Dst", dst},
{"Size", size},
+ {"Pkt", pktName},
}
logMsg := func(les LEs) string {
return fmt.Sprintf(
nice uint8,
hsh string,
minSize int64,
-) error {
+) (pktName string, err error) {
hshRaw, err := Base32Codec.DecodeString(hsh)
if err != nil {
- return err
+ return "", err
}
if len(hshRaw) != MTHSize {
- return errors.New("Invalid packet id size")
+ return "", errors.New("Invalid packet id size")
}
pkt, err := NewPkt(PktTypeACK, nice, []byte(hshRaw))
if err != nil {
- return err
+ return "", err
}
src := bytes.NewReader([]byte{})
- _, _, err = ctx.Tx(node, pkt, nice, 0, minSize, MaxFileSize, src, hsh, nil)
+ _, _, pktName, err = ctx.Tx(
+ node, pkt, nice, 0, minSize, MaxFileSize, src, hsh, nil,
+ )
les := LEs{
{"Type", "ack"},
{"Node", node.Id},
{"Nice", int(nice)},
{"Pkt", hsh},
+ {"NewPkt", pktName},
}
logMsg := func(les LEs) string {
return fmt.Sprintf("ACK to %s of %s is sent", ctx.NodeName(node.Id), hsh)
} else {
ctx.LogE("tx", les, err, logMsg)
}
- return err
+ return
}
}
pkt, err := NewPkt(PktTypeExec, replyNice, []byte(pathSrc))
src := bytes.NewReader(data)
- dstNode, _, err := ctx.Tx(
+ dstNode, _, _, err := ctx.Tx(
nodeTgt,
pkt,
123,