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