]> Cypherpunks.ru repositories - nncp.git/blob - src/toss_test.go
freq.maxsize configuration file option
[nncp.git] / src / 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, 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                         ); 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                                 MaxFileSize,
203                                 1<<15,
204                                 MaxFileSize,
205                         ); err != nil {
206                                 t.Error(err)
207                                 return false
208                         }
209                 }
210                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
211                 os.Rename(filepath.Join(spool, ctx.Self.Id.String(), string(TTx)), rxPath)
212                 ctx.Toss(ctx.Self.Id, DefaultNiceFile, false, false, false, false, false, false)
213                 if len(dirFiles(rxPath)) == 0 {
214                         return false
215                 }
216                 ctx.Neigh[*nodeOur.Id].Incoming = &incomingPath
217                 ctx.Toss(ctx.Self.Id, DefaultNiceFile, false, false, false, false, false, false)
218                 if len(dirFiles(rxPath)) != 0 {
219                         return false
220                 }
221                 for _, fileData := range files {
222                         checksum := blake2b.Sum256(fileData)
223                         fileName := ToBase32(checksum[:])
224                         data, err := ioutil.ReadFile(filepath.Join(incomingPath, fileName))
225                         if err != nil {
226                                 panic(err)
227                         }
228                         if bytes.Compare(data, fileData) != 0 {
229                                 return false
230                         }
231                 }
232                 return true
233         }
234         if err := quick.Check(f, nil); err != nil {
235                 t.Error(err)
236         }
237 }
238
239 func TestTossFileSameName(t *testing.T) {
240         f := func(filesRaw uint8) bool {
241                 files := int(filesRaw)%8 + 1
242                 spool, err := ioutil.TempDir("", "testtoss")
243                 if err != nil {
244                         panic(err)
245                 }
246                 defer os.RemoveAll(spool)
247                 nodeOur, err := NewNodeGenerate()
248                 if err != nil {
249                         t.Error(err)
250                         return false
251                 }
252                 ctx := Ctx{
253                         Spool:   spool,
254                         Self:    nodeOur,
255                         SelfId:  nodeOur.Id,
256                         Neigh:   make(map[NodeId]*Node),
257                         Alias:   make(map[string]*NodeId),
258                         LogPath: filepath.Join(spool, "log.log"),
259                         Debug:   TDebug,
260                 }
261                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
262                 srcPath := filepath.Join(spool, "junk")
263                 if err = ioutil.WriteFile(
264                         srcPath,
265                         []byte("doesnotmatter"),
266                         os.FileMode(0600),
267                 ); err != nil {
268                         t.Error(err)
269                         return false
270                 }
271                 incomingPath := filepath.Join(spool, "incoming")
272                 for i := 0; i < files; i++ {
273                         if err := ctx.TxFile(
274                                 ctx.Neigh[*nodeOur.Id],
275                                 DefaultNiceFile,
276                                 srcPath,
277                                 "samefile",
278                                 MaxFileSize,
279                                 1<<15,
280                                 MaxFileSize,
281                         ); err != nil {
282                                 t.Error(err)
283                                 return false
284                         }
285                 }
286                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
287                 os.Rename(filepath.Join(spool, ctx.Self.Id.String(), string(TTx)), rxPath)
288                 ctx.Neigh[*nodeOur.Id].Incoming = &incomingPath
289                 ctx.Toss(ctx.Self.Id, DefaultNiceFile, false, false, false, false, false, false)
290                 expected := make(map[string]struct{})
291                 expected["samefile"] = struct{}{}
292                 for i := 0; i < files-1; i++ {
293                         expected["samefile."+strconv.Itoa(i)] = struct{}{}
294                 }
295                 for _, filename := range dirFiles(incomingPath) {
296                         if _, exists := expected[filename]; !exists {
297                                 return false
298                         }
299                         delete(expected, filename)
300                 }
301                 if len(expected) != 0 {
302                         return false
303                 }
304                 return true
305         }
306         if err := quick.Check(f, nil); err != nil {
307                 t.Error(err)
308         }
309 }
310
311 func TestTossFreq(t *testing.T) {
312         f := func(fileSizes []uint8, replyNice uint8) bool {
313                 if len(fileSizes) == 0 {
314                         return true
315                 }
316                 spool, err := ioutil.TempDir("", "testtoss")
317                 if err != nil {
318                         panic(err)
319                 }
320                 defer os.RemoveAll(spool)
321                 nodeOur, err := NewNodeGenerate()
322                 if err != nil {
323                         t.Error(err)
324                         return false
325                 }
326                 ctx := Ctx{
327                         Spool:   spool,
328                         Self:    nodeOur,
329                         SelfId:  nodeOur.Id,
330                         Neigh:   make(map[NodeId]*Node),
331                         Alias:   make(map[string]*NodeId),
332                         LogPath: filepath.Join(spool, "log.log"),
333                         Debug:   TDebug,
334                 }
335                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
336                 files := make(map[string][]byte)
337                 for i, fileSize := range fileSizes {
338                         fileData := make([]byte, fileSize)
339                         if _, err := io.ReadFull(rand.Reader, fileData); err != nil {
340                                 panic(err)
341                         }
342                         fileName := strconv.Itoa(i)
343                         files[fileName] = fileData
344                         if err := ctx.TxFreq(
345                                 ctx.Neigh[*nodeOur.Id],
346                                 DefaultNiceFreq,
347                                 replyNice,
348                                 fileName,
349                                 fileName,
350                                 1<<15,
351                         ); err != nil {
352                                 t.Error(err)
353                                 return false
354                         }
355                 }
356                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
357                 txPath := filepath.Join(spool, ctx.Self.Id.String(), string(TTx))
358                 os.Rename(txPath, rxPath)
359                 os.MkdirAll(txPath, os.FileMode(0700))
360                 ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false, false, false, false, false, false)
361                 if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 {
362                         return false
363                 }
364                 ctx.Neigh[*nodeOur.Id].FreqPath = &spool
365                 ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false, false, false, false, false, false)
366                 if len(dirFiles(txPath)) != 0 || len(dirFiles(rxPath)) == 0 {
367                         return false
368                 }
369                 for fileName, fileData := range files {
370                         if err := ioutil.WriteFile(
371                                 filepath.Join(spool, fileName),
372                                 fileData,
373                                 os.FileMode(0600),
374                         ); err != nil {
375                                 panic(err)
376                         }
377                 }
378                 ctx.Toss(ctx.Self.Id, DefaultNiceFreq, false, false, false, false, false, false)
379                 if len(dirFiles(txPath)) == 0 || len(dirFiles(rxPath)) != 0 {
380                         return false
381                 }
382                 for job := range ctx.Jobs(ctx.Self.Id, TTx) {
383                         var buf bytes.Buffer
384                         _, _, err := PktEncRead(ctx.Self, ctx.Neigh, job.Fd, &buf)
385                         if err != nil {
386                                 t.Error(err)
387                                 return false
388                         }
389                         var pkt Pkt
390                         if _, err = xdr.Unmarshal(&buf, &pkt); err != nil {
391                                 t.Error(err)
392                                 return false
393                         }
394                         if pkt.Nice != replyNice {
395                                 return false
396                         }
397                         dst := string(pkt.Path[:int(pkt.PathLen)])
398                         if bytes.Compare(buf.Bytes(), files[dst]) != 0 {
399                                 return false
400                         }
401                 }
402                 return true
403         }
404         if err := quick.Check(f, nil); err != nil {
405                 t.Error(err)
406         }
407 }
408
409 func TestTossTrns(t *testing.T) {
410         f := func(datumLens []uint8) bool {
411                 if len(datumLens) == 0 {
412                         return true
413                 }
414                 datum := make(map[int][]byte)
415                 for i, datumLen := range datumLens {
416                         datumLen += 64
417                         data := make([]byte, datumLen)
418                         if _, err := io.ReadFull(rand.Reader, data); err != nil {
419                                 panic(err)
420                         }
421                         datum[i] = data
422                 }
423                 spool, err := ioutil.TempDir("", "testtoss")
424                 if err != nil {
425                         panic(err)
426                 }
427                 defer os.RemoveAll(spool)
428                 nodeOur, err := NewNodeGenerate()
429                 if err != nil {
430                         t.Error(err)
431                         return false
432                 }
433                 ctx := Ctx{
434                         Spool:   spool,
435                         Self:    nodeOur,
436                         SelfId:  nodeOur.Id,
437                         Neigh:   make(map[NodeId]*Node),
438                         Alias:   make(map[string]*NodeId),
439                         LogPath: filepath.Join(spool, "log.log"),
440                         Debug:   TDebug,
441                 }
442                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
443                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
444                 os.MkdirAll(rxPath, os.FileMode(0700))
445                 txPath := filepath.Join(spool, ctx.Self.Id.String(), string(TTx))
446                 os.MkdirAll(txPath, os.FileMode(0700))
447                 for _, data := range datum {
448                         pktTrans := Pkt{
449                                 Magic:   MagicNNCPPv3,
450                                 Type:    PktTypeTrns,
451                                 PathLen: blake2b.Size256,
452                                 Path:    new([MaxPathSize]byte),
453                         }
454                         copy(pktTrans.Path[:], nodeOur.Id[:])
455                         var dst bytes.Buffer
456                         if err := PktEncWrite(
457                                 ctx.Self,
458                                 ctx.Neigh[*nodeOur.Id],
459                                 &pktTrans,
460                                 123,
461                                 int64(len(data)),
462                                 0,
463                                 bytes.NewReader(data),
464                                 &dst,
465                         ); err != nil {
466                                 t.Error(err)
467                                 return false
468                         }
469                         checksum := blake2b.Sum256(dst.Bytes())
470                         if err := ioutil.WriteFile(
471                                 filepath.Join(rxPath, ToBase32(checksum[:])),
472                                 dst.Bytes(),
473                                 os.FileMode(0600),
474                         ); err != nil {
475                                 panic(err)
476                         }
477                 }
478                 ctx.Toss(ctx.Self.Id, 123, false, false, false, false, false, false)
479                 if len(dirFiles(rxPath)) != 0 {
480                         return false
481                 }
482                 for _, filename := range dirFiles(txPath) {
483                         dataRead, err := ioutil.ReadFile(filepath.Join(txPath, filename))
484                         if err != nil {
485                                 panic(err)
486                         }
487                         for k, data := range datum {
488                                 if bytes.Compare(dataRead, data) == 0 {
489                                         delete(datum, k)
490                                 }
491                         }
492                 }
493                 if len(datum) > 0 {
494                         return false
495                 }
496                 return true
497         }
498         if err := quick.Check(f, nil); err != nil {
499                 t.Error(err)
500         }
501 }