2018-09-22 05:17:57 +02:00
|
|
|
package croc
|
|
|
|
|
2018-09-22 05:51:43 +02:00
|
|
|
import (
|
2019-04-30 00:48:17 +02:00
|
|
|
"bytes"
|
|
|
|
"crypto/elliptic"
|
2019-04-30 05:40:42 +02:00
|
|
|
"encoding/binary"
|
2019-04-30 00:48:17 +02:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2019-04-30 05:40:42 +02:00
|
|
|
"io"
|
|
|
|
"math"
|
2019-04-30 00:48:17 +02:00
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
2018-09-22 05:51:43 +02:00
|
|
|
"time"
|
|
|
|
|
2019-04-30 00:54:31 +02:00
|
|
|
log "github.com/cihub/seelog"
|
2019-04-30 00:48:17 +02:00
|
|
|
"github.com/denisbrodbeck/machineid"
|
2019-04-30 04:50:01 +02:00
|
|
|
"github.com/pkg/errors"
|
2019-04-30 00:54:31 +02:00
|
|
|
"github.com/schollz/croc/v6/src/comm"
|
2019-04-30 05:40:42 +02:00
|
|
|
"github.com/schollz/croc/v6/src/compress"
|
2019-04-30 00:54:31 +02:00
|
|
|
"github.com/schollz/croc/v6/src/crypt"
|
|
|
|
"github.com/schollz/croc/v6/src/logger"
|
2019-04-30 04:50:01 +02:00
|
|
|
"github.com/schollz/croc/v6/src/message"
|
2019-04-30 15:57:45 +02:00
|
|
|
"github.com/schollz/croc/v6/src/models"
|
2019-04-30 04:50:01 +02:00
|
|
|
"github.com/schollz/croc/v6/src/tcp"
|
2019-04-30 00:54:31 +02:00
|
|
|
"github.com/schollz/croc/v6/src/utils"
|
2019-04-30 00:48:17 +02:00
|
|
|
"github.com/schollz/pake"
|
2018-10-17 19:20:09 +02:00
|
|
|
"github.com/schollz/progressbar/v2"
|
2019-04-30 00:48:17 +02:00
|
|
|
"github.com/schollz/spinner"
|
2018-09-22 05:51:43 +02:00
|
|
|
)
|
2018-09-22 05:17:57 +02:00
|
|
|
|
2018-09-25 21:33:51 +02:00
|
|
|
func init() {
|
2019-04-30 00:54:31 +02:00
|
|
|
logger.SetLogLevel("debug")
|
2019-04-30 00:48:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func Debug(debug bool) {
|
2018-09-22 05:51:43 +02:00
|
|
|
if debug {
|
2019-04-30 00:54:31 +02:00
|
|
|
logger.SetLogLevel("debug")
|
2019-04-30 00:48:17 +02:00
|
|
|
} else {
|
2019-04-30 00:54:31 +02:00
|
|
|
logger.SetLogLevel("warn")
|
2019-04-30 00:48:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-30 00:54:31 +02:00
|
|
|
type Options struct {
|
|
|
|
IsSender bool
|
|
|
|
SharedSecret string
|
|
|
|
Debug bool
|
2019-04-30 04:50:01 +02:00
|
|
|
RelayAddress string
|
|
|
|
RelayPorts []string
|
2019-04-30 00:54:31 +02:00
|
|
|
Stdout bool
|
|
|
|
NoPrompt bool
|
|
|
|
}
|
|
|
|
|
2019-04-30 00:48:17 +02:00
|
|
|
type Client struct {
|
|
|
|
Options Options
|
|
|
|
Pake *pake.Pake
|
2019-04-30 04:50:01 +02:00
|
|
|
Key crypt.Encryption
|
2019-04-30 00:48:17 +02:00
|
|
|
|
|
|
|
// steps involved in forming relationship
|
|
|
|
Step1ChannelSecured bool
|
|
|
|
Step2FileInfoTransfered bool
|
|
|
|
Step3RecipientRequestFile bool
|
|
|
|
Step4FileTransfer bool
|
2019-04-30 00:54:31 +02:00
|
|
|
Step5CloseChannels bool
|
2019-04-30 00:48:17 +02:00
|
|
|
|
|
|
|
// send / receive information of all files
|
|
|
|
FilesToTransfer []FileInfo
|
|
|
|
FilesToTransferCurrentNum int
|
|
|
|
|
|
|
|
// send / receive information of current file
|
|
|
|
CurrentFile *os.File
|
|
|
|
CurrentFileChunks []int64
|
2019-04-30 06:25:30 +02:00
|
|
|
TotalSent int64
|
2019-04-30 00:48:17 +02:00
|
|
|
|
2019-04-30 04:50:01 +02:00
|
|
|
// tcp connections
|
|
|
|
conn []*comm.Comm
|
2019-04-30 00:48:17 +02:00
|
|
|
|
|
|
|
bar *progressbar.ProgressBar
|
|
|
|
spinner *spinner.Spinner
|
|
|
|
machineID string
|
|
|
|
|
|
|
|
mutex *sync.Mutex
|
|
|
|
quit chan bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type Chunk struct {
|
|
|
|
Bytes []byte `json:"b,omitempty"`
|
|
|
|
Location int64 `json:"l,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type FileInfo struct {
|
|
|
|
Name string `json:"n,omitempty"`
|
|
|
|
FolderRemote string `json:"fr,omitempty"`
|
|
|
|
FolderSource string `json:"fs,omitempty"`
|
|
|
|
Hash []byte `json:"h,omitempty"`
|
|
|
|
Size int64 `json:"s,omitempty"`
|
|
|
|
ModTime time.Time `json:"m,omitempty"`
|
|
|
|
IsCompressed bool `json:"c,omitempty"`
|
|
|
|
IsEncrypted bool `json:"e,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type RemoteFileRequest struct {
|
|
|
|
CurrentFileChunks []int64
|
|
|
|
FilesToTransferCurrentNum int
|
|
|
|
}
|
|
|
|
|
|
|
|
type SenderInfo struct {
|
|
|
|
MachineID string
|
|
|
|
FilesToTransfer []FileInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
// New establishes a new connection for transfering files between two instances.
|
|
|
|
func New(ops Options) (c *Client, err error) {
|
|
|
|
c = new(Client)
|
|
|
|
|
|
|
|
// setup basic info
|
|
|
|
c.Options = ops
|
|
|
|
Debug(c.Options.Debug)
|
|
|
|
log.Debugf("options: %+v", c.Options)
|
|
|
|
|
2019-04-30 04:50:01 +02:00
|
|
|
log.Debug("establishing connection")
|
|
|
|
c.conn = make([]*comm.Comm, len(c.Options.RelayPorts))
|
|
|
|
// connect to the relay for messaging
|
|
|
|
c.conn[0], err = tcp.ConnectToTCPServer(fmt.Sprintf("%s:%s", c.Options.RelayAddress, c.Options.RelayPorts[0]), c.Options.SharedSecret)
|
|
|
|
if err != nil {
|
|
|
|
err = errors.Wrap(err, fmt.Sprintf("could not connect to %s:%s", c.Options.RelayAddress, c.Options.RelayPorts[0]))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
log.Debugf("connection established: %+v", c.conn[0])
|
|
|
|
|
|
|
|
// use default key (no encryption, until PAKE succeeds)
|
|
|
|
c.Key, err = crypt.New(nil, nil)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-30 00:48:17 +02:00
|
|
|
// initialize pake
|
|
|
|
if c.Options.IsSender {
|
|
|
|
c.Pake, err = pake.Init([]byte(c.Options.SharedSecret), 1, elliptic.P521(), 1*time.Microsecond)
|
|
|
|
} else {
|
|
|
|
c.Pake, err = pake.Init([]byte(c.Options.SharedSecret), 0, elliptic.P521(), 1*time.Microsecond)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.mutex = &sync.Mutex{}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-30 04:50:01 +02:00
|
|
|
// TransferOptions for sending
|
2019-04-30 00:48:17 +02:00
|
|
|
type TransferOptions struct {
|
|
|
|
PathToFiles []string
|
|
|
|
KeepPathInRemote bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send will send the specified file
|
|
|
|
func (c *Client) Send(options TransferOptions) (err error) {
|
|
|
|
return c.transfer(options)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Receive will receive a file
|
|
|
|
func (c *Client) Receive() (err error) {
|
|
|
|
return c.transfer(TransferOptions{})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) transfer(options TransferOptions) (err error) {
|
2019-04-30 04:50:01 +02:00
|
|
|
// connect to the server
|
|
|
|
|
2019-04-30 00:48:17 +02:00
|
|
|
if c.Options.IsSender {
|
|
|
|
c.FilesToTransfer = make([]FileInfo, len(options.PathToFiles))
|
|
|
|
totalFilesSize := int64(0)
|
|
|
|
for i, pathToFile := range options.PathToFiles {
|
|
|
|
var fstats os.FileInfo
|
|
|
|
var fullPath string
|
|
|
|
fullPath, err = filepath.Abs(pathToFile)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fullPath = filepath.Clean(fullPath)
|
|
|
|
var folderName string
|
|
|
|
folderName, _ = filepath.Split(fullPath)
|
|
|
|
|
|
|
|
fstats, err = os.Stat(fullPath)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.FilesToTransfer[i] = FileInfo{
|
|
|
|
Name: fstats.Name(),
|
|
|
|
FolderRemote: ".",
|
|
|
|
FolderSource: folderName,
|
|
|
|
Size: fstats.Size(),
|
|
|
|
ModTime: fstats.ModTime(),
|
|
|
|
}
|
|
|
|
c.FilesToTransfer[i].Hash, err = utils.HashFile(fullPath)
|
|
|
|
totalFilesSize += fstats.Size()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if options.KeepPathInRemote {
|
|
|
|
var curFolder string
|
|
|
|
curFolder, err = os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
curFolder, err = filepath.Abs(curFolder)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !strings.HasPrefix(folderName, curFolder) {
|
|
|
|
err = fmt.Errorf("remote directory must be relative to current")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.FilesToTransfer[i].FolderRemote = strings.TrimPrefix(folderName, curFolder)
|
|
|
|
c.FilesToTransfer[i].FolderRemote = filepath.ToSlash(c.FilesToTransfer[i].FolderRemote)
|
|
|
|
c.FilesToTransfer[i].FolderRemote = strings.TrimPrefix(c.FilesToTransfer[i].FolderRemote, "/")
|
|
|
|
if c.FilesToTransfer[i].FolderRemote == "" {
|
|
|
|
c.FilesToTransfer[i].FolderRemote = "."
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log.Debugf("file %d info: %+v", i, c.FilesToTransfer[i])
|
|
|
|
}
|
|
|
|
fname := fmt.Sprintf("%d files", len(c.FilesToTransfer))
|
|
|
|
if len(c.FilesToTransfer) == 1 {
|
|
|
|
fname = fmt.Sprintf("'%s'", c.FilesToTransfer[0].Name)
|
|
|
|
}
|
|
|
|
machID, macIDerr := machineid.ID()
|
|
|
|
if macIDerr != nil {
|
|
|
|
log.Error(macIDerr)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(machID) > 6 {
|
|
|
|
machID = machID[:6]
|
|
|
|
}
|
|
|
|
c.machineID = machID
|
|
|
|
fmt.Fprintf(os.Stderr, "Sending %s (%s) from your machine, '%s'\n", fname, utils.ByteCountDecimal(totalFilesSize), machID)
|
|
|
|
fmt.Fprintf(os.Stderr, "Code is: %s\nOn the other computer run\n\ncroc %s\n", c.Options.SharedSecret, c.Options.SharedSecret)
|
2019-04-30 04:50:01 +02:00
|
|
|
// // c.spinner.Suffix = " waiting for recipient..."
|
2019-04-30 00:48:17 +02:00
|
|
|
}
|
2019-04-30 04:50:01 +02:00
|
|
|
// c.spinner.Start()
|
2019-04-30 00:48:17 +02:00
|
|
|
// create channel for quitting
|
|
|
|
// quit with c.quit <- true
|
|
|
|
c.quit = make(chan bool)
|
|
|
|
|
|
|
|
// if recipient, initialize with sending pake information
|
2019-04-30 01:05:48 +02:00
|
|
|
log.Debug("ready")
|
2019-04-30 00:48:17 +02:00
|
|
|
if !c.Options.IsSender && !c.Step1ChannelSecured {
|
2019-04-30 04:50:01 +02:00
|
|
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
2019-04-30 00:48:17 +02:00
|
|
|
Type: "pake",
|
|
|
|
Bytes: c.Pake.Bytes(),
|
2019-04-30 04:50:01 +02:00
|
|
|
})
|
2019-04-30 00:48:17 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// listen for incoming messages and process them
|
|
|
|
for {
|
2019-04-30 04:50:01 +02:00
|
|
|
var data []byte
|
2019-04-30 05:40:42 +02:00
|
|
|
var done bool
|
2019-04-30 04:50:01 +02:00
|
|
|
data, err = c.conn[0].Receive()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2019-04-30 05:40:42 +02:00
|
|
|
done, err = c.processMessage(data)
|
2019-04-30 04:50:01 +02:00
|
|
|
if err != nil {
|
2019-04-30 00:48:17 +02:00
|
|
|
return
|
|
|
|
}
|
2019-04-30 05:40:42 +02:00
|
|
|
if done {
|
|
|
|
break
|
|
|
|
}
|
2019-04-30 00:48:17 +02:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-30 05:40:42 +02:00
|
|
|
func (c *Client) processMessage(payload []byte) (done bool, err error) {
|
2019-04-30 04:50:01 +02:00
|
|
|
m, err := message.Decode(c.Key, payload)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-30 00:48:17 +02:00
|
|
|
switch m.Type {
|
2019-04-30 05:40:42 +02:00
|
|
|
case "finished":
|
|
|
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
|
|
|
Type: "finished",
|
|
|
|
})
|
|
|
|
done = true
|
|
|
|
return
|
2019-04-30 00:48:17 +02:00
|
|
|
case "pake":
|
2019-04-30 04:50:01 +02:00
|
|
|
// if // c.spinner.Suffix != " performing PAKE..." {
|
|
|
|
// // c.spinner.Stop()
|
|
|
|
// // c.spinner.Suffix = " performing PAKE..."
|
|
|
|
// // c.spinner.Start()
|
|
|
|
// }
|
2019-04-30 00:48:17 +02:00
|
|
|
notVerified := !c.Pake.IsVerified()
|
|
|
|
err = c.Pake.Update(m.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (notVerified && c.Pake.IsVerified() && !c.Options.IsSender) || !c.Pake.IsVerified() {
|
2019-04-30 04:50:01 +02:00
|
|
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
2019-04-30 00:48:17 +02:00
|
|
|
Type: "pake",
|
|
|
|
Bytes: c.Pake.Bytes(),
|
2019-04-30 04:50:01 +02:00
|
|
|
})
|
2019-04-30 00:48:17 +02:00
|
|
|
}
|
|
|
|
if c.Pake.IsVerified() {
|
2019-04-30 04:50:01 +02:00
|
|
|
log.Debug("session key is verified, generating encryption")
|
|
|
|
key, err := c.Pake.SessionKey()
|
|
|
|
if err != nil {
|
2019-04-30 05:40:42 +02:00
|
|
|
return true, err
|
2019-04-30 04:50:01 +02:00
|
|
|
}
|
|
|
|
c.Key, err = crypt.New(key, []byte(c.Options.SharedSecret))
|
|
|
|
if err != nil {
|
2019-04-30 05:40:42 +02:00
|
|
|
return true, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// connects to the other ports of the server for transfer
|
2019-04-30 16:07:23 +02:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(len(c.Options.RelayPorts) - 1)
|
2019-04-30 05:40:42 +02:00
|
|
|
for i := 1; i < len(c.Options.RelayPorts); i++ {
|
2019-04-30 16:07:23 +02:00
|
|
|
go func(j int) {
|
|
|
|
defer wg.Done()
|
|
|
|
c.conn[j], err = tcp.ConnectToTCPServer(
|
|
|
|
fmt.Sprintf("%s:%s", c.Options.RelayAddress, c.Options.RelayPorts[j]),
|
|
|
|
fmt.Sprintf("%s-%d", utils.SHA256(c.Options.SharedSecret)[:7], j),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
if !c.Options.IsSender {
|
|
|
|
go c.receiveData(j)
|
|
|
|
}
|
|
|
|
}(i)
|
2019-04-30 04:50:01 +02:00
|
|
|
}
|
2019-04-30 16:07:23 +02:00
|
|
|
wg.Wait()
|
2019-04-30 00:48:17 +02:00
|
|
|
c.Step1ChannelSecured = true
|
|
|
|
}
|
|
|
|
case "error":
|
2019-04-30 04:50:01 +02:00
|
|
|
// c.spinner.Stop()
|
2019-04-30 00:48:17 +02:00
|
|
|
fmt.Print("\r")
|
|
|
|
err = fmt.Errorf("peer error: %s", m.Message)
|
2019-04-30 05:40:42 +02:00
|
|
|
return true, err
|
2019-04-30 00:48:17 +02:00
|
|
|
case "fileinfo":
|
|
|
|
var senderInfo SenderInfo
|
2019-04-30 04:50:01 +02:00
|
|
|
err = json.Unmarshal(m.Bytes, &senderInfo)
|
2019-04-30 00:48:17 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.FilesToTransfer = senderInfo.FilesToTransfer
|
|
|
|
fname := fmt.Sprintf("%d files", len(c.FilesToTransfer))
|
|
|
|
if len(c.FilesToTransfer) == 1 {
|
|
|
|
fname = fmt.Sprintf("'%s'", c.FilesToTransfer[0].Name)
|
|
|
|
}
|
|
|
|
totalSize := int64(0)
|
|
|
|
for _, fi := range c.FilesToTransfer {
|
|
|
|
totalSize += fi.Size
|
|
|
|
}
|
2019-04-30 04:50:01 +02:00
|
|
|
// c.spinner.Stop()
|
2019-04-30 00:48:17 +02:00
|
|
|
if !c.Options.NoPrompt {
|
|
|
|
fmt.Fprintf(os.Stderr, "\rAccept %s (%s) from machine '%s'? (y/n) ", fname, utils.ByteCountDecimal(totalSize), senderInfo.MachineID)
|
|
|
|
if strings.ToLower(strings.TrimSpace(utils.GetInput(""))) != "y" {
|
2019-04-30 04:50:01 +02:00
|
|
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
2019-04-30 00:48:17 +02:00
|
|
|
Type: "error",
|
|
|
|
Message: "refusing files",
|
2019-04-30 04:50:01 +02:00
|
|
|
})
|
2019-04-30 05:40:42 +02:00
|
|
|
return true, fmt.Errorf("refused files")
|
2019-04-30 00:48:17 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(os.Stderr, "\rReceiving %s (%s) from machine '%s'\n", fname, utils.ByteCountDecimal(totalSize), senderInfo.MachineID)
|
|
|
|
}
|
2019-04-30 01:05:48 +02:00
|
|
|
log.Debug(c.FilesToTransfer)
|
2019-04-30 00:48:17 +02:00
|
|
|
c.Step2FileInfoTransfered = true
|
|
|
|
case "recipientready":
|
|
|
|
var remoteFile RemoteFileRequest
|
2019-04-30 04:50:01 +02:00
|
|
|
err = json.Unmarshal(m.Bytes, &remoteFile)
|
2019-04-30 00:48:17 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.FilesToTransferCurrentNum = remoteFile.FilesToTransferCurrentNum
|
|
|
|
c.CurrentFileChunks = remoteFile.CurrentFileChunks
|
|
|
|
c.Step3RecipientRequestFile = true
|
|
|
|
case "close-sender":
|
|
|
|
log.Debug("close-sender received...")
|
|
|
|
c.Step4FileTransfer = false
|
|
|
|
c.Step3RecipientRequestFile = false
|
|
|
|
log.Debug("sending close-recipient")
|
2019-04-30 04:50:01 +02:00
|
|
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
2019-04-30 00:48:17 +02:00
|
|
|
Type: "close-recipient",
|
2019-04-30 04:50:01 +02:00
|
|
|
})
|
2019-04-30 00:48:17 +02:00
|
|
|
case "close-recipient":
|
|
|
|
c.Step4FileTransfer = false
|
|
|
|
c.Step3RecipientRequestFile = false
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = c.updateState()
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) updateState() (err error) {
|
|
|
|
if c.Options.IsSender && c.Step1ChannelSecured && !c.Step2FileInfoTransfered {
|
|
|
|
var b []byte
|
|
|
|
b, err = json.Marshal(SenderInfo{
|
|
|
|
MachineID: c.machineID,
|
|
|
|
FilesToTransfer: c.FilesToTransfer,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2019-04-30 04:50:01 +02:00
|
|
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
2019-04-30 00:48:17 +02:00
|
|
|
Type: "fileinfo",
|
2019-04-30 04:50:01 +02:00
|
|
|
Bytes: b,
|
|
|
|
})
|
2019-04-30 00:48:17 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.Step2FileInfoTransfered = true
|
|
|
|
}
|
|
|
|
if !c.Options.IsSender && c.Step2FileInfoTransfered && !c.Step3RecipientRequestFile {
|
|
|
|
// find the next file to transfer and send that number
|
|
|
|
// if the files are the same size, then look for missing chunks
|
|
|
|
finished := true
|
|
|
|
for i, fileInfo := range c.FilesToTransfer {
|
|
|
|
if i < c.FilesToTransferCurrentNum {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fileHash, errHash := utils.HashFile(path.Join(fileInfo.FolderRemote, fileInfo.Name))
|
|
|
|
if errHash != nil || !bytes.Equal(fileHash, fileInfo.Hash) {
|
|
|
|
if !bytes.Equal(fileHash, fileInfo.Hash) {
|
|
|
|
log.Debugf("hashes are not equal %x != %x", fileHash, fileInfo.Hash)
|
|
|
|
}
|
|
|
|
finished = false
|
|
|
|
c.FilesToTransferCurrentNum = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// TODO: print out something about this file already existing
|
|
|
|
}
|
|
|
|
if finished {
|
|
|
|
// TODO: do the last finishing stuff
|
|
|
|
log.Debug("finished")
|
2019-04-30 04:50:01 +02:00
|
|
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
2019-04-30 00:48:17 +02:00
|
|
|
Type: "finished",
|
2019-04-30 04:50:01 +02:00
|
|
|
})
|
2019-04-30 00:48:17 +02:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// start initiating the process to receive a new file
|
|
|
|
log.Debugf("working on file %d", c.FilesToTransferCurrentNum)
|
|
|
|
|
2019-04-30 05:40:42 +02:00
|
|
|
// recipient sets the file
|
|
|
|
pathToFile := path.Join(
|
|
|
|
c.FilesToTransfer[c.FilesToTransferCurrentNum].FolderRemote,
|
|
|
|
c.FilesToTransfer[c.FilesToTransferCurrentNum].Name,
|
|
|
|
)
|
|
|
|
folderForFile, _ := filepath.Split(pathToFile)
|
|
|
|
os.MkdirAll(folderForFile, os.ModePerm)
|
|
|
|
var errOpen error
|
|
|
|
c.CurrentFile, errOpen = os.OpenFile(
|
|
|
|
pathToFile,
|
|
|
|
os.O_WRONLY, 0666)
|
|
|
|
truncate := false
|
|
|
|
if errOpen == nil {
|
|
|
|
stat, _ := c.CurrentFile.Stat()
|
|
|
|
truncate = stat.Size() != c.FilesToTransfer[c.FilesToTransferCurrentNum].Size
|
|
|
|
} else {
|
|
|
|
c.CurrentFile, errOpen = os.Create(pathToFile)
|
|
|
|
if errOpen != nil {
|
|
|
|
errOpen = errors.Wrap(errOpen, "could not create "+pathToFile)
|
|
|
|
log.Error(errOpen)
|
|
|
|
return errOpen
|
|
|
|
}
|
|
|
|
truncate = true
|
|
|
|
}
|
|
|
|
if truncate {
|
|
|
|
err := c.CurrentFile.Truncate(c.FilesToTransfer[c.FilesToTransferCurrentNum].Size)
|
|
|
|
if err != nil {
|
|
|
|
err = errors.Wrap(err, "could not truncate "+pathToFile)
|
|
|
|
log.Error(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// setup the progressbar
|
|
|
|
c.bar = progressbar.NewOptions64(
|
|
|
|
c.FilesToTransfer[c.FilesToTransferCurrentNum].Size,
|
|
|
|
progressbar.OptionOnCompletion(func() {
|
|
|
|
fmt.Println(" ✔️")
|
|
|
|
}),
|
|
|
|
progressbar.OptionSetWidth(8),
|
|
|
|
progressbar.OptionSetDescription(c.FilesToTransfer[c.FilesToTransferCurrentNum].Name),
|
|
|
|
progressbar.OptionSetRenderBlankState(true),
|
|
|
|
progressbar.OptionSetBytes64(c.FilesToTransfer[c.FilesToTransferCurrentNum].Size),
|
|
|
|
progressbar.OptionSetWriter(os.Stderr),
|
|
|
|
progressbar.OptionThrottle(100*time.Millisecond),
|
|
|
|
)
|
2019-04-30 06:25:30 +02:00
|
|
|
c.TotalSent = 0
|
2019-04-30 05:40:42 +02:00
|
|
|
|
2019-04-30 00:48:17 +02:00
|
|
|
// recipient requests the file and chunks (if empty, then should receive all chunks)
|
|
|
|
bRequest, _ := json.Marshal(RemoteFileRequest{
|
|
|
|
CurrentFileChunks: c.CurrentFileChunks,
|
|
|
|
FilesToTransferCurrentNum: c.FilesToTransferCurrentNum,
|
|
|
|
})
|
2019-04-30 04:50:01 +02:00
|
|
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
2019-04-30 00:48:17 +02:00
|
|
|
Type: "recipientready",
|
2019-04-30 04:50:01 +02:00
|
|
|
Bytes: bRequest,
|
|
|
|
})
|
2019-04-30 00:48:17 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.Step3RecipientRequestFile = true
|
|
|
|
}
|
|
|
|
if c.Options.IsSender && c.Step3RecipientRequestFile && !c.Step4FileTransfer {
|
2019-04-30 01:05:48 +02:00
|
|
|
log.Debug("start sending data!")
|
2019-04-30 00:48:17 +02:00
|
|
|
c.Step4FileTransfer = true
|
2019-04-30 05:40:42 +02:00
|
|
|
// setup the progressbar
|
|
|
|
c.bar = progressbar.NewOptions64(
|
|
|
|
c.FilesToTransfer[c.FilesToTransferCurrentNum].Size,
|
|
|
|
progressbar.OptionOnCompletion(func() {
|
|
|
|
fmt.Println(" ✔️")
|
|
|
|
}),
|
|
|
|
progressbar.OptionSetWidth(8),
|
|
|
|
progressbar.OptionSetDescription(c.FilesToTransfer[c.FilesToTransferCurrentNum].Name),
|
|
|
|
progressbar.OptionSetRenderBlankState(true),
|
|
|
|
progressbar.OptionSetBytes64(c.FilesToTransfer[c.FilesToTransferCurrentNum].Size),
|
|
|
|
progressbar.OptionSetWriter(os.Stderr),
|
|
|
|
progressbar.OptionThrottle(100*time.Millisecond),
|
|
|
|
)
|
2019-04-30 06:25:30 +02:00
|
|
|
c.TotalSent = 0
|
2019-04-30 05:40:42 +02:00
|
|
|
for i := 1; i < len(c.Options.RelayPorts); i++ {
|
|
|
|
go c.sendData(i)
|
|
|
|
}
|
2019-04-30 00:48:17 +02:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-30 05:40:42 +02:00
|
|
|
func (c *Client) receiveData(i int) {
|
|
|
|
for {
|
2019-04-30 15:57:45 +02:00
|
|
|
log.Debug("waiting for data")
|
2019-04-30 05:40:42 +02:00
|
|
|
data, err := c.conn[i].Receive()
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("%s: %s", i, err.Error())
|
2019-04-30 15:57:45 +02:00
|
|
|
continue
|
2019-04-30 05:40:42 +02:00
|
|
|
}
|
2019-04-30 15:57:45 +02:00
|
|
|
|
2019-04-30 05:40:42 +02:00
|
|
|
data, err = c.Key.Decrypt(data)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
data = compress.Decompress(data)
|
|
|
|
|
|
|
|
// get position
|
|
|
|
var position uint64
|
|
|
|
rbuf := bytes.NewReader(data[:8])
|
|
|
|
err = binary.Read(rbuf, binary.LittleEndian, &position)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("binary.Read failed:", err)
|
|
|
|
}
|
|
|
|
positionInt64 := int64(position)
|
|
|
|
|
|
|
|
c.mutex.Lock()
|
2019-04-30 06:25:30 +02:00
|
|
|
_, err = c.CurrentFile.WriteAt(data[8:], positionInt64)
|
2019-04-30 05:40:42 +02:00
|
|
|
c.mutex.Unlock()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-04-30 06:25:30 +02:00
|
|
|
c.bar.Add(len(data[8:]))
|
|
|
|
c.TotalSent += int64(len(data[8:]))
|
2019-04-30 15:57:45 +02:00
|
|
|
log.Debugf("block: %+v", positionInt64)
|
2019-04-30 06:25:30 +02:00
|
|
|
if c.TotalSent == c.FilesToTransfer[c.FilesToTransferCurrentNum].Size {
|
2019-04-30 05:40:42 +02:00
|
|
|
log.Debug("finished receiving!")
|
|
|
|
c.CurrentFile.Close()
|
|
|
|
log.Debug("sending close-sender")
|
|
|
|
err = message.Send(c.conn[0], c.Key, message.Message{
|
|
|
|
Type: "close-sender",
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
2019-04-30 00:48:17 +02:00
|
|
|
}
|
|
|
|
|
2019-04-30 05:40:42 +02:00
|
|
|
return
|
2019-04-30 00:48:17 +02:00
|
|
|
}
|
|
|
|
|
2019-04-30 05:40:42 +02:00
|
|
|
func (c *Client) sendData(i int) {
|
2019-04-30 06:25:30 +02:00
|
|
|
defer func() {
|
|
|
|
log.Debugf("finished with %d", i)
|
|
|
|
}()
|
2019-04-30 05:40:42 +02:00
|
|
|
pathToFile := path.Join(
|
|
|
|
c.FilesToTransfer[c.FilesToTransferCurrentNum].FolderSource,
|
|
|
|
c.FilesToTransfer[c.FilesToTransferCurrentNum].Name,
|
|
|
|
)
|
|
|
|
log.Debugf("opening %s to read", pathToFile)
|
|
|
|
f, err := os.Open(pathToFile)
|
2019-04-30 00:48:17 +02:00
|
|
|
if err != nil {
|
2019-04-30 05:40:42 +02:00
|
|
|
panic(err)
|
2019-04-30 00:48:17 +02:00
|
|
|
}
|
2019-04-30 05:40:42 +02:00
|
|
|
defer f.Close()
|
2019-04-30 00:48:17 +02:00
|
|
|
|
2019-04-30 05:40:42 +02:00
|
|
|
pos := uint64(0)
|
|
|
|
curi := float64(0)
|
|
|
|
for {
|
|
|
|
// Read file
|
2019-04-30 15:57:45 +02:00
|
|
|
data := make([]byte, models.TCP_BUFFER_SIZE/2)
|
2019-04-30 05:40:42 +02:00
|
|
|
n, err := f.Read(data)
|
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF {
|
2019-04-30 15:57:45 +02:00
|
|
|
break
|
2019-04-30 05:40:42 +02:00
|
|
|
}
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if math.Mod(curi, float64(len(c.Options.RelayPorts)-1))+1 == float64(i) {
|
|
|
|
posByte := make([]byte, 8)
|
|
|
|
binary.LittleEndian.PutUint64(posByte, pos)
|
|
|
|
|
|
|
|
dataToSend, err := c.Key.Encrypt(
|
|
|
|
compress.Compress(
|
|
|
|
append(posByte, data[:n]...),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = c.conn[i].Send(dataToSend)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
c.bar.Add(n)
|
2019-04-30 06:25:30 +02:00
|
|
|
c.TotalSent += int64(n)
|
2019-04-30 16:07:23 +02:00
|
|
|
// time.Sleep(100 * time.Millisecond)
|
2019-04-30 05:40:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
curi++
|
|
|
|
pos += uint64(n)
|
|
|
|
}
|
|
|
|
|
2019-04-30 15:57:45 +02:00
|
|
|
time.Sleep(10 * time.Second)
|
|
|
|
return
|
2018-09-22 05:51:43 +02:00
|
|
|
}
|