]> Cypherpunks.ru repositories - nncp.git/blob - src/toss_test.go
nncp-exec -nocompress/-use-tmp options
[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                         _, _, err := PktEncRead(ctx.Self, ctx.Neigh, job.Fd, &buf)
387                         if err != nil {
388                                 t.Error(err)
389                                 return false
390                         }
391                         var pkt Pkt
392                         if _, err = xdr.Unmarshal(&buf, &pkt); err != nil {
393                                 t.Error(err)
394                                 return false
395                         }
396                         if pkt.Nice != replyNice {
397                                 return false
398                         }
399                         dst := string(pkt.Path[:int(pkt.PathLen)])
400                         if bytes.Compare(buf.Bytes(), files[dst]) != 0 {
401                                 return false
402                         }
403                 }
404                 return true
405         }
406         if err := quick.Check(f, nil); err != nil {
407                 t.Error(err)
408         }
409 }
410
411 func TestTossTrns(t *testing.T) {
412         f := func(datumLens []uint8) bool {
413                 if len(datumLens) == 0 {
414                         return true
415                 }
416                 datum := make(map[int][]byte)
417                 for i, datumLen := range datumLens {
418                         datumLen += 64
419                         data := make([]byte, datumLen)
420                         if _, err := io.ReadFull(rand.Reader, data); err != nil {
421                                 panic(err)
422                         }
423                         datum[i] = data
424                 }
425                 spool, err := ioutil.TempDir("", "testtoss")
426                 if err != nil {
427                         panic(err)
428                 }
429                 defer os.RemoveAll(spool)
430                 nodeOur, err := NewNodeGenerate()
431                 if err != nil {
432                         t.Error(err)
433                         return false
434                 }
435                 ctx := Ctx{
436                         Spool:   spool,
437                         Self:    nodeOur,
438                         SelfId:  nodeOur.Id,
439                         Neigh:   make(map[NodeId]*Node),
440                         Alias:   make(map[string]*NodeId),
441                         LogPath: filepath.Join(spool, "log.log"),
442                         Debug:   TDebug,
443                 }
444                 ctx.Neigh[*nodeOur.Id] = nodeOur.Their()
445                 rxPath := filepath.Join(spool, ctx.Self.Id.String(), string(TRx))
446                 os.MkdirAll(rxPath, os.FileMode(0700))
447                 txPath := filepath.Join(spool, ctx.Self.Id.String(), string(TTx))
448                 os.MkdirAll(txPath, os.FileMode(0700))
449                 for _, data := range datum {
450                         pktTrans := Pkt{
451                                 Magic:   MagicNNCPPv3,
452                                 Type:    PktTypeTrns,
453                                 PathLen: blake2b.Size256,
454                         }
455                         copy(pktTrans.Path[:], nodeOur.Id[:])
456                         var dst bytes.Buffer
457                         if err := PktEncWrite(
458                                 ctx.Self,
459                                 ctx.Neigh[*nodeOur.Id],
460                                 &pktTrans,
461                                 123,
462                                 int64(len(data)),
463                                 0,
464                                 bytes.NewReader(data),
465                                 &dst,
466                         ); err != nil {
467                                 t.Error(err)
468                                 return false
469                         }
470                         checksum := blake2b.Sum256(dst.Bytes())
471                         if err := ioutil.WriteFile(
472                                 filepath.Join(rxPath, Base32Codec.EncodeToString(checksum[:])),
473                                 dst.Bytes(),
474                                 os.FileMode(0600),
475                         ); err != nil {
476                                 panic(err)
477                         }
478                 }
479                 ctx.Toss(ctx.Self.Id, 123, false, false, false, false, false, false)
480                 if len(dirFiles(rxPath)) != 0 {
481                         return false
482                 }
483                 for _, filename := range dirFiles(txPath) {
484                         dataRead, err := ioutil.ReadFile(filepath.Join(txPath, filename))
485                         if err != nil {
486                                 panic(err)
487                         }
488                         for k, data := range datum {
489                                 if bytes.Compare(dataRead, data) == 0 {
490                                         delete(datum, k)
491                                 }
492                         }
493                 }
494                 if len(datum) > 0 {
495                         return false
496                 }
497                 return true
498         }
499         if err := quick.Check(f, nil); err != nil {
500                 t.Error(err)
501         }
502 }