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