]> Cypherpunks.ru repositories - nncp.git/blob - src/toss_test.go
ioutil is deprecated since 1.16/1.17
[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, DefaultNiceExec-1,
115                                 false, false, false, false, false, false, false, false)
116                         if len(dirFiles(rxPath)) == 0 {
117                                 return false
118                         }
119                         ctx.Neigh[*nodeOur.Id].Exec = make(map[string][]string)
120                         ctx.Neigh[*nodeOur.Id].Exec[handle] = []string{"/bin/sh", "-c", "false"}
121                         ctx.Toss(ctx.Self.Id, TRx, DefaultNiceExec,
122                                 false, false, false, false, false, false, false, false)
123                         if len(dirFiles(rxPath)) == 0 {
124                                 return false
125                         }
126                         ctx.Neigh[*nodeOur.Id].Exec[handle] = []string{
127                                 "/bin/sh", "-c",
128                                 fmt.Sprintf(
129                                         "echo $NNCP_NICE $0 $1 >> %s ; cat >> %s",
130                                         filepath.Join(spool, "mbox"),
131                                         filepath.Join(spool, "mbox"),
132                                 ),
133                         }
134                         ctx.Toss(ctx.Self.Id, TRx, DefaultNiceExec,
135                                 false, false, false, false, false, false, false, false)
136                         if len(dirFiles(rxPath)) != 0 {
137                                 return false
138                         }
139                 }
140                 mbox, err := os.ReadFile(filepath.Join(spool, "mbox"))
141                 if err != nil {
142                         return false
143                 }
144                 expected := make([]byte, 0, 16)
145                 for i := 0; i < 16; i++ {
146                         expected = append(
147                                 expected,
148                                 []byte(fmt.Sprintf("%d arg0 arg1\n", replyNice))...,
149                         )
150                         expected = append(expected, []byte("BODY\n")...)
151                 }
152                 return bytes.Equal(mbox, expected)
153         }
154         if err := quick.Check(f, nil); err != nil {
155                 t.Error(err)
156         }
157 }
158
159 func TestTossFile(t *testing.T) {
160         f := func(fileSizes []uint8) bool {
161                 if len(fileSizes) == 0 {
162                         return true
163                 }
164                 files := make(map[string][]byte)
165                 for i, fileSize := range fileSizes {
166                         if fileSize == 0 {
167                                 // to prevent chunked send
168                                 fileSize++
169                         }
170                         data := make([]byte, fileSize)
171                         if _, err := io.ReadFull(rand.Reader, data); err != nil {
172                                 panic(err)
173                         }
174                         files[strconv.Itoa(i)] = data
175                 }
176                 spool, err := os.MkdirTemp("", "testtoss")
177                 if err != nil {
178                         panic(err)
179                 }
180                 defer os.RemoveAll(spool)
181                 nodeOur, err := NewNodeGenerate()
182                 if err != nil {
183                         t.Error(err)
184                         return false
185                 }
186                 ctx := Ctx{
187                         Spool:   spool,
188                         Self:    nodeOur,
189                         SelfId:  nodeOur.Id,
190                         Neigh:   make(map[NodeId]*Node),
191                         Alias:   make(map[string]*NodeId),
192                         LogPath: filepath.Join(spool, "log.log"),
193                         Debug:   TDebug,
194                 }
195                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
196                 incomingPath := filepath.Join(spool, "incoming")
197                 for _, fileData := range files {
198                         hasher := MTHNew(0, 0)
199                         hasher.Write(fileData)
200                         fileName := Base32Codec.EncodeToString(hasher.Sum(nil))
201                         src := filepath.Join(spool, fileName)
202                         if err := os.WriteFile(src, fileData, os.FileMode(0600)); err != nil {
203                                 panic(err)
204                         }
205                         if err := ctx.TxFile(
206                                 ctx.Neigh[*nodeOur.Id],
207                                 DefaultNiceFile,
208                                 src,
209                                 fileName,
210                                 MaxFileSize,
211                                 1<<15,
212                                 MaxFileSize,
213                                 nil,
214                         ); err != nil {
215                                 t.Error(err)
216                                 return false
217                         }
218                 }
219                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
220                 os.Rename(filepath.Join(spool, ctx.Self.Id.String(), string(TTx)), rxPath)
221                 ctx.Toss(ctx.Self.Id, TRx, DefaultNiceFile,
222                         false, false, false, false, false, false, false, false)
223                 if len(dirFiles(rxPath)) == 0 {
224                         return false
225                 }
226                 ctx.Neigh[*nodeOur.Id].Incoming = &incomingPath
227                 if ctx.Toss(ctx.Self.Id, TRx, DefaultNiceFile,
228                         false, false, false, false, false, false, false, false) {
229                         return false
230                 }
231                 if len(dirFiles(rxPath)) != 0 {
232                         return false
233                 }
234                 for _, fileData := range files {
235                         hasher := MTHNew(0, 0)
236                         hasher.Write(fileData)
237                         fileName := Base32Codec.EncodeToString(hasher.Sum(nil))
238                         data, err := os.ReadFile(filepath.Join(incomingPath, fileName))
239                         if err != nil {
240                                 panic(err)
241                         }
242                         if !bytes.Equal(data, fileData) {
243                                 return false
244                         }
245                 }
246                 return true
247         }
248         if err := quick.Check(f, nil); err != nil {
249                 t.Error(err)
250         }
251 }
252
253 func TestTossFileSameName(t *testing.T) {
254         f := func(filesRaw uint8) bool {
255                 files := int(filesRaw)%8 + 1
256                 spool, err := os.MkdirTemp("", "testtoss")
257                 if err != nil {
258                         panic(err)
259                 }
260                 defer os.RemoveAll(spool)
261                 nodeOur, err := NewNodeGenerate()
262                 if err != nil {
263                         t.Error(err)
264                         return false
265                 }
266                 ctx := Ctx{
267                         Spool:   spool,
268                         Self:    nodeOur,
269                         SelfId:  nodeOur.Id,
270                         Neigh:   make(map[NodeId]*Node),
271                         Alias:   make(map[string]*NodeId),
272                         LogPath: filepath.Join(spool, "log.log"),
273                         Debug:   TDebug,
274                 }
275                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
276                 srcPath := filepath.Join(spool, "junk")
277                 if err = os.WriteFile(
278                         srcPath,
279                         []byte("doesnotmatter"),
280                         os.FileMode(0600),
281                 ); err != nil {
282                         t.Error(err)
283                         return false
284                 }
285                 incomingPath := filepath.Join(spool, "incoming")
286                 for i := 0; i < files; i++ {
287                         if err := ctx.TxFile(
288                                 ctx.Neigh[*nodeOur.Id],
289                                 DefaultNiceFile,
290                                 srcPath,
291                                 "samefile",
292                                 MaxFileSize,
293                                 1<<15,
294                                 MaxFileSize,
295                                 nil,
296                         ); err != nil {
297                                 t.Error(err)
298                                 return false
299                         }
300                 }
301                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
302                 os.Rename(filepath.Join(spool, ctx.Self.Id.String(), string(TTx)), rxPath)
303                 ctx.Neigh[*nodeOur.Id].Incoming = &incomingPath
304                 ctx.Toss(ctx.Self.Id, TRx, DefaultNiceFile,
305                         false, false, false, false, false, false, false, false)
306                 expected := make(map[string]struct{})
307                 expected["samefile"] = struct{}{}
308                 for i := 0; i < files-1; i++ {
309                         expected["samefile."+strconv.Itoa(i)] = struct{}{}
310                 }
311                 for _, filename := range dirFiles(incomingPath) {
312                         if _, exists := expected[filename]; !exists {
313                                 return false
314                         }
315                         delete(expected, filename)
316                 }
317                 return len(expected) == 0
318         }
319         if err := quick.Check(f, nil); err != nil {
320                 t.Error(err)
321         }
322 }
323
324 func TestTossFreq(t *testing.T) {
325         f := func(fileSizes []uint8, replyNice uint8) bool {
326                 if len(fileSizes) == 0 {
327                         return true
328                 }
329                 spool, err := os.MkdirTemp("", "testtoss")
330                 if err != nil {
331                         panic(err)
332                 }
333                 defer os.RemoveAll(spool)
334                 nodeOur, err := NewNodeGenerate()
335                 if err != nil {
336                         t.Error(err)
337                         return false
338                 }
339                 ctx := Ctx{
340                         Spool:   spool,
341                         Self:    nodeOur,
342                         SelfId:  nodeOur.Id,
343                         Neigh:   make(map[NodeId]*Node),
344                         Alias:   make(map[string]*NodeId),
345                         LogPath: filepath.Join(spool, "log.log"),
346                         Debug:   TDebug,
347                 }
348                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
349                 files := make(map[string][]byte)
350                 for i, fileSize := range fileSizes {
351                         if fileSize == 0 {
352                                 // to prevent chunked send
353                                 fileSize++
354                         }
355                         fileData := make([]byte, fileSize)
356                         if _, err := io.ReadFull(rand.Reader, fileData); err != nil {
357                                 panic(err)
358                         }
359                         fileName := strconv.Itoa(i)
360                         files[fileName] = fileData
361                         if err := ctx.TxFreq(
362                                 ctx.Neigh[*nodeOur.Id],
363                                 DefaultNiceFreq,
364                                 replyNice,
365                                 fileName,
366                                 fileName,
367                                 1<<15,
368                         ); err != nil {
369                                 t.Error(err)
370                                 return false
371                         }
372                 }
373                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
374                 txPath := filepath.Join(spool, ctx.Self.Id.String(), string(TTx))
375                 os.Rename(txPath, rxPath)
376                 os.MkdirAll(txPath, os.FileMode(0700))
377                 ctx.Toss(ctx.Self.Id, TRx, DefaultNiceFreq,
378                         false, false, false, false, false, false, false, false)
379                 if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 {
380                         return false
381                 }
382                 ctx.Neigh[*nodeOur.Id].FreqPath = &spool
383                 ctx.Toss(ctx.Self.Id, TRx, DefaultNiceFreq,
384                         false, false, false, false, false, false, false, false)
385                 if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 {
386                         return false
387                 }
388                 for fileName, fileData := range files {
389                         if err := os.WriteFile(
390                                 filepath.Join(spool, fileName),
391                                 fileData,
392                                 os.FileMode(0600),
393                         ); err != nil {
394                                 panic(err)
395                         }
396                 }
397                 ctx.Toss(ctx.Self.Id, TRx, DefaultNiceFreq,
398                         false, false, false, false, false, false, false, false)
399                 if len(dirFiles(txPath)) == 0 || len(dirFiles(rxPath)) != 0 {
400                         return false
401                 }
402                 for job := range ctx.Jobs(ctx.Self.Id, TTx) {
403                         var buf bytes.Buffer
404                         fd, err := os.Open(job.Path)
405                         if err != nil {
406                                 t.Error(err)
407                                 return false
408                         }
409                         _, _, _, err = PktEncRead(ctx.Self, ctx.Neigh, fd, &buf, true, nil)
410                         if err != nil {
411                                 t.Error(err)
412                                 return false
413                         }
414                         var pkt Pkt
415                         if _, err = xdr.Unmarshal(&buf, &pkt); err != nil {
416                                 t.Error(err)
417                                 return false
418                         }
419                         if pkt.Nice != replyNice {
420                                 return false
421                         }
422                         if !bytes.Equal(buf.Bytes(), files[string(pkt.Path[:int(pkt.PathLen)])]) {
423                                 return false
424                         }
425                 }
426                 return true
427         }
428         if err := quick.Check(f, nil); err != nil {
429                 t.Error(err)
430         }
431 }
432
433 func TestTossTrns(t *testing.T) {
434         f := func(datumLens []uint8) bool {
435                 if len(datumLens) == 0 {
436                         return true
437                 }
438                 datum := make(map[int][]byte)
439                 for i, datumLen := range datumLens {
440                         datumLen += 64
441                         data := make([]byte, datumLen)
442                         if _, err := io.ReadFull(rand.Reader, data); err != nil {
443                                 panic(err)
444                         }
445                         datum[i] = data
446                 }
447                 spool, err := os.MkdirTemp("", "testtoss")
448                 if err != nil {
449                         panic(err)
450                 }
451                 defer os.RemoveAll(spool)
452                 nodeOur, err := NewNodeGenerate()
453                 if err != nil {
454                         t.Error(err)
455                         return false
456                 }
457                 ctx := Ctx{
458                         Spool:   spool,
459                         Self:    nodeOur,
460                         SelfId:  nodeOur.Id,
461                         Neigh:   make(map[NodeId]*Node),
462                         Alias:   make(map[string]*NodeId),
463                         LogPath: filepath.Join(spool, "log.log"),
464                         Debug:   TDebug,
465                 }
466                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
467                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
468                 os.MkdirAll(rxPath, os.FileMode(0700))
469                 txPath := filepath.Join(spool, ctx.Self.Id.String(), string(TTx))
470                 os.MkdirAll(txPath, os.FileMode(0700))
471                 for _, data := range datum {
472                         pktTrans := Pkt{
473                                 Magic:   MagicNNCPPv3.B,
474                                 Type:    PktTypeTrns,
475                                 PathLen: MTHSize,
476                         }
477                         copy(pktTrans.Path[:], nodeOur.Id[:])
478                         var dst bytes.Buffer
479                         if _, _, err := PktEncWrite(
480                                 ctx.Self,
481                                 ctx.Neigh[*nodeOur.Id],
482                                 &pktTrans,
483                                 123,
484                                 0, MaxFileSize, 1,
485                                 bytes.NewReader(data),
486                                 &dst,
487                         ); err != nil {
488                                 t.Error(err)
489                                 return false
490                         }
491                         hasher := MTHNew(0, 0)
492                         hasher.Write(dst.Bytes())
493                         if err := os.WriteFile(
494                                 filepath.Join(rxPath, Base32Codec.EncodeToString(hasher.Sum(nil))),
495                                 dst.Bytes(),
496                                 os.FileMode(0600),
497                         ); err != nil {
498                                 panic(err)
499                         }
500                 }
501                 ctx.Toss(ctx.Self.Id, TRx, 123,
502                         false, false, false, false, false, false, false, false)
503                 if len(dirFiles(rxPath)) != 0 {
504                         return false
505                 }
506                 for _, filename := range dirFiles(txPath) {
507                         dataRead, err := os.ReadFile(filepath.Join(txPath, filename))
508                         if err != nil {
509                                 panic(err)
510                         }
511                         for k, data := range datum {
512                                 if bytes.Equal(dataRead, data) {
513                                         delete(datum, k)
514                                 }
515                         }
516                 }
517                 return len(datum) == 0
518         }
519         if err := quick.Check(f, nil); err != nil {
520                 t.Error(err)
521         }
522 }