Compare commits

...

22 Commits
2.1.0 ... main

Author SHA1 Message Date
Kasianov Nikolai Alekseevich 1d8f987c27 Removed old email from license notices; minor changes to the program output, help message 2 years ago
Gitea b30d0c3465 No more github 2 years ago
Gitea 6bb927a26e Being idependent from github 2 years ago
Unbewohnte c2bd2cb0cf Apparently my name is spelled differently in English ¯\_(ツ)_/¯ 3 years ago
Unbewohnte 2548fa68e7 Less syscalls to open files on the receiving end 3 years ago
Unbewohnte 8eb2f181fd Symlinks support ! 3 years ago
Unbewohnte 700811b179 [checksum] Micro optimisations; [node] Renamed nodes to receiving and sending; [fsys] ftu now DOES NOT die on 'permission denied's 3 years ago
Unbewohnte 21d16c590f Updated README: I can't be inspired by a thing I've never used, can I ?... Tehehe~ 3 years ago
Unbewohnte 4d12a15a58 [Node] Invalid transfer info printing is >>|FIXED|<< 3 years ago
Unbewohnte 382908c1e5 Updated copyright year 3 years ago
Unbewohnte e4c086b358 make release 3 years ago
Unbewohnte 0ac9cd2b71 v2.2.0; Fixed freezing on big amount of files; Improved transfer data printing; Introduction to some minor bugs with it as well, but that is not a severe problem anyway 3 years ago
Unbewohnte 75cb68dd30 [node] fixed one-file transfer logical issue 3 years ago
Unbewohnte 550c6e26ef [main] changed version 3 years ago
Unbewohnte 74464c9470 [main] verbose output flag changed to ?; [node] added 3 microsecond delay in sender node when sending file info packets 3 years ago
Unbewohnte a8e973645a [main] added a missing new line for verbose output usage message 3 years ago
Unbewohnte d0a0eaf780 [Make] output clean executable names 3 years ago
Unbewohnte 5aae65980d [Make] remove release folders after zipping 3 years ago
Unbewohnte 3a40920af5 Verbose output 3 years ago
Unbewohnte 502877c515 [main] changed version string; [make] pkgrelease for easy cross-building 3 years ago
Unbewohnte 09e3c7b960 [node] print transfer info. The output is not flooded anymore on receiver 3 years ago
Unbewohnte 1a302cb9fb [node] bugfixes? [make] cross compilation automatisation 3 years ago
  1. 33
      Makefile
  2. 13
      README.md
  3. 2
      src/addr/local.go
  4. 16
      src/checksum/checksum.go
  5. 2
      src/checksum/checksum_test.go
  6. 2
      src/encryption/decrypt.go
  7. 2
      src/encryption/encrypt.go
  8. 2
      src/encryption/encryption_test.go
  9. 2
      src/encryption/key.go
  10. 79
      src/fsys/dir.go
  11. 38
      src/fsys/dir_test.go
  12. 6
      src/fsys/file.go
  13. 2
      src/fsys/file_test.go
  14. 70
      src/fsys/symlink.go
  15. 42
      src/fsys/symlink_test.go
  16. 4
      src/go.mod
  17. 34
      src/main.go
  18. 1065
      src/node/node.go
  19. 15
      src/node/options.go
  20. 2
      src/protocol/constants.go
  21. 17
      src/protocol/headers.go
  22. 4
      src/protocol/packet.go
  23. 4
      src/protocol/packetConstruct.go
  24. 4
      src/protocol/packetDecode.go
  25. 2
      src/protocol/protocol_test.go
  26. 2
      src/protocol/recv.go
  27. 60
      src/protocol/send.go
  28. 1
      src/testfiles/testdir/testsymlink2.txt
  29. 1
      src/testfiles/testsymlink.txt

33
Makefile

@ -3,10 +3,39 @@
SRC_DIR := src/ SRC_DIR := src/
EXE_NAME := ftu EXE_NAME := ftu
INSTALLATION_DIR := /usr/local/bin/ INSTALLATION_DIR := /usr/local/bin/
RELEASE_DIR := release
LICENSE_FILE := COPYING
INSTALLATION_SCRIPT := install.sh
all: all:
cd $(SRC_DIR) && go build && mv $(EXE_NAME) .. cd $(SRC_DIR) && go build && mv $(EXE_NAME) ..
release:
rm -rf $(RELEASE_DIR)
mkdir $(RELEASE_DIR)
mkdir $(RELEASE_DIR)/ftu_linux_amd64
mkdir $(RELEASE_DIR)/ftu_darwin_amd64
mkdir $(RELEASE_DIR)/ftu_windows_amd64
cd $(SRC_DIR) && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ftu && mv ftu ../$(RELEASE_DIR)/ftu_linux_amd64
cd $(SRC_DIR) && CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o ftu && mv ftu ../$(RELEASE_DIR)/ftu_darwin_amd64
cd $(SRC_DIR) && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o ftu.exe && mv ftu.exe ../$(RELEASE_DIR)/ftu_windows_amd64
cp $(LICENSE_FILE) $(RELEASE_DIR)/ftu_linux_amd64
cp $(INSTALLATION_SCRIPT) $(RELEASE_DIR)/ftu_linux_amd64
cp $(LICENSE_FILE) $(RELEASE_DIR)/ftu_darwin_amd64
cp $(LICENSE_FILE) $(RELEASE_DIR)/ftu_windows_amd64
cd $(RELEASE_DIR) && zip -r ftu_linux_amd64 ftu_linux_amd64/
cd $(RELEASE_DIR) && zip -r ftu_darwin_amd64 ftu_darwin_amd64/
cd $(RELEASE_DIR) && zip -r ftu_windows_amd64 ftu_windows_amd64/
rm -rf $(RELEASE_DIR)/ftu_linux_amd64
rm -rf $(RELEASE_DIR)/ftu_darwin_amd64
rm -rf $(RELEASE_DIR)/ftu_windows_amd64
race: race:
cd $(SRC_DIR) && go build -race && mv $(EXE_NAME) .. cd $(SRC_DIR) && go build -race && mv $(EXE_NAME) ..
@ -14,7 +43,7 @@ install: all
cp $(EXE_NAME) $(INSTALLATION_DIR) cp $(EXE_NAME) $(INSTALLATION_DIR)
test: test:
cd $(SRC_DIR) && go test ./... ; cd .. cd $(SRC_DIR) && go test ./...
clean: clean:
rm $(EXE_NAME) rm -rf $(EXE_NAME) $(RELEASE_DIR)

13
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
@ -54,6 +54,7 @@ Now you have ftu installed !
- -a [ip_address|domain_name] address to connect to (cannot be used with -s) - -a [ip_address|domain_name] address to connect to (cannot be used with -s)
- -d [path_to_directory] where the files will be downloaded to (cannot be used with -s) - -d [path_to_directory] where the files will be downloaded to (cannot be used with -s)
- -s [path_to_file|directory] to send it (cannot be used with -a) - -s [path_to_file|directory] to send it (cannot be used with -a)
- -? [true|false] to turn on|off verbose output
- -v print version text - -v print version text
- -l print license - -l print license
@ -80,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 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 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 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 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 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 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 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 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 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 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 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

34
src/main.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021 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.1.0" VERSION string = "v2.3.3"
versionInformation string = fmt.Sprintf("ftu %s\n\nCopyright (C) 2021 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
@ -43,6 +43,7 @@ var (
ADDRESS *string = flag.String("a", "", "Specifies an address to connect to") ADDRESS *string = flag.String("a", "", "Specifies an address to connect to")
DOWNLOADS_DIR *string = flag.String("d", ".", "Downloads folder") DOWNLOADS_DIR *string = flag.String("d", ".", "Downloads folder")
SEND *string = flag.String("s", "", "Specify a file|directory to send") SEND *string = flag.String("s", "", "Specify a file|directory to send")
VERBOSE *bool = flag.Bool("?", false, "Turn on/off verbose output")
PRINT_VERSION *bool = flag.Bool("v", false, "Print version information") PRINT_VERSION *bool = flag.Bool("v", false, "Print version information")
PRINT_LICENSE *bool = flag.Bool("l", false, "Print license information") PRINT_LICENSE *bool = flag.Bool("l", false, "Print license information")
@ -51,14 +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] 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")
@ -81,7 +83,6 @@ func init() {
fmt.Printf("| ftu -r -s /home/user/homework/\n") fmt.Printf("| ftu -r -s /home/user/homework/\n")
fmt.Printf("| creates a node that will send every file in the directory !RECUSRIVELY!\n\n\n") fmt.Printf("| creates a node that will send every file in the directory !RECUSRIVELY!\n\n\n")
} }
flag.Parse() flag.Parse()
@ -97,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 only -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)
} }
@ -118,13 +119,14 @@ func init() {
func main() { func main() {
nodeOptions := node.NodeOptions{ nodeOptions := node.NodeOptions{
IsSending: isSending, VerboseOutput: *VERBOSE,
WorkingPort: *PORT, IsSending: isSending,
ServerSide: &node.ServerSideNodeOptions{ WorkingPort: *PORT,
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,
}, },
@ -132,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)
} }

1065
src/node/node.go

File diff suppressed because it is too large Load Diff

15
src/node/options.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021 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,20 +20,21 @@ 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
} }
// Options to configure the node // Options to configure the node
type NodeOptions struct { type NodeOptions struct {
IsSending bool IsSending bool
WorkingPort uint WorkingPort uint
ServerSide *ServerSideNodeOptions VerboseOutput bool
ClientSide *ClientSideNodeOptions SenderSide *SenderNodeOptions
ReceiverSide *ReceiverNodeOptions
} }

2
src/protocol/constants.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021 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

17
src/protocol/headers.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021 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
@ -29,7 +29,7 @@ type Header string
//// and (size) is 8 bytes long big-endian binary encoded uint64 //// and (size) is 8 bytes long big-endian binary encoded uint64
// ENCRKEY. // ENCRKEY.
// The FIRST header to be sent. Sent immediately after the connection has been established // The FIRST header to be sent if you`re going to encrypt the transfer. Sent immediately after the connection has been established
// by sender. Body contains a size of a key and the key itself. // by sender. Body contains a size of a key and the key itself.
// ie: ENCRKEY~(size)(encryption key) // ie: ENCRKEY~(size)(encryption key)
const HeaderEncryptionKey Header = "ENCRKEY" const HeaderEncryptionKey Header = "ENCRKEY"
@ -41,7 +41,7 @@ const HeaderReject Header = "REJECT"
// ACCEPT. // ACCEPT.
// The opposite of the previous REJECT. Sent by receiver when // The opposite of the previous REJECT. Sent by receiver when
// he has agreed to download the file|directory. // it has agreed to download the file|directory.
// ie: ACCEPT~ // ie: ACCEPT~
const HeaderAccept Header = "ACCEPT" const HeaderAccept Header = "ACCEPT"
@ -55,8 +55,8 @@ const HeaderDone Header = "DONE"
// READY. // READY.
// Sent by receiver when it has read and processed the last // Sent by receiver when it has read and processed the last
// FILEBYTES packet. The sender is not allowed to "spam" FILEBYTES // FILEBYTES or FILE packet. The sender is not allowed to "spam" FILEBYTES or FILE
// packets without the permission of receiver. // packets without the permission (packet with this header) from receiver.
// ie: READY!~ // ie: READY!~
const HeaderReady Header = "READY" const HeaderReady Header = "READY"
@ -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 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 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 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 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 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

60
src/protocol/send.go

@ -1,6 +1,6 @@
/* /*
ftu - file transferring utility. ftu - file transferring utility.
Copyright (C) 2021 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.
@ -137,13 +137,15 @@ func SendTransferOffer(connection net.Conn, file *fsys.File, dir *fsys.Directory
var ErrorSentAll error = fmt.Errorf("sent the whole file") var ErrorSentAll error = fmt.Errorf("sent the whole file")
// sends a piece of file to the connection; The next calls will send // Sends a piece of file to the connection; The next calls will send
// another piece util the file has been fully sent. If encrKey is not nil - encrypts each packet with // another piece util the file has been fully sent. If encrKey is not nil - encrypts each packet with
// this key // this key. Returns amount of filebytes written to the connection
func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) error { func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) (uint64, error) {
var sentBytes uint64 = 0
err := file.Open() err := file.Open()
if err != nil { if err != nil {
return err return sentBytes, err
} }
defer file.Close() defer file.Close()
@ -152,7 +154,7 @@ func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) error {
} }
if file.Size == file.SentBytes { if file.Size == file.SentBytes {
return ErrorSentAll return sentBytes, ErrorSentAll
} }
fileBytesPacket := Packet{ fileBytesPacket := Packet{
@ -164,7 +166,7 @@ func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) error {
// write file ID first // write file ID first
err = binary.Write(packetBodyBuff, binary.BigEndian, file.ID) err = binary.Write(packetBodyBuff, binary.BigEndian, file.ID)
if err != nil { if err != nil {
return err return sentBytes, err
} }
// fill the remaining space of packet with the contents of a file // fill the remaining space of packet with the contents of a file
@ -172,7 +174,7 @@ func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) error {
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 {
@ -183,9 +185,10 @@ func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) error {
read, err := file.Handler.ReadAt(fileBytes, int64(file.SentBytes)) read, err := file.Handler.ReadAt(fileBytes, int64(file.SentBytes))
if err != nil { if err != nil {
return err return sentBytes, err
} }
file.SentBytes += uint64(read) file.SentBytes += uint64(read)
sentBytes += uint64(canSendBytes)
packetBodyBuff.Write(fileBytes) packetBodyBuff.Write(fileBytes)
@ -194,12 +197,45 @@ func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) error {
if encrKey != nil { if encrKey != nil {
err = fileBytesPacket.EncryptBody(encrKey) err = fileBytesPacket.EncryptBody(encrKey)
if err != nil { if err != nil {
return err return sentBytes, err
} }
} }
// send it to the other side // send it to the other side
err = SendPacket(connection, fileBytesPacket) err = SendPacket(connection, fileBytesPacket)
if err != nil {
return 0, err
}
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 { if err != nil {
return err return err
} }

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