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