diff --git a/src/client.go b/src/client.go index 9bf48de..57dfc7a 100644 --- a/src/client.go +++ b/src/client.go @@ -8,8 +8,6 @@ import ( "net/url" "os" "os/signal" - "path" - "path/filepath" "strconv" "strings" "sync" @@ -19,7 +17,6 @@ import ( "github.com/gorilla/websocket" "github.com/pkg/errors" "github.com/schollz/croc/src/pake" - tarinator "github.com/schollz/tarinator-go" ) func (c *Croc) client(role int, codePhrase string, fname ...string) (err error) { @@ -231,8 +228,9 @@ func (c *Croc) processState(ws *websocket.Conn, cd channelData) (err error) { c.cs.channel.Update = false } } - if c.cs.channel.Role == 0 && c.cs.channel.Pake.IsVerified() && !c.cs.channel.notSentMetaData { + if c.cs.channel.Role == 0 && c.cs.channel.Pake.IsVerified() && !c.cs.channel.notSentMetaData && !c.cs.channel.filesReady { go c.getFilesReady(ws) + c.cs.channel.filesReady = true } // process the client state @@ -267,74 +265,6 @@ func (c *Croc) processState(ws *websocket.Conn, cd channelData) (err error) { return } -func (c *Croc) getFilesReady(ws *websocket.Conn) (err error) { - c.cs.Lock() - defer c.cs.Unlock() - c.cs.channel.notSentMetaData = true - // send metadata - - // wait until data is ready - for { - if c.cs.channel.fileMetaData.Name != "" { - break - } - c.cs.Unlock() - time.Sleep(10 * time.Millisecond) - c.cs.Lock() - } - - // get passphrase - var passphrase []byte - passphrase, err = c.cs.channel.Pake.SessionKey() - if err != nil { - return - } - // encrypt file data - err = encryptFile(path.Join(c.cs.channel.fileMetaData.Path, c.cs.channel.fileMetaData.Name), c.cs.channel.fileMetaData.Name+".enc", passphrase) - if err != nil { - return - } - c.cs.channel.fileMetaData.IsEncrypted = true - // split into pieces to send - if err = splitFile(c.cs.channel.fileMetaData.Name+".enc", len(c.cs.channel.Ports)); err != nil { - return - } - // remove the file now since we still have pieces - if err = os.Remove(c.cs.channel.fileMetaData.Name + ".enc"); err != nil { - return - } - // remove compressed archive - if c.cs.channel.fileMetaData.IsDir { - log.Debug("removing archive: " + c.cs.channel.fileMetaData.Name) - if err = os.Remove(c.cs.channel.fileMetaData.Name); err != nil { - return - } - } - // encrypt meta data - var metaDataBytes []byte - metaDataBytes, err = json.Marshal(c.cs.channel.fileMetaData) - if err != nil { - return - } - c.cs.channel.EncryptedFileMetaData = encrypt(metaDataBytes, passphrase) - - c.cs.channel.Update = true - log.Debugf("updating channel") - errWrite := ws.WriteJSON(c.cs.channel) - if errWrite != nil { - log.Error(errWrite) - } - c.cs.channel.Update = false - go func() { - // encrypt the files - // TODO - c.cs.Lock() - c.cs.channel.fileReady = true - c.cs.Unlock() - }() - return -} - func (c *Croc) spawnConnections(ws *websocket.Conn, role int) (err error) { err = c.dialUp(ws) if err == nil { @@ -365,8 +295,19 @@ func (c *Croc) dialUp(ws *websocket.Conn) (err error) { role := c.cs.channel.Role c.cs.Unlock() errorChan := make(chan error, len(ports)) + + // generate a receive filename + var f *os.File + f, err = ioutil.TempFile(".", "croc-received") + if err != nil { + return + } + receiveFileName := f.Name() + f.Close() + os.Remove(receiveFileName) + for i, port := range ports { - go func(channel, uuid, port string, i int, errorChan chan error) { + go func(channel, uuid, port string, i int, errorChan chan error, receiveFileName string) { if i == 0 { log.Debug("dialing up") } @@ -411,7 +352,7 @@ func (c *Croc) dialUp(ws *websocket.Conn) (err error) { time.Sleep(10 * time.Millisecond) } c.cs.RLock() - filename := c.cs.channel.fileMetaData.Name + ".enc." + strconv.Itoa(i) + c.cs.RUnlock() if role == 0 { log.Debug("send file") @@ -425,6 +366,7 @@ func (c *Croc) dialUp(ws *websocket.Conn) (err error) { time.Sleep(10 * time.Millisecond) } log.Debug("sending file") + filename := c.crocFileEncrypted + "." + strconv.Itoa(i) err = sendFile(filename, i, connection) } else { go func() { @@ -441,11 +383,11 @@ func (c *Croc) dialUp(ws *websocket.Conn) (err error) { c.cs.Unlock() log.Debug("receive file") }() - - err = receiveFile(filename, i, connection) + receiveFileName += "." + strconv.Itoa(i) + err = receiveFile(receiveFileName, i, connection) } errorChan <- err - }(channel, uuid, port, i, errorChan) + }(channel, uuid, port, i, errorChan, receiveFileName) } // collect errors @@ -462,67 +404,6 @@ func (c *Croc) dialUp(ws *websocket.Conn) (err error) { return } -func (c *Croc) processFile(fname string) (err error) { - - fd := FileMetaData{} - - // first check if it is stdin - if fname == "stdin" { - var f *os.File - f, err = ioutil.TempFile(".", "croc-stdin-") - if err != nil { - return - } - _, err = io.Copy(f, os.Stdin) - if err != nil { - return - } - fname = f.Name() - err = f.Close() - if err != nil { - return - } - fd.DeleteAfterSending = true - } - - fname = filepath.Clean(fname) - // check wether the file is a dir - info, err := os.Stat(fname) - if err != nil { - return - } - - fd.Path, fd.Name = filepath.Split(fname) - if info.Mode().IsDir() { - // tar folder - err = tarinator.Tarinate([]string{fname}, fd.Name+".tar") - if err != nil { - log.Error(err) - return - } - fd.Name = fd.Name + ".tar" - fd.Path = "." - fd.IsDir = true - fname = path.Join(fd.Path, fd.Name) - } - fd.Hash, err = hashFile(fname) - if err != nil { - log.Error(err) - return err - } - fd.Size, err = fileSize(fname) - if err != nil { - err = errors.Wrap(err, "could not determine filesize") - log.Error(err) - return err - } - - c.cs.Lock() - defer c.cs.Unlock() - c.cs.channel.fileMetaData = fd - return -} - func receiveFile(filename string, id int, connection net.Conn) error { log.Debug("waiting for chunk size from sender") fileSizeBuffer := make([]byte, 10) diff --git a/src/files.go b/src/files.go new file mode 100644 index 0000000..99c136f --- /dev/null +++ b/src/files.go @@ -0,0 +1,151 @@ +package croc + +import ( + "encoding/json" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "time" + + log "github.com/cihub/seelog" + "github.com/gorilla/websocket" + "github.com/pkg/errors" +) + +func (c *Croc) processFile(src string) (err error) { + fd := FileMetaData{} + + // pathToFile and filename are the files that should be used internally + var pathToFile, filename string + // first check if it is stdin + if src == "stdin" { + var f *os.File + f, err = ioutil.TempFile(".", "croc-stdin-") + if err != nil { + return + } + _, err = io.Copy(f, os.Stdin) + if err != nil { + return + } + pathToFile = "." + filename = f.Name() + err = f.Close() + if err != nil { + return + } + // fd.Name is what the user will see + fd.Name = "stdin" + fd.DeleteAfterSending = true + } else { + pathToFile, filename = filepath.Split(filepath.Clean(src)) + fd.Name = filename + } + + // check wether the file is a dir + info, err := os.Stat(path.Join(pathToFile, filename)) + if err != nil { + return + } + fd.IsDir = info.Mode().IsDir() + + // zip file + c.crocFile, err = zipFile(path.Join(pathToFile, filename), c.UseCompression) + fd.IsCompressed = c.UseCompression + + fd.Hash, err = hashFile(c.crocFile) + if err != nil { + log.Error(err) + return err + } + fd.Size, err = fileSize(c.crocFile) + if err != nil { + err = errors.Wrap(err, "could not determine filesize") + log.Error(err) + return err + } + + c.cs.Lock() + defer c.cs.Unlock() + c.cs.channel.fileMetaData = fd + return +} + +func (c *Croc) getFilesReady(ws *websocket.Conn) (err error) { + c.cs.Lock() + defer c.cs.Unlock() + log.Debug("getting files ready") + c.cs.channel.notSentMetaData = true + // send metadata + + // wait until data is ready + for { + if c.cs.channel.fileMetaData.Name != "" { + break + } + c.cs.Unlock() + time.Sleep(10 * time.Millisecond) + c.cs.Lock() + } + + // get passphrase + var passphrase []byte + passphrase, err = c.cs.channel.Pake.SessionKey() + if err != nil { + return + } + // encrypt file data + // create temporary filename + var f *os.File + f, err = ioutil.TempFile(".", "croc-encrypted") + if err != nil { + return + } + c.crocFileEncrypted = f.Name() + f.Close() + os.Remove(c.crocFileEncrypted) + err = encryptFile(c.crocFile, c.crocFileEncrypted, passphrase) + if err != nil { + return + } + // remove the unencrypted versoin + if err = os.Remove(c.crocFile); err != nil { + return + } + c.cs.channel.fileMetaData.IsEncrypted = true + // split into pieces to send + log.Debugf("splitting %s", c.crocFileEncrypted) + if err = splitFile(c.crocFileEncrypted, len(c.cs.channel.Ports)); err != nil { + return + } + // remove the file now since we still have pieces + if err = os.Remove(c.crocFileEncrypted); err != nil { + return + } + + // encrypt meta data + var metaDataBytes []byte + metaDataBytes, err = json.Marshal(c.cs.channel.fileMetaData) + if err != nil { + return + } + c.cs.channel.EncryptedFileMetaData = encrypt(metaDataBytes, passphrase) + + c.cs.channel.Update = true + log.Debugf("updating channel") + errWrite := ws.WriteJSON(c.cs.channel) + if errWrite != nil { + log.Error(errWrite) + } + c.cs.channel.Update = false + go func() { + // encrypt the files + // TODO + c.cs.Lock() + c.cs.channel.fileReady = true + c.cs.Unlock() + }() + return +} diff --git a/src/models.go b/src/models.go index 93bd959..279a9fc 100644 --- a/src/models.go +++ b/src/models.go @@ -30,6 +30,11 @@ type Croc struct { // cs keeps the client state cs clientState + + // crocFile is the name of the file that is prepared to sent + crocFile string + // crocFileEncrypted is the name of the encrypted file + crocFileEncrypted string } // Init will initialize the croc relay @@ -60,11 +65,9 @@ type clientState struct { } type FileMetaData struct { - TempName string Name string Size int Hash string - Path string IsDir bool IsEncrypted bool IsCompressed bool @@ -122,6 +125,9 @@ type channelData struct { fileMetaData FileMetaData notSentMetaData bool finishedHappy bool + filesReady bool + // ws is the connection that the client has to the relay + ws *websocket.Conn // relay parameters // isopen determine whether or not the channel has been opened diff --git a/src/zip.go b/src/zip.go index 6b00472..e3a5c33 100644 --- a/src/zip.go +++ b/src/zip.go @@ -81,7 +81,7 @@ func zipFile(fname string, compress bool) (writtenFilename string, err error) { defer os.Chdir(curdir) os.Chdir(pathtofile) - newfile, err := ioutil.TempFile("/tmp/", "croc") + newfile, err := ioutil.TempFile(".", "croc-unencrypted") if err != nil { log.Error(err) return