1/*
2NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3Copyright (C) 2016-2021 Sergey Matveev <stargrave@stargrave.org>
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, version 3 of the License.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program.  If not, see <http://www.gnu.org/licenses/>.
16*/
17
18// Parse raw NNCP packet.
19package main
20
21import (
22	"bufio"
23	"bytes"
24	"flag"
25	"fmt"
26	"io"
27	"log"
28	"os"
29
30	xdr "github.com/davecgh/go-xdr/xdr2"
31	"github.com/klauspost/compress/zstd"
32	"go.cypherpunks.ru/nncp/v8"
33)
34
35func usage() {
36	fmt.Fprintf(os.Stderr, nncp.UsageHeader())
37	fmt.Fprintf(os.Stderr, "nncp-pkt -- parse raw packet\n\n")
38	fmt.Fprintf(os.Stderr, "Usage: %s [options]\nOptions:\n", os.Args[0])
39	flag.PrintDefaults()
40	fmt.Fprintln(os.Stderr, "Packet is read from stdin.")
41}
42
43func doPlain(ctx *nncp.Ctx, pkt nncp.Pkt, dump, decompress bool) {
44	if dump {
45		bufW := bufio.NewWriter(os.Stdout)
46		var r io.Reader
47		r = bufio.NewReader(os.Stdin)
48		if decompress {
49			decompressor, err := zstd.NewReader(r)
50			if err != nil {
51				log.Fatalln(err)
52			}
53			r = decompressor
54		}
55		if _, err := io.Copy(bufW, r); err != nil {
56			log.Fatalln(err)
57		}
58		if err := bufW.Flush(); err != nil {
59			log.Fatalln(err)
60		}
61		return
62	}
63	payloadType := "unknown"
64	switch pkt.Type {
65	case nncp.PktTypeFile:
66		payloadType = "file"
67	case nncp.PktTypeFreq:
68		payloadType = "file request"
69	case nncp.PktTypeExec:
70		payloadType = "exec compressed"
71	case nncp.PktTypeTrns:
72		payloadType = "transitional"
73	case nncp.PktTypeExecFat:
74		payloadType = "exec uncompressed"
75	case nncp.PktTypeArea:
76		payloadType = "area"
77	}
78	var path string
79	switch pkt.Type {
80	case nncp.PktTypeExec, nncp.PktTypeExecFat:
81		path = string(bytes.Replace(
82			pkt.Path[:pkt.PathLen], []byte{0}, []byte(" "), -1,
83		))
84	case nncp.PktTypeTrns:
85		path = nncp.Base32Codec.EncodeToString(pkt.Path[:pkt.PathLen])
86		node, err := ctx.FindNode(path)
87		if err == nil {
88			path = fmt.Sprintf("%s (%s)", path, node.Name)
89		}
90	case nncp.PktTypeArea:
91		path = nncp.Base32Codec.EncodeToString(pkt.Path[:pkt.PathLen])
92		if areaId, err := nncp.AreaIdFromString(path); err == nil {
93			path = fmt.Sprintf("%s (%s)", path, ctx.AreaName(areaId))
94		}
95	default:
96		path = string(pkt.Path[:pkt.PathLen])
97	}
98	fmt.Printf(
99		"Packet type: plain\nPayload type: %s\nNiceness: %s (%d)\nPath: %s\n",
100		payloadType, nncp.NicenessFmt(pkt.Nice), pkt.Nice, path,
101	)
102	return
103}
104
105func doEncrypted(
106	ctx *nncp.Ctx,
107	pktEnc nncp.PktEnc,
108	dump bool,
109	beginning []byte,
110) {
111	senderName := "unknown"
112	senderNode := ctx.Neigh[*pktEnc.Sender]
113	if senderNode != nil {
114		senderName = senderNode.Name
115	}
116
117	recipientName := "unknown"
118	var area *nncp.Area
119	recipientNode := ctx.Neigh[*pktEnc.Recipient]
120	if recipientNode == nil {
121		area = ctx.AreaId2Area[nncp.AreaId(*pktEnc.Recipient)]
122		if area != nil {
123			recipientName = "area " + area.Name
124		}
125	} else {
126		recipientName = recipientNode.Name
127	}
128
129	if !dump {
130		fmt.Printf(`Packet type: encrypted
131Niceness: %s (%d)
132Sender: %s (%s)
133Recipient: %s (%s)
134`,
135			nncp.NicenessFmt(pktEnc.Nice), pktEnc.Nice,
136			pktEnc.Sender, senderName,
137			pktEnc.Recipient, recipientName,
138		)
139		return
140	}
141	if ctx.Self == nil {
142		log.Fatalln("Config lacks private keys")
143	}
144	bufW := bufio.NewWriter(os.Stdout)
145	var err error
146	if area == nil {
147		_, _, _, err = nncp.PktEncRead(
148			ctx.Self, ctx.Neigh,
149			io.MultiReader(bytes.NewReader(beginning), bufio.NewReader(os.Stdin)),
150			bufW, senderNode != nil, nil,
151		)
152	} else {
153		areaNode := nncp.NodeOur{Id: new(nncp.NodeId), ExchPrv: new([32]byte)}
154		copy(areaNode.Id[:], area.Id[:])
155		copy(areaNode.ExchPrv[:], area.Prv[:])
156		_, _, _, err = nncp.PktEncRead(
157			&areaNode, ctx.Neigh,
158			io.MultiReader(bytes.NewReader(beginning), bufio.NewReader(os.Stdin)),
159			bufW, senderNode != nil, nil,
160		)
161	}
162	if err != nil {
163		log.Fatalln(err)
164	}
165	if err = bufW.Flush(); err != nil {
166		log.Fatalln(err)
167	}
168}
169
170func main() {
171	var (
172		overheads  = flag.Bool("overheads", false, "Print packet overheads")
173		dump       = flag.Bool("dump", false, "Write decrypted/parsed payload to stdout")
174		decompress = flag.Bool("decompress", false, "Try to zstd decompress dumped data")
175		cfgPath    = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
176		version    = flag.Bool("version", false, "Print version information")
177		warranty   = flag.Bool("warranty", false, "Print warranty information")
178	)
179	log.SetFlags(log.Lshortfile)
180	flag.Usage = usage
181	flag.Parse()
182	if *warranty {
183		fmt.Println(nncp.Warranty)
184		return
185	}
186	if *version {
187		fmt.Println(nncp.VersionGet())
188		return
189	}
190
191	ctx, err := nncp.CtxFromCmdline(*cfgPath, "", "", false, false, false, false)
192	if err != nil {
193		log.Fatalln("Error during initialization:", err)
194	}
195
196	if *overheads {
197		fmt.Printf(
198			"Plain: %d\nEncrypted: %d\nSize: %d\n",
199			nncp.PktOverhead,
200			nncp.PktEncOverhead,
201			nncp.PktSizeOverhead,
202		)
203		return
204	}
205
206	beginning := make([]byte, nncp.PktOverhead)
207	if _, err := io.ReadFull(os.Stdin, beginning[:nncp.PktEncOverhead]); err != nil {
208		log.Fatalln("Not enough data to read")
209	}
210	var pktEnc nncp.PktEnc
211	if _, err := xdr.Unmarshal(bytes.NewReader(beginning), &pktEnc); err == nil {
212		switch pktEnc.Magic {
213		case nncp.MagicNNCPEv1.B:
214			log.Fatalln(nncp.MagicNNCPEv1.TooOld())
215		case nncp.MagicNNCPEv2.B:
216			log.Fatalln(nncp.MagicNNCPEv2.TooOld())
217		case nncp.MagicNNCPEv3.B:
218			log.Fatalln(nncp.MagicNNCPEv3.TooOld())
219		case nncp.MagicNNCPEv4.B:
220			log.Fatalln(nncp.MagicNNCPEv4.TooOld())
221		case nncp.MagicNNCPEv5.B:
222			log.Fatalln(nncp.MagicNNCPEv5.TooOld())
223		case nncp.MagicNNCPEv6.B:
224			doEncrypted(ctx, pktEnc, *dump, beginning[:nncp.PktEncOverhead])
225			return
226		}
227	}
228
229	if _, err := io.ReadFull(os.Stdin, beginning[nncp.PktEncOverhead:]); err != nil {
230		log.Fatalln("Not enough data to read")
231	}
232	var pkt nncp.Pkt
233	if _, err := xdr.Unmarshal(bytes.NewReader(beginning), &pkt); err == nil {
234		switch pkt.Magic {
235		case nncp.MagicNNCPPv1.B:
236			log.Fatalln(nncp.MagicNNCPPv1.TooOld())
237		case nncp.MagicNNCPPv2.B:
238			log.Fatalln(nncp.MagicNNCPPv2.TooOld())
239		case nncp.MagicNNCPPv3.B:
240			doPlain(ctx, pkt, *dump, *decompress)
241			return
242		}
243	}
244	log.Fatalln("Unable to determine packet type")
245}
246