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