Compare commits

...

8 Commits
v2.2.2 ... main

  1. 12
      README.md
  2. 2
      src/addr/local.go
  3. 16
      src/checksum/checksum.go
  4. 2
      src/checksum/checksum_test.go
  5. 2
      src/encryption/decrypt.go
  6. 2
      src/encryption/encrypt.go
  7. 2
      src/encryption/encryption_test.go
  8. 2
      src/encryption/key.go
  9. 79
      src/fsys/dir.go
  10. 38
      src/fsys/dir_test.go
  11. 6
      src/fsys/file.go
  12. 2
      src/fsys/file_test.go
  13. 70
      src/fsys/symlink.go
  14. 42
      src/fsys/symlink_test.go
  15. 4
      src/go.mod
  16. 28
      src/main.go
  17. 129
      src/node/node.go
  18. 10
      src/node/options.go
  19. 2
      src/protocol/constants.go
  20. 9
      src/protocol/headers.go
  21. 4
      src/protocol/packet.go
  22. 4
      src/protocol/packetConstruct.go
  23. 4
      src/protocol/packetDecode.go
  24. 2
      src/protocol/protocol_test.go
  25. 2
      src/protocol/recv.go
  26. 41
      src/protocol/send.go
  27. 1
      src/testfiles/testdir/testsymlink2.txt
  28. 1
      src/testfiles/testsymlink.txt

12
README.md

@ -16,7 +16,7 @@ In order to transfer one file on one computer to another - they need to establis
In order to establish a connection - there needs to be a 1) sender (server) (the owner of the file), waiting for connections, and a 2) receiver (client), who will try to connect to a sender (server). If the requirements are met - client will connect to server and the packet exchange will begin. In order to establish a connection - there needs to be a 1) sender (server) (the owner of the file), waiting for connections, and a 2) receiver (client), who will try to connect to a sender (server). If the requirements are met - client will connect to server and the packet exchange will begin.
The server and the client needs to communicate with packets according to certain rules, given by a [protocol](https://github.com/Unbewohnte/ftu/tree/main/src/protocol). The server and the client needs to communicate with packets according to certain rules, given by a [protocol](http://unbewohnte.xyz:3000/Unbewohnte/ftu/src/branch/main/src/protocol).
The packet has its header and body. They are divided into several groups of use by headers, this way we can specify what kind of data is stored inside packet`s body and react accordingly. The packet has its header and body. They are divided into several groups of use by headers, this way we can specify what kind of data is stored inside packet`s body and react accordingly.
@ -28,13 +28,13 @@ Thus, with a connection and a way of communication, the sender will send some pa
## ● Installation ## ● Installation
### ● From release (Pre-compiled) ### ● From release (Pre-compiled)
- Proceed to [releases page](https://github.com/Unbewohnte/ftu/releases) - Proceed to [releases page](http://unbewohnte.xyz:3000/Unbewohnte/ftu/releases)
- Choose a version/architecture you have and download an archive - Choose a version/architecture you have and download an archive
- Unpack an archive - Unpack an archive
- If on GNU/Linux - run `chmod +x install.sh && sudo ./install.sh` - If on GNU/Linux - run `chmod +x install.sh && sudo ./install.sh`
### ● From source (Compile it yourself) (You need [Go](https://golang.org/dl/) and [git](https://git-scm.com/) to be installed on your machine) ### ● From source (Compile it yourself) (You need [Go](https://golang.org/dl/) and [git](https://git-scm.com/) to be installed on your machine)
- `git clone https://github.com/Unbewohnte/ftu.git` - `git clone http://unbewohnte.xyz:3000/Unbewohnte/ftu`
- `cd` into the folder - `cd` into the folder
- If on GNU/Linux - run `make && sudo make install` or `make && chmod +x install.sh && sudo ./install` - If on GNU/Linux - run `make && sudo make install` or `make && chmod +x install.sh && sudo ./install`
- else - cd into src/ folder and simply run `go build`; after that you`re free to put the binary wherever you desire - else - cd into src/ folder and simply run `go build`; after that you`re free to put the binary wherever you desire
@ -46,7 +46,7 @@ Now you have ftu installed !
## ● Usage ## ● Usage
`ftu -h` - to print a usage message `ftu -h` - to print a usage message
`ftu [FLAGS]` `ftu [FLAGs]`
### ● FLAGs ### ● FLAGs
- -p [uint] for port - -p [uint] for port
@ -81,10 +81,6 @@ creates a node that will send every file in the directory !RECUSRIVELY!
`make test` or in "src" directory `go test ./...` `make test` or in "src" directory `go test ./...`
---
## ● Inspired by [croc](https://github.com/schollz/croc)
--- ---
## ● License ## ● License

2
src/addr/local.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu

16
src/checksum/checksum.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -21,6 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
package checksum package checksum
import ( import (
"bytes"
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"io" "io"
@ -36,8 +37,8 @@ import (
// GetPartialCheckSum is default method used to get a file checksum by sender and receiver // GetPartialCheckSum is default method used to get a file checksum by sender and receiver
func GetPartialCheckSum(file *os.File) (string, error) { func GetPartialCheckSum(file *os.File) (string, error) {
// "capturing" CHUNKSIZE bytes and then skipping STEP bytes before the next chunk until the last one // "capturing" CHUNKSIZE bytes and then skipping STEP bytes before the next chunk until the last one
const CHUNKS uint = 100 const CHUNKS uint = 50
const CHUNKSIZE uint = 100 const CHUNKSIZE uint = 50
const STEP uint = 250 const STEP uint = 250
fileStats, err := file.Stat() fileStats, err := file.Stat()
@ -48,7 +49,7 @@ func GetPartialCheckSum(file *os.File) (string, error) {
fileSize := fileStats.Size() fileSize := fileStats.Size()
if fileSize < int64(CHUNKS*CHUNKSIZE+STEP*(CHUNKS-1)) { if fileSize < int64(CHUNKS*CHUNKSIZE+STEP*(CHUNKS-1)) {
// file is too small to chop it in chunks, so just doing full checksum // file is too small to chop it in chunks, so just get the full checksum
checksum, err := getFullCheckSum(file) checksum, err := getFullCheckSum(file)
if err != nil { if err != nil {
@ -62,19 +63,20 @@ func GetPartialCheckSum(file *os.File) (string, error) {
return "", err return "", err
} }
var capturedChunks string // var capturedChunks string
var capturedChunks bytes.Buffer
var read uint64 = 0 var read uint64 = 0
for i := 0; uint(i) < CHUNKS; i++ { for i := 0; uint(i) < CHUNKS; i++ {
buffer := make([]byte, CHUNKSIZE) buffer := make([]byte, CHUNKSIZE)
r, _ := file.ReadAt(buffer, int64(read)) r, _ := file.ReadAt(buffer, int64(read))
capturedChunks += string(buffer) capturedChunks.Write(buffer)
read += uint64(r) read += uint64(r)
read += uint64(STEP) read += uint64(STEP)
} }
checksumBytes := sha256.Sum256([]byte(capturedChunks)) checksumBytes := sha256.Sum256(capturedChunks.Bytes())
checksum := hex.EncodeToString(checksumBytes[:]) checksum := hex.EncodeToString(checksumBytes[:])
return checksum, nil return checksum, nil

2
src/checksum/checksum_test.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu

2
src/encryption/decrypt.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu

2
src/encryption/encrypt.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu

2
src/encryption/encryption_test.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu

2
src/encryption/key.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu

79
src/fsys/dir.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -22,6 +22,7 @@ package fsys
import ( import (
"fmt" "fmt"
"io/fs"
"os" "os"
"path/filepath" "path/filepath"
) )
@ -32,6 +33,7 @@ type Directory struct {
Path string Path string
Size uint64 Size uint64
RelativeParentPath string // Relative path to the directory, where the highest point in the hierarchy is the upmost parent dir. Set manually RelativeParentPath string // Relative path to the directory, where the highest point in the hierarchy is the upmost parent dir. Set manually
Symlinks []*Symlink
Files []*File Files []*File
Directories []*Directory Directories []*Directory
} }
@ -63,12 +65,13 @@ func GetDir(path string, recursive bool) (*Directory, error) {
// loop through each entry in the directory // loop through each entry in the directory
entries, err := os.ReadDir(absPath) entries, err := os.ReadDir(absPath)
if err != nil { if err != nil && err != fs.ErrPermission {
return nil, err return nil, err
} }
var innerDirs []*Directory var innerDirs []*Directory
var innerFiles []*File var innerFiles []*File
var innerSymlinks []*Symlink
for _, entry := range entries { for _, entry := range entries {
entryInfo, err := entry.Info() entryInfo, err := entry.Info()
if err != nil { if err != nil {
@ -92,21 +95,42 @@ func GetDir(path string, recursive bool) (*Directory, error) {
// if not - skip the directory and only work with the files // if not - skip the directory and only work with the files
} else { } else {
innerFilePath := filepath.Join(absPath, entryInfo.Name()) // not a directory
innerFile, err := GetFile(innerFilePath) switch entryInfo.Mode()&os.ModeSymlink != 0 {
if err != nil { case true:
return nil, err // it is a symlink
} innerSymlinkPath := filepath.Join(absPath, entryInfo.Name())
symlink, err := GetSymlink(innerSymlinkPath, false)
if err != nil {
// skip this symlink
continue
}
directory.Size += innerFile.Size innerSymlinks = append(innerSymlinks, symlink)
innerFiles = append(innerFiles, innerFile) case false:
// it is a usual file
innerFilePath := filepath.Join(absPath, entryInfo.Name())
innerFile, err := GetFile(innerFilePath)
if err != nil {
// skip this file
continue
}
directory.Size += innerFile.Size
innerFiles = append(innerFiles, innerFile)
}
} }
} }
directory.Directories = innerDirs directory.Directories = innerDirs
directory.Files = innerFiles directory.Files = innerFiles
directory.Symlinks = innerSymlinks
return &directory, nil return &directory, nil
} }
@ -132,7 +156,27 @@ func (dir *Directory) GetAllFiles(recursive bool) []*File {
return files return files
} }
// Sets `RelativeParentPath` relative to the given base path. // Returns every symlink in that directory
func (dir *Directory) GetAllSymlinks(recursive bool) []*Symlink {
var symlinks []*Symlink = dir.Symlinks
if recursive {
if len(dir.Directories) == 0 {
return symlinks
}
for _, innerDir := range dir.Directories {
innerSymlinks := innerDir.GetAllSymlinks(recursive)
symlinks = append(symlinks, innerSymlinks...)
}
} else {
symlinks = dir.Symlinks
}
return symlinks
}
// Sets `RelativeParentPath` relative to the given base path for files and `Path`, `TargetPath` for symlinks so the
// file with such path: // file with such path:
// /home/user/directory/somefile.txt // /home/user/directory/somefile.txt
// had a relative path like that: // had a relative path like that:
@ -148,5 +192,20 @@ func (dir *Directory) SetRelativePaths(base string, recursive bool) error {
file.RelativeParentPath = relPath file.RelativeParentPath = relPath
} }
for _, symlink := range dir.GetAllSymlinks(recursive) {
symRelPath, err := filepath.Rel(base, symlink.Path)
if err != nil {
return err
}
symlink.Path = symRelPath
symRelTargetPath, err := filepath.Rel(base, symlink.TargetPath)
if err != nil {
return err
}
symlink.TargetPath = symRelTargetPath
}
return nil return nil
} }

38
src/fsys/dir_test.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -21,6 +21,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
package fsys package fsys
import ( import (
"os"
"path/filepath"
"testing" "testing"
) )
@ -79,25 +81,23 @@ func Test_GetFiles(t *testing.T) {
} }
// func Test_SetRelativePaths(t *testing.T) { func Test_GetSymlinks(t *testing.T) {
// dirpath := "../testfiles/" dirpath := "../testfiles/"
// dir, err := GetDir(dirpath, true) os.Symlink(filepath.Join(dirpath, "testfile.txt"), filepath.Join(dirpath, "testsymlink.txt"))
// if err != nil { os.Symlink(filepath.Join(dirpath, "testdir", "testfile2.txt"), filepath.Join(dirpath, "testdir", "testsymlink2.txt"))
// t.Fatalf("%s", err)
// }
// absDirPath, err := filepath.Abs(dirpath) dir, err := GetDir(dirpath, true)
// if err != nil { if err != nil {
// t.Fatalf("%s", err) t.Fatalf("%s", err)
// } }
// err = dir.SetRelativePaths(absDirPath, true) // recursive
// if err != nil { symlinks := dir.GetAllSymlinks(true)
// t.Fatalf("%s", err)
// } symlinkCount := 2
// for count, file := range dir.GetAllFiles(true) { if len(symlinks) != symlinkCount {
// t.Errorf("[%d] %v\n", count, file.RelativeParentPath) t.Fatalf("expected to get %d symlinks; got %d\n", symlinkCount, len(symlinks))
// } }
// } }

6
src/fsys/file.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -25,7 +25,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/Unbewohnte/ftu/checksum" "unbewohnte/ftu/checksum"
) )
// A struct that represents the necessary file information for transportation through node // A struct that represents the necessary file information for transportation through node
@ -45,7 +45,7 @@ var ErrorNotFile error = fmt.Errorf("not a file")
// Get general information about a file with the // Get general information about a file with the
// future ability to open it. // future ability to open it.
// NOTE that Handler field is nil BY DEFAULT until you // NOTE that Handler field is nil BY DEFAULT until you
// manually call a (file *File) Open() function to open it ! // manually call (file *File).Open() to open it !
func GetFile(path string) (*File, error) { func GetFile(path string) (*File, error) {
absPath, err := filepath.Abs(path) absPath, err := filepath.Abs(path)
if err != nil { if err != nil {

2
src/fsys/file_test.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu

70
src/fsys/symlink.go

@ -0,0 +1,70 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package fsys
import (
"fmt"
"os"
)
type Symlink struct {
TargetPath string
Path string
}
// Checks whether path is referring to a symlink or not
func IsSymlink(path string) (bool, error) {
stats, err := os.Lstat(path)
if err != nil {
return false, err
}
isSymlink := stats.Mode()&os.ModeSymlink != 0
return isSymlink, nil
}
var ErrorNotSymlink error = fmt.Errorf("not a symlink")
// get necessary information about a symlink in a filesystem. If check is false -
// does not check if path REALLY refers to a symlink
func GetSymlink(path string, check bool) (*Symlink, error) {
if check {
isSymlink, err := IsSymlink(path)
if err != nil {
return nil, err
}
if !isSymlink {
return nil, ErrorNotSymlink
}
}
target, err := os.Readlink(path)
if err != nil {
return nil, err
}
symlink := Symlink{
TargetPath: target,
Path: path,
}
return &symlink, nil
}

42
src/fsys/symlink_test.go

@ -0,0 +1,42 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package fsys
import (
"os"
"path/filepath"
"testing"
)
func Test_IsSymlink(t *testing.T) {
dirpath := "../testfiles/"
symlinkPath := filepath.Join(dirpath, "testsymlink.txt")
os.Symlink(filepath.Join(dirpath, "testfile.txt"), symlinkPath)
isSymlink, err := IsSymlink(symlinkPath)
if err != nil {
t.Fatalf("%s\n", err)
}
if !isSymlink {
t.Fatalf("%s expected to be a symlink\n", symlinkPath)
}
}

4
src/go.mod

@ -1,3 +1,3 @@
module github.com/Unbewohnte/ftu module unbewohnte/ftu
go 1.17 go 1.18

28
src/main.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte (https://unbewohnte.xyz/))
This file is a part of ftu This file is a part of ftu
@ -26,13 +26,13 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/Unbewohnte/ftu/node" "unbewohnte/ftu/node"
) )
var ( var (
VERSION string = "v2.2.2" VERSION string = "v2.3.3"
versionInformation string = fmt.Sprintf("ftu %s\n\nCopyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it under certain conditions; type \"ftu -l\" for details.\n", VERSION) versionInformation string = fmt.Sprintf("ftu %s\nfile transferring utility\n\nCopyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it under certain conditions; type \"ftu -l\" for details.\n", VERSION)
//go:embed COPYING //go:embed COPYING
licenseInformation string licenseInformation string
@ -52,15 +52,15 @@ var (
func init() { func init() {
flag.Usage = func() { flag.Usage = func() {
fmt.Printf("ftu -[FLAG]...\n\n") fmt.Printf("ftu -[FLAGs]\n\n")
fmt.Printf("[FLAGs]\n\n") fmt.Printf("[FLAGs]\n\n")
fmt.Printf("| -p [Uinteger_here] for port\n") fmt.Printf("| -p [integer] for port\n")
fmt.Printf("| -r [true|false] for recursive sending of a directory\n") fmt.Printf("| -r [true|false] send recursively or not\n")
fmt.Printf("| -a [ip_address|domain_name] address to connect to (cannot be used with -s)\n") fmt.Printf("| -a [ip_address|domain_name] address to connect to (cannot be used with -s)\n")
fmt.Printf("| -d [path_to_directory] where the files will be downloaded to (cannot be used with -s)\n") fmt.Printf("| -d [path_to_directory] where the files will be downloaded to (cannot be used with -s)\n")
fmt.Printf("| -s [path_to_file|directory] to send it (cannot be used with -a)\n") fmt.Printf("| -s [path_to_file|directory] send it (cannot be used with -a)\n")
fmt.Printf("| -? [true|false] to turn on|off verbose output\n") fmt.Printf("| -? [true|false] turn on|off verbose output\n")
fmt.Printf("| -l print license information\n") fmt.Printf("| -l print license information\n")
fmt.Printf("| -v print version information\n\n\n") fmt.Printf("| -v print version information\n\n\n")
@ -98,12 +98,12 @@ func init() {
// validate flags // validate flags
if *SEND == "" && *ADDRESS == "" { if *SEND == "" && *ADDRESS == "" {
fmt.Printf("Neither sending nor receiving flag was specified. Run ftu -h for help\n") fmt.Printf("[ERROR] Neither sending nor receiving flag was specified. Run ftu -h for help\n")
os.Exit(-1) os.Exit(-1)
} }
if *SEND != "" && *ADDRESS != "" { if *SEND != "" && *ADDRESS != "" {
fmt.Printf("Can`t send and receive at the same time. Specify either -s or -a\n") fmt.Printf("[ERROR] Can't send and receive at the same time. Specify either -s or -a\n")
os.Exit(-1) os.Exit(-1)
} }
@ -122,11 +122,11 @@ func main() {
VerboseOutput: *VERBOSE, VerboseOutput: *VERBOSE,
IsSending: isSending, IsSending: isSending,
WorkingPort: *PORT, WorkingPort: *PORT,
ServerSide: &node.ServerSideNodeOptions{ SenderSide: &node.SenderNodeOptions{
ServingPath: *SEND, ServingPath: *SEND,
Recursive: *RECUSRIVE, Recursive: *RECUSRIVE,
}, },
ClientSide: &node.ClientSideNodeOptions{ ReceiverSide: &node.ReceiverNodeOptions{
ConnectionAddr: *ADDRESS, ConnectionAddr: *ADDRESS,
DownloadsFolderPath: *DOWNLOADS_DIR, DownloadsFolderPath: *DOWNLOADS_DIR,
}, },
@ -134,7 +134,7 @@ func main() {
node, err := node.NewNode(&nodeOptions) node, err := node.NewNode(&nodeOptions)
if err != nil { if err != nil {
fmt.Printf("Error constructing a new node: %s\n", err) fmt.Printf("[ERROR] Error constructing a new node: %s\n", err)
os.Exit(-1) os.Exit(-1)
} }

129
src/node/node.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -32,11 +32,11 @@ import (
"fmt" "fmt"
"github.com/Unbewohnte/ftu/addr" "unbewohnte/ftu/addr"
"github.com/Unbewohnte/ftu/checksum" "unbewohnte/ftu/checksum"
"github.com/Unbewohnte/ftu/encryption" "unbewohnte/ftu/encryption"
"github.com/Unbewohnte/ftu/fsys" "unbewohnte/ftu/fsys"
"github.com/Unbewohnte/ftu/protocol" "unbewohnte/ftu/protocol"
) )
// netInfowork specific settings // netInfowork specific settings
@ -49,16 +49,18 @@ type netInfo struct {
// Sending-side node information // Sending-side node information
type sending struct { type sending struct {
ServingPath string // path to the thing that will be sent ServingPath string // path to the thing that will be sent
IsDirectory bool // is ServingPath a directory IsDirectory bool // is ServingPath a directory
Recursive bool // recursively send directory Recursive bool // recursively send directory
CanSendBytes bool // is the other node ready to receive another piece CanSendBytes bool // is the other node ready to receive another piece
AllowedToTransfer bool // the way to notify the mainloop of a sending node to start sending pieces of files AllowedToTransfer bool // the way to notify the mainloop of a sending node to start sending pieces of files
InTransfer bool // already transferring|receiving files InTransfer bool // already transferring|receiving files
FilesToSend []*fsys.File FilesToSend []*fsys.File
CurrentFileID uint64 // an id of a file that is currently being transported SymlinksToSend []*fsys.Symlink
SentBytes uint64 // how many bytes sent already CurrentFileID uint64 // an id of a file that is currently being transported
TotalTransferSize uint64 // how many bytes will be sent in total SentBytes uint64 // how many bytes sent already
TotalTransferSize uint64 // how many bytes will be sent in total
CurrentSymlinkIndex uint64 // current index of a symlink that is
} }
// Receiving-side node information // Receiving-side node information
@ -91,7 +93,7 @@ func NewNode(options *NodeOptions) (*Node, error) {
var isDir bool var isDir bool
if options.IsSending { if options.IsSending {
// sending node preparation // sending node preparation
sendingPathStats, err := os.Stat(options.ServerSide.ServingPath) sendingPathStats, err := os.Stat(options.SenderSide.ServingPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -106,12 +108,12 @@ func NewNode(options *NodeOptions) (*Node, error) {
} else { } else {
// receiving node preparation // receiving node preparation
var err error var err error
options.ClientSide.DownloadsFolderPath, err = filepath.Abs(options.ClientSide.DownloadsFolderPath) options.ReceiverSide.DownloadsFolderPath, err = filepath.Abs(options.ReceiverSide.DownloadsFolderPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = os.MkdirAll(options.ClientSide.DownloadsFolderPath, os.ModePerm) err = os.MkdirAll(options.ReceiverSide.DownloadsFolderPath, os.ModePerm)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -125,22 +127,22 @@ func NewNode(options *NodeOptions) (*Node, error) {
isSending: options.IsSending, isSending: options.IsSending,
netInfo: &netInfo{ netInfo: &netInfo{
Port: options.WorkingPort, Port: options.WorkingPort,
ConnAddr: options.ClientSide.ConnectionAddr, ConnAddr: options.ReceiverSide.ConnectionAddr,
EncryptionKey: nil, EncryptionKey: nil,
Conn: nil, Conn: nil,
}, },
stopped: false, stopped: false,
transferInfo: &transferInfo{ transferInfo: &transferInfo{
Sending: &sending{ Sending: &sending{
ServingPath: options.ServerSide.ServingPath, ServingPath: options.SenderSide.ServingPath,
Recursive: options.ServerSide.Recursive, Recursive: options.SenderSide.Recursive,
IsDirectory: isDir, IsDirectory: isDir,
TotalTransferSize: 0, TotalTransferSize: 0,
SentBytes: 0, SentBytes: 0,
}, },
Receiving: &receiving{ Receiving: &receiving{
AcceptedFiles: nil, AcceptedFiles: nil,
DownloadsPath: options.ClientSide.DownloadsFolderPath, DownloadsPath: options.ReceiverSide.DownloadsFolderPath,
ReceivedBytes: 0, ReceivedBytes: 0,
TotalDownloadSize: 0, TotalDownloadSize: 0,
}, },
@ -323,7 +325,7 @@ func (node *Node) send() {
incomingPacket, ok := <-node.packetPipe incomingPacket, ok := <-node.packetPipe
if !ok { if !ok {
fmt.Printf("\nThe connection has been closed unexpectedly\n") fmt.Printf("\nThe connection has been closed unexpectedly\n")
os.Exit(-1.) os.Exit(-1)
} }
// if encryption key is set - decrypt packet on the spot // if encryption key is set - decrypt packet on the spot
@ -355,8 +357,10 @@ func (node *Node) send() {
panic(err) panic(err)
} }
filesToSend := DIRTOSEND.GetAllFiles(node.transferInfo.Sending.Recursive) filesToSend := DIRTOSEND.GetAllFiles(node.transferInfo.Sending.Recursive)
symlinksToSend := DIRTOSEND.GetAllSymlinks(node.transferInfo.Sending.Recursive)
node.transferInfo.Sending.SymlinksToSend = symlinksToSend
// notify the other node about all the files that are going to be sent
for counter, file := range filesToSend { for counter, file := range filesToSend {
// assign ID and add it to the node sendlist // assign ID and add it to the node sendlist
file.ID = uint64(counter) file.ID = uint64(counter)
@ -410,7 +414,14 @@ func (node *Node) send() {
// Transfer section // Transfer section
if len(node.transferInfo.Sending.FilesToSend) == 0 { // if all files have been sent -> send symlinks
if len(node.transferInfo.Sending.FilesToSend) == 0 && node.transferInfo.Sending.CurrentSymlinkIndex < uint64(len(node.transferInfo.Sending.SymlinksToSend)) {
protocol.SendSymlink(node.transferInfo.Sending.SymlinksToSend[node.transferInfo.Sending.CurrentSymlinkIndex], node.netInfo.Conn, encrKey)
node.transferInfo.Sending.CurrentSymlinkIndex++
continue
}
if len(node.transferInfo.Sending.FilesToSend) == 0 && node.transferInfo.Sending.CurrentSymlinkIndex == uint64(len(node.transferInfo.Sending.SymlinksToSend)) {
// if there`s nothing else to send - create and send DONE packet // if there`s nothing else to send - create and send DONE packet
protocol.SendPacket(node.netInfo.Conn, protocol.Packet{ protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderDone, Header: protocol.HeaderDone,
@ -512,7 +523,7 @@ func (node *Node) send() {
default: default:
node.stopped = true node.stopped = true
fmt.Printf("\nAn error occured while sending a piece of \"%s\": %s", node.transferInfo.Sending.FilesToSend[currentFileIndex].Name, err) fmt.Printf("\n[ERROR] An error occured while sending a piece of \"%s\": %s", node.transferInfo.Sending.FilesToSend[currentFileIndex].Name, err)
panic(err) panic(err)
} }
} }
@ -525,7 +536,7 @@ func (node *Node) receive() {
// connect to the sending node // connect to the sending node
err := node.connect() err := node.connect()
if err != nil { if err != nil {
fmt.Printf("\nCould not connect to %s:%d", node.netInfo.ConnAddr, node.netInfo.Port) fmt.Printf("\n[ERROR] Could not connect to %s:%d\n", node.netInfo.ConnAddr, node.netInfo.Port)
os.Exit(-1) os.Exit(-1)
} }
@ -763,10 +774,11 @@ func (node *Node) receive() {
// accepted // accepted
// append provided bytes to the file // append provided bytes to the file
if acceptedFile.Handler == nil {
err = acceptedFile.Open() err = acceptedFile.Open()
if err != nil { if err != nil {
panic(err) panic(err)
}
} }
fileBytes := fileBytesBuffer.Bytes() fileBytes := fileBytesBuffer.Bytes()
@ -777,11 +789,6 @@ func (node *Node) receive() {
} }
acceptedFile.SentBytes += uint64(wrote) acceptedFile.SentBytes += uint64(wrote)
node.transferInfo.Receiving.ReceivedBytes += uint64(wrote) node.transferInfo.Receiving.ReceivedBytes += uint64(wrote)
err = acceptedFile.Close()
if err != nil {
panic(err)
}
} }
} }
@ -808,9 +815,11 @@ func (node *Node) receive() {
fmt.Printf("\n[File] fully received \"%s\" -- %d bytes", acceptedFile.Name, acceptedFile.Size) fmt.Printf("\n[File] fully received \"%s\" -- %d bytes", acceptedFile.Name, acceptedFile.Size)
} }
err = acceptedFile.Open() if acceptedFile.Handler == nil {
if err != nil { err = acceptedFile.Open()
panic(err) if err != nil {
panic(err)
}
} }
// remove this file from the pool // remove this file from the pool
@ -823,7 +832,10 @@ func (node *Node) receive() {
} }
if realChecksum != acceptedFile.Checksum { if realChecksum != acceptedFile.Checksum {
fmt.Printf("\n| \"%s\" is corrupted", acceptedFile.Name) if node.verboseOutput {
fmt.Printf("\n[ERROR] \"%s\" is corrupted", acceptedFile.Name)
}
acceptedFile.Close() acceptedFile.Close()
break break
} else { } else {
@ -854,6 +866,41 @@ func (node *Node) receive() {
fmt.Printf("\nGot an encryption key: %s", encrKey) fmt.Printf("\nGot an encryption key: %s", encrKey)
case protocol.HeaderSymlink:
// SYMLINK~(string size in binary)(location in the filesystem)(string size in binary)(location of a target)
packetReader := bytes.NewReader(incomingPacket.Body)
// extract the location of the symlink
var locationSize uint64
binary.Read(packetReader, binary.BigEndian, &locationSize)
symlinkLocationBytes := make([]byte, locationSize)
packetReader.Read(symlinkLocationBytes)
// extract the target of a symlink
var targetSize uint64
binary.Read(packetReader, binary.BigEndian, &targetSize)
symlinkTargetLocationBytes := make([]byte, targetSize)
packetReader.Read(symlinkTargetLocationBytes)
symlinkLocation := string(symlinkLocationBytes)
symlinkTargetLocation := string(symlinkTargetLocationBytes)
// create a symlink
// should be already downloaded
symlinkDir := filepath.Join(node.transferInfo.Receiving.DownloadsPath, filepath.Dir(symlinkLocation))
os.MkdirAll(symlinkDir, os.ModePerm)
os.Symlink(
filepath.Join(node.transferInfo.Receiving.DownloadsPath, symlinkTargetLocation),
filepath.Join(node.transferInfo.Receiving.DownloadsPath, symlinkLocation))
protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderReady,
})
case protocol.HeaderDone: case protocol.HeaderDone:
node.mutex.Lock() node.mutex.Lock()
node.stopped = true node.stopped = true

10
src/node/options.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -20,12 +20,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
package node package node
type ServerSideNodeOptions struct { type SenderNodeOptions struct {
ServingPath string ServingPath string
Recursive bool Recursive bool
} }
type ClientSideNodeOptions struct { type ReceiverNodeOptions struct {
ConnectionAddr string ConnectionAddr string
DownloadsFolderPath string DownloadsFolderPath string
} }
@ -35,6 +35,6 @@ type NodeOptions struct {
IsSending bool IsSending bool
WorkingPort uint WorkingPort uint
VerboseOutput bool VerboseOutput bool
ServerSide *ServerSideNodeOptions SenderSide *SenderNodeOptions
ClientSide *ClientSideNodeOptions ReceiverSide *ReceiverNodeOptions
} }

2
src/protocol/constants.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu

9
src/protocol/headers.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -114,3 +114,10 @@ const HeaderDirectory Header = "DIRECTORY"
// Body must contain a file ID. // Body must contain a file ID.
// ie: ALREADYHAVE~(file ID in binary) // ie: ALREADYHAVE~(file ID in binary)
const HeaderAlreadyHave Header = "ALREADYHAVE" const HeaderAlreadyHave Header = "ALREADYHAVE"
// SYMLINK
// Sent by sender AFTER ALL FILES has been sent already. Indicates that there
// is a symlink in some place that points to some other already received file.
// Body must contain information where the symlink is and the target file.
// ie: SYMLINK~(string size in binary)(location in the filesystem)(string size in binary)(location of a target)
const HeaderSymlink Header = "SYMLINK"

4
src/protocol/packet.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -31,7 +31,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/Unbewohnte/ftu/encryption" "unbewohnte/ftu/encryption"
) )
// Internal representation of packet before|after the transportation // Internal representation of packet before|after the transportation

4
src/protocol/packetConstruct.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -25,7 +25,7 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"github.com/Unbewohnte/ftu/fsys" "unbewohnte/ftu/fsys"
) )
// constructs a ready to send FILE packet // constructs a ready to send FILE packet

4
src/protocol/packetDecode.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -26,7 +26,7 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"github.com/Unbewohnte/ftu/fsys" "unbewohnte/ftu/fsys"
) )
var ErrorWrongPacket error = fmt.Errorf("wrong type of packet header") var ErrorWrongPacket error = fmt.Errorf("wrong type of packet header")

2
src/protocol/protocol_test.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu

2
src/protocol/recv.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu

41
src/protocol/send.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/)) Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu This file is a part of ftu
@ -28,8 +28,8 @@ import (
"io" "io"
"net" "net"
"github.com/Unbewohnte/ftu/encryption" "unbewohnte/ftu/encryption"
"github.com/Unbewohnte/ftu/fsys" "unbewohnte/ftu/fsys"
) )
// Sends given packet to connection. // Sends given packet to connection.
@ -174,7 +174,7 @@ func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) (uint64, er
if encrKey != nil { if encrKey != nil {
// account for padding // account for padding
canSendBytes -= 32 canSendBytes -= 48
} }
if (file.Size - file.SentBytes) < canSendBytes { if (file.Size - file.SentBytes) < canSendBytes {
@ -209,3 +209,36 @@ func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) (uint64, er
return sentBytes, nil return sentBytes, nil
} }
// Sends a symlink to the other side. If encrKey is not nil - encrypts the packet with this key
func SendSymlink(symlink *fsys.Symlink, connection net.Conn, encrKey []byte) error {
symlinkPacket := Packet{
Header: HeaderSymlink,
}
symlinkPacketBodyBuff := new(bytes.Buffer)
// SYMLINK~(string size in binary)(location in the filesystem)(string size in binary)(location of a target)
binary.Write(symlinkPacketBodyBuff, binary.BigEndian, uint64(len(symlink.Path)))
symlinkPacketBodyBuff.Write([]byte(symlink.Path))
binary.Write(symlinkPacketBodyBuff, binary.BigEndian, uint64(len(symlink.TargetPath)))
symlinkPacketBodyBuff.Write([]byte(symlink.TargetPath))
symlinkPacket.Body = symlinkPacketBodyBuff.Bytes()
if encrKey != nil {
err := symlinkPacket.EncryptBody(encrKey)
if err != nil {
return err
}
}
err := SendPacket(connection, symlinkPacket)
if err != nil {
return err
}
return nil
}

1
src/testfiles/testdir/testsymlink2.txt

@ -0,0 +1 @@
../testfiles/testdir/testfile2.txt

1
src/testfiles/testsymlink.txt

@ -0,0 +1 @@
../testfiles/testfile.txt
Loading…
Cancel
Save