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