]> Cypherpunks.ru repositories - nncp.git/blob - src/toss_test.go
299e80f77d0166a13fe2097c7396a4c265600424
[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, 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)
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)
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)
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.Compare(mbox, expected) == 0
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)
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) {
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.Compare(data, fileData) != 0 {
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)
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                 if len(expected) != 0 {
319                         return false
320                 }
321                 return true
322         }
323         if err := quick.Check(f, nil); err != nil {
324                 t.Error(err)
325         }
326 }
327
328 func TestTossFreq(t *testing.T) {
329         f := func(fileSizes []uint8, replyNice uint8) bool {
330                 if len(fileSizes) == 0 {
331                         return true
332                 }
333                 spool, err := ioutil.TempDir("", "testtoss")
334                 if err != nil {
335                         panic(err)
336                 }
337                 defer os.RemoveAll(spool)
338                 nodeOur, err := NewNodeGenerate()
339                 if err != nil {
340                         t.Error(err)
341                         return false
342                 }
343                 ctx := Ctx{
344                         Spool:   spool,
345                         Self:    nodeOur,
346                         SelfId:  nodeOur.Id,
347                         Neigh:   make(map[NodeId]*Node),
348                         Alias:   make(map[string]*NodeId),
349                         LogPath: filepath.Join(spool, "log.log"),
350                         Debug:   TDebug,
351                 }
352                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
353                 files := make(map[string][]byte)
354                 for i, fileSize := range fileSizes {
355                         if fileSize == 0 {
356                                 // to prevent chunked send
357                                 fileSize++
358                         }
359                         fileData := make([]byte, fileSize)
360                         if _, err := io.ReadFull(rand.Reader, fileData); err != nil {
361                                 panic(err)
362                         }
363                         fileName := strconv.Itoa(i)
364                         files[fileName] = fileData
365                         if err := ctx.TxFreq(
366                                 ctx.Neigh[*nodeOur.Id],
367                                 DefaultNiceFreq,
368                                 replyNice,
369                                 fileName,
370                                 fileName,
371                                 1<<15,
372                         ); err != nil {
373                                 t.Error(err)
374                                 return false
375                         }
376                 }
377                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
378                 txPath := filepath.Join(spool, ctx.Self.Id.String(), string(TTx))
379                 os.Rename(txPath, rxPath)
380                 os.MkdirAll(txPath, os.FileMode(0700))
381                 ctx.Toss(ctx.Self.Id, TRx, DefaultNiceFreq,
382                         false, false, false, false, false, false, false)
383                 if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 {
384                         return false
385                 }
386                 ctx.Neigh[*nodeOur.Id].FreqPath = &spool
387                 ctx.Toss(ctx.Self.Id, TRx, DefaultNiceFreq,
388                         false, false, false, false, false, false, false)
389                 if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 {
390                         return false
391                 }
392                 for fileName, fileData := range files {
393                         if err := ioutil.WriteFile(
394                                 filepath.Join(spool, fileName),
395                                 fileData,
396                                 os.FileMode(0600),
397                         ); err != nil {
398                                 panic(err)
399                         }
400                 }
401                 ctx.Toss(ctx.Self.Id, TRx, DefaultNiceFreq,
402                         false, false, false, false, false, false, false)
403                 if len(dirFiles(txPath)) == 0 || len(dirFiles(rxPath)) != 0 {
404                         return false
405                 }
406                 for job := range ctx.Jobs(ctx.Self.Id, TTx) {
407                         var buf bytes.Buffer
408                         fd, err := os.Open(job.Path)
409                         if err != nil {
410                                 t.Error(err)
411                                 return false
412                         }
413                         _, _, _, err = PktEncRead(ctx.Self, ctx.Neigh, fd, &buf, true, nil)
414                         if err != nil {
415                                 t.Error(err)
416                                 return false
417                         }
418                         var pkt Pkt
419                         if _, err = xdr.Unmarshal(&buf, &pkt); err != nil {
420                                 t.Error(err)
421                                 return false
422                         }
423                         if pkt.Nice != replyNice {
424                                 return false
425                         }
426                         dst := string(pkt.Path[:int(pkt.PathLen)])
427                         if bytes.Compare(buf.Bytes(), files[dst]) != 0 {
428                                 return false
429                         }
430                 }
431                 return true
432         }
433         if err := quick.Check(f, nil); err != nil {
434                 t.Error(err)
435         }
436 }
437
438 func TestTossTrns(t *testing.T) {
439         f := func(datumLens []uint8) bool {
440                 if len(datumLens) == 0 {
441                         return true
442                 }
443                 datum := make(map[int][]byte)
444                 for i, datumLen := range datumLens {
445                         datumLen += 64
446                         data := make([]byte, datumLen)
447                         if _, err := io.ReadFull(rand.Reader, data); err != nil {
448                                 panic(err)
449                         }
450                         datum[i] = data
451                 }
452                 spool, err := ioutil.TempDir("", "testtoss")
453                 if err != nil {
454                         panic(err)
455                 }
456                 defer os.RemoveAll(spool)
457                 nodeOur, err := NewNodeGenerate()
458                 if err != nil {
459                         t.Error(err)
460                         return false
461                 }
462                 ctx := Ctx{
463                         Spool:   spool,
464                         Self:    nodeOur,
465                         SelfId:  nodeOur.Id,
466                         Neigh:   make(map[NodeId]*Node),
467                         Alias:   make(map[string]*NodeId),
468                         LogPath: filepath.Join(spool, "log.log"),
469                         Debug:   TDebug,
470                 }
471                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
472                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
473                 os.MkdirAll(rxPath, os.FileMode(0700))
474                 txPath := filepath.Join(spool, ctx.Self.Id.String(), string(TTx))
475                 os.MkdirAll(txPath, os.FileMode(0700))
476                 for _, data := range datum {
477                         pktTrans := Pkt{
478                                 Magic:   MagicNNCPPv3.B,
479                                 Type:    PktTypeTrns,
480                                 PathLen: MTHSize,
481                         }
482                         copy(pktTrans.Path[:], nodeOur.Id[:])
483                         var dst bytes.Buffer
484                         if _, _, err := PktEncWrite(
485                                 ctx.Self,
486                                 ctx.Neigh[*nodeOur.Id],
487                                 &pktTrans,
488                                 123,
489                                 0, MaxFileSize, 1,
490                                 bytes.NewReader(data),
491                                 &dst,
492                         ); err != nil {
493                                 t.Error(err)
494                                 return false
495                         }
496                         hasher := MTHNew(0, 0)
497                         hasher.Write(dst.Bytes())
498                         if err := ioutil.WriteFile(
499                                 filepath.Join(rxPath, Base32Codec.EncodeToString(hasher.Sum(nil))),
500                                 dst.Bytes(),
501                                 os.FileMode(0600),
502                         ); err != nil {
503                                 panic(err)
504                         }
505                 }
506                 ctx.Toss(ctx.Self.Id, TRx, 123,
507                         false, false, false, false, false, false, false)
508                 if len(dirFiles(rxPath)) != 0 {
509                         return false
510                 }
511                 for _, filename := range dirFiles(txPath) {
512                         dataRead, err := ioutil.ReadFile(filepath.Join(txPath, filename))
513                         if err != nil {
514                                 panic(err)
515                         }
516                         for k, data := range datum {
517                                 if bytes.Compare(dataRead, data) == 0 {
518                                         delete(datum, k)
519                                 }
520                         }
521                 }
522                 if len(datum) > 0 {
523                         return false
524                 }
525                 return true
526         }
527         if err := quick.Check(f, nil); err != nil {
528                 t.Error(err)
529         }
530 }