]> Cypherpunks.ru repositories - nncp.git/blob - src/toss_test.go
Move arguments to TossOpts
[nncp.git] / src / toss_test.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2023 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package nncp
19
20 import (
21         "bytes"
22         "crypto/rand"
23         "fmt"
24         "io"
25         "os"
26         "path/filepath"
27         "strconv"
28         "strings"
29         "testing"
30         "testing/quick"
31
32         xdr "github.com/davecgh/go-xdr/xdr2"
33 )
34
35 var (
36         TDebug bool = false
37 )
38
39 func dirFiles(path string) []string {
40         dir, err := os.Open(path)
41         if err != nil {
42                 panic(err)
43         }
44         defer dir.Close()
45         names, err := dir.Readdirnames(0)
46         if err != nil {
47                 panic(err)
48         }
49         return names
50 }
51
52 func TestTossExec(t *testing.T) {
53         f := func(replyNice uint8, handleRaw uint32, recipients [16]uint8) bool {
54                 handle := strconv.Itoa(int(handleRaw))
55                 for i, recipient := range recipients {
56                         recipients[i] = recipient % 8
57                 }
58                 spool, err := os.MkdirTemp("", "testtoss")
59                 if err != nil {
60                         panic(err)
61                 }
62                 defer os.RemoveAll(spool)
63                 nodeOur, err := NewNodeGenerate()
64                 if err != nil {
65                         t.Error(err)
66                         return false
67                 }
68                 ctx := Ctx{
69                         Spool:   spool,
70                         Self:    nodeOur,
71                         SelfId:  nodeOur.Id,
72                         Neigh:   make(map[NodeId]*Node),
73                         Alias:   make(map[string]*NodeId),
74                         LogPath: filepath.Join(spool, "log.log"),
75                         Debug:   TDebug,
76                 }
77                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
78                 privates := make(map[uint8]*NodeOur)
79                 for _, recipient := range recipients {
80                         if _, exists := privates[recipient]; exists {
81                                 continue
82                         }
83                         our, err := NewNodeGenerate()
84                         if err != nil {
85                                 t.Error(err)
86                                 return false
87                         }
88                         privates[recipient] = our
89                         ctx.Neigh[*our.Id] = our.Their()
90                 }
91                 for _, recipient := range recipients {
92                         if err := ctx.TxExec(
93                                 ctx.Neigh[*privates[recipient].Id],
94                                 DefaultNiceExec,
95                                 replyNice,
96                                 handle,
97                                 []string{"arg0", "arg1"},
98                                 strings.NewReader("BODY\n"),
99                                 1<<15, MaxFileSize,
100                                 false,
101                                 nil,
102                         ); err != nil {
103                                 t.Error(err)
104                                 return false
105                         }
106                 }
107                 for _, recipient := range recipients {
108                         ctx.Self = privates[recipient]
109                         rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
110                         os.Rename(filepath.Join(spool, ctx.Self.Id.String(), string(TTx)), rxPath)
111                         if len(dirFiles(rxPath)) == 0 {
112                                 continue
113                         }
114                         ctx.Toss(ctx.Self.Id, TRx, &TossOpts{Nice: DefaultNiceExec - 1})
115                         if len(dirFiles(rxPath)) == 0 {
116                                 return false
117                         }
118                         ctx.Neigh[*nodeOur.Id].Exec = make(map[string][]string)
119                         ctx.Neigh[*nodeOur.Id].Exec[handle] = []string{"/bin/sh", "-c", "false"}
120                         ctx.Toss(ctx.Self.Id, TRx, &TossOpts{Nice: DefaultNiceExec})
121                         if len(dirFiles(rxPath)) == 0 {
122                                 return false
123                         }
124                         ctx.Neigh[*nodeOur.Id].Exec[handle] = []string{
125                                 "/bin/sh", "-c",
126                                 fmt.Sprintf(
127                                         "echo $NNCP_NICE $0 $1 >> %s ; cat >> %s",
128                                         filepath.Join(spool, "mbox"),
129                                         filepath.Join(spool, "mbox"),
130                                 ),
131                         }
132                         ctx.Toss(ctx.Self.Id, TRx, &TossOpts{Nice: DefaultNiceExec})
133                         if len(dirFiles(rxPath)) != 0 {
134                                 return false
135                         }
136                 }
137                 mbox, err := os.ReadFile(filepath.Join(spool, "mbox"))
138                 if err != nil {
139                         return false
140                 }
141                 expected := make([]byte, 0, 16)
142                 for i := 0; i < 16; i++ {
143                         expected = append(
144                                 expected,
145                                 []byte(fmt.Sprintf("%d arg0 arg1\n", replyNice))...,
146                         )
147                         expected = append(expected, []byte("BODY\n")...)
148                 }
149                 return bytes.Equal(mbox, expected)
150         }
151         if err := quick.Check(f, nil); err != nil {
152                 t.Error(err)
153         }
154 }
155
156 func TestTossFile(t *testing.T) {
157         f := func(fileSizes []uint8) bool {
158                 if len(fileSizes) == 0 {
159                         return true
160                 }
161                 files := make(map[string][]byte)
162                 for i, fileSize := range fileSizes {
163                         if fileSize == 0 {
164                                 // to prevent chunked send
165                                 fileSize++
166                         }
167                         data := make([]byte, fileSize)
168                         if _, err := io.ReadFull(rand.Reader, data); err != nil {
169                                 panic(err)
170                         }
171                         files[strconv.Itoa(i)] = data
172                 }
173                 spool, err := os.MkdirTemp("", "testtoss")
174                 if err != nil {
175                         panic(err)
176                 }
177                 defer os.RemoveAll(spool)
178                 nodeOur, err := NewNodeGenerate()
179                 if err != nil {
180                         t.Error(err)
181                         return false
182                 }
183                 ctx := Ctx{
184                         Spool:   spool,
185                         Self:    nodeOur,
186                         SelfId:  nodeOur.Id,
187                         Neigh:   make(map[NodeId]*Node),
188                         Alias:   make(map[string]*NodeId),
189                         LogPath: filepath.Join(spool, "log.log"),
190                         Debug:   TDebug,
191                 }
192                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
193                 incomingPath := filepath.Join(spool, "incoming")
194                 for _, fileData := range files {
195                         hasher := MTHNew(0, 0)
196                         hasher.Write(fileData)
197                         fileName := Base32Codec.EncodeToString(hasher.Sum(nil))
198                         src := filepath.Join(spool, fileName)
199                         if err := os.WriteFile(src, fileData, os.FileMode(0600)); err != nil {
200                                 panic(err)
201                         }
202                         if err := ctx.TxFile(
203                                 ctx.Neigh[*nodeOur.Id],
204                                 DefaultNiceFile,
205                                 src,
206                                 fileName,
207                                 MaxFileSize,
208                                 1<<15,
209                                 MaxFileSize,
210                                 nil,
211                         ); err != nil {
212                                 t.Error(err)
213                                 return false
214                         }
215                 }
216                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
217                 os.Rename(filepath.Join(spool, ctx.Self.Id.String(), string(TTx)), rxPath)
218                 ctx.Toss(ctx.Self.Id, TRx, &TossOpts{Nice: DefaultNiceFile})
219                 if len(dirFiles(rxPath)) == 0 {
220                         return false
221                 }
222                 ctx.Neigh[*nodeOur.Id].Incoming = &incomingPath
223                 if ctx.Toss(ctx.Self.Id, TRx, &TossOpts{Nice: DefaultNiceFile}) {
224                         return false
225                 }
226                 if len(dirFiles(rxPath)) != 0 {
227                         return false
228                 }
229                 for _, fileData := range files {
230                         hasher := MTHNew(0, 0)
231                         hasher.Write(fileData)
232                         fileName := Base32Codec.EncodeToString(hasher.Sum(nil))
233                         data, err := os.ReadFile(filepath.Join(incomingPath, fileName))
234                         if err != nil {
235                                 panic(err)
236                         }
237                         if !bytes.Equal(data, fileData) {
238                                 return false
239                         }
240                 }
241                 return true
242         }
243         if err := quick.Check(f, nil); err != nil {
244                 t.Error(err)
245         }
246 }
247
248 func TestTossFileSameName(t *testing.T) {
249         f := func(filesRaw uint8) bool {
250                 files := int(filesRaw)%8 + 1
251                 spool, err := os.MkdirTemp("", "testtoss")
252                 if err != nil {
253                         panic(err)
254                 }
255                 defer os.RemoveAll(spool)
256                 nodeOur, err := NewNodeGenerate()
257                 if err != nil {
258                         t.Error(err)
259                         return false
260                 }
261                 ctx := Ctx{
262                         Spool:   spool,
263                         Self:    nodeOur,
264                         SelfId:  nodeOur.Id,
265                         Neigh:   make(map[NodeId]*Node),
266                         Alias:   make(map[string]*NodeId),
267                         LogPath: filepath.Join(spool, "log.log"),
268                         Debug:   TDebug,
269                 }
270                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
271                 srcPath := filepath.Join(spool, "junk")
272                 if err = os.WriteFile(
273                         srcPath,
274                         []byte("doesnotmatter"),
275                         os.FileMode(0600),
276                 ); err != nil {
277                         t.Error(err)
278                         return false
279                 }
280                 incomingPath := filepath.Join(spool, "incoming")
281                 for i := 0; i < files; i++ {
282                         if err := ctx.TxFile(
283                                 ctx.Neigh[*nodeOur.Id],
284                                 DefaultNiceFile,
285                                 srcPath,
286                                 "samefile",
287                                 MaxFileSize,
288                                 1<<15,
289                                 MaxFileSize,
290                                 nil,
291                         ); err != nil {
292                                 t.Error(err)
293                                 return false
294                         }
295                 }
296                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
297                 os.Rename(filepath.Join(spool, ctx.Self.Id.String(), string(TTx)), rxPath)
298                 ctx.Neigh[*nodeOur.Id].Incoming = &incomingPath
299                 ctx.Toss(ctx.Self.Id, TRx, &TossOpts{Nice: DefaultNiceFile})
300                 expected := make(map[string]struct{})
301                 expected["samefile"] = struct{}{}
302                 for i := 0; i < files-1; i++ {
303                         expected["samefile."+strconv.Itoa(i)] = struct{}{}
304                 }
305                 for _, filename := range dirFiles(incomingPath) {
306                         if _, exists := expected[filename]; !exists {
307                                 return false
308                         }
309                         delete(expected, filename)
310                 }
311                 return len(expected) == 0
312         }
313         if err := quick.Check(f, nil); err != nil {
314                 t.Error(err)
315         }
316 }
317
318 func TestTossFreq(t *testing.T) {
319         f := func(fileSizes []uint8, replyNice uint8) bool {
320                 if len(fileSizes) == 0 {
321                         return true
322                 }
323                 spool, err := os.MkdirTemp("", "testtoss")
324                 if err != nil {
325                         panic(err)
326                 }
327                 defer os.RemoveAll(spool)
328                 nodeOur, err := NewNodeGenerate()
329                 if err != nil {
330                         t.Error(err)
331                         return false
332                 }
333                 ctx := Ctx{
334                         Spool:   spool,
335                         Self:    nodeOur,
336                         SelfId:  nodeOur.Id,
337                         Neigh:   make(map[NodeId]*Node),
338                         Alias:   make(map[string]*NodeId),
339                         LogPath: filepath.Join(spool, "log.log"),
340                         Debug:   TDebug,
341                 }
342                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
343                 files := make(map[string][]byte)
344                 for i, fileSize := range fileSizes {
345                         if fileSize == 0 {
346                                 // to prevent chunked send
347                                 fileSize++
348                         }
349                         fileData := make([]byte, fileSize)
350                         if _, err := io.ReadFull(rand.Reader, fileData); err != nil {
351                                 panic(err)
352                         }
353                         fileName := strconv.Itoa(i)
354                         files[fileName] = fileData
355                         if err := ctx.TxFreq(
356                                 ctx.Neigh[*nodeOur.Id],
357                                 DefaultNiceFreq,
358                                 replyNice,
359                                 fileName,
360                                 fileName,
361                                 1<<15,
362                         ); err != nil {
363                                 t.Error(err)
364                                 return false
365                         }
366                 }
367                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
368                 txPath := filepath.Join(spool, ctx.Self.Id.String(), string(TTx))
369                 os.Rename(txPath, rxPath)
370                 os.MkdirAll(txPath, os.FileMode(0700))
371                 ctx.Toss(ctx.Self.Id, TRx, &TossOpts{Nice: DefaultNiceFreq})
372                 if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 {
373                         return false
374                 }
375                 ctx.Neigh[*nodeOur.Id].FreqPath = &spool
376                 ctx.Toss(ctx.Self.Id, TRx, &TossOpts{Nice: DefaultNiceFreq})
377                 if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 {
378                         return false
379                 }
380                 for fileName, fileData := range files {
381                         if err := os.WriteFile(
382                                 filepath.Join(spool, fileName),
383                                 fileData,
384                                 os.FileMode(0600),
385                         ); err != nil {
386                                 panic(err)
387                         }
388                 }
389                 ctx.Toss(ctx.Self.Id, TRx, &TossOpts{Nice: DefaultNiceFreq})
390                 if len(dirFiles(txPath)) == 0 || len(dirFiles(rxPath)) != 0 {
391                         return false
392                 }
393                 for job := range ctx.Jobs(ctx.Self.Id, TTx) {
394                         var buf bytes.Buffer
395                         fd, err := os.Open(job.Path)
396                         if err != nil {
397                                 t.Error(err)
398                                 return false
399                         }
400                         _, _, _, err = PktEncRead(ctx.Self, ctx.Neigh, fd, &buf, true, nil)
401                         if err != nil {
402                                 t.Error(err)
403                                 return false
404                         }
405                         var pkt Pkt
406                         if _, err = xdr.Unmarshal(&buf, &pkt); err != nil {
407                                 t.Error(err)
408                                 return false
409                         }
410                         if pkt.Nice != replyNice {
411                                 return false
412                         }
413                         if !bytes.Equal(buf.Bytes(), files[string(pkt.Path[:int(pkt.PathLen)])]) {
414                                 return false
415                         }
416                 }
417                 return true
418         }
419         if err := quick.Check(f, nil); err != nil {
420                 t.Error(err)
421         }
422 }
423
424 func TestTossTrns(t *testing.T) {
425         f := func(datumLens []uint8) bool {
426                 if len(datumLens) == 0 {
427                         return true
428                 }
429                 datum := make(map[int][]byte)
430                 for i, datumLen := range datumLens {
431                         datumLen += 64
432                         data := make([]byte, datumLen)
433                         if _, err := io.ReadFull(rand.Reader, data); err != nil {
434                                 panic(err)
435                         }
436                         datum[i] = data
437                 }
438                 spool, err := os.MkdirTemp("", "testtoss")
439                 if err != nil {
440                         panic(err)
441                 }
442                 defer os.RemoveAll(spool)
443                 nodeOur, err := NewNodeGenerate()
444                 if err != nil {
445                         t.Error(err)
446                         return false
447                 }
448                 ctx := Ctx{
449                         Spool:   spool,
450                         Self:    nodeOur,
451                         SelfId:  nodeOur.Id,
452                         Neigh:   make(map[NodeId]*Node),
453                         Alias:   make(map[string]*NodeId),
454                         LogPath: filepath.Join(spool, "log.log"),
455                         Debug:   TDebug,
456                 }
457                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
458                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
459                 os.MkdirAll(rxPath, os.FileMode(0700))
460                 txPath := filepath.Join(spool, ctx.Self.Id.String(), string(TTx))
461                 os.MkdirAll(txPath, os.FileMode(0700))
462                 for _, data := range datum {
463                         pktTrans := Pkt{
464                                 Magic:   MagicNNCPPv3.B,
465                                 Type:    PktTypeTrns,
466                                 PathLen: MTHSize,
467                         }
468                         copy(pktTrans.Path[:], nodeOur.Id[:])
469                         var dst bytes.Buffer
470                         if _, _, err := PktEncWrite(
471                                 ctx.Self,
472                                 ctx.Neigh[*nodeOur.Id],
473                                 &pktTrans,
474                                 123,
475                                 0, MaxFileSize, 1,
476                                 bytes.NewReader(data),
477                                 &dst,
478                         ); err != nil {
479                                 t.Error(err)
480                                 return false
481                         }
482                         hasher := MTHNew(0, 0)
483                         hasher.Write(dst.Bytes())
484                         if err := os.WriteFile(
485                                 filepath.Join(rxPath, Base32Codec.EncodeToString(hasher.Sum(nil))),
486                                 dst.Bytes(),
487                                 os.FileMode(0600),
488                         ); err != nil {
489                                 panic(err)
490                         }
491                 }
492                 ctx.Toss(ctx.Self.Id, TRx, &TossOpts{Nice: 123})
493                 if len(dirFiles(rxPath)) != 0 {
494                         return false
495                 }
496                 for _, filename := range dirFiles(txPath) {
497                         dataRead, err := os.ReadFile(filepath.Join(txPath, filename))
498                         if err != nil {
499                                 panic(err)
500                         }
501                         for k, data := range datum {
502                                 if bytes.Equal(dataRead, data) {
503                                         delete(datum, k)
504                                 }
505                         }
506                 }
507                 return len(datum) == 0
508         }
509         if err := quick.Check(f, nil); err != nil {
510                 t.Error(err)
511         }
512 }