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