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