file transfer ready

This commit is contained in:
Zack Scholl 2018-06-29 20:12:19 -07:00
parent 72e2d4d3d8
commit 0bf384dd82
4 changed files with 172 additions and 45 deletions

View File

@ -1,14 +1,16 @@
package croc
import (
"errors"
"net"
"net/url"
"os"
"os/signal"
"sync"
"time"
log "github.com/cihub/seelog"
"github.com/gorilla/websocket"
"github.com/pkg/errors"
"github.com/schollz/croc/src/pake"
)
@ -85,36 +87,48 @@ func (c *Croc) client(role int, codePhrase string) (err error) {
return
}
for {
select {
case <-done:
return
case <-interrupt:
// send Close signal to relay on interrupt
log.Debugf("interrupt")
c.cs.Lock()
channel := c.cs.channel.Channel
uuid := c.cs.channel.UUID
c.cs.Unlock()
// Cleanly close the connection by sending a close message and then
// waiting (with timeout) for the server to close the connection.
log.Debug("sending close signal")
errWrite := ws.WriteJSON(channelData{
Channel: channel,
UUID: uuid,
Close: true,
})
if errWrite != nil {
log.Debugf("write close:", err)
return
}
var wg sync.WaitGroup
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-done:
case <-time.After(time.Second):
return
case <-interrupt:
// send Close signal to relay on interrupt
log.Debugf("interrupt")
c.cs.Lock()
channel := c.cs.channel.Channel
uuid := c.cs.channel.UUID
c.cs.Unlock()
// Cleanly close the connection by sending a close message and then
// waiting (with timeout) for the server to close the connection.
log.Debug("sending close signal")
errWrite := ws.WriteJSON(channelData{
Channel: channel,
UUID: uuid,
Close: true,
})
if errWrite != nil {
log.Debugf("write close:", err)
return
}
select {
case <-done:
case <-time.After(time.Second):
}
return
}
return
}
}(&wg)
wg.Wait()
c.cs.Lock()
if c.cs.channel.FileReceived {
log.Info("file recieved!")
}
c.cs.Unlock()
return
}
@ -131,6 +145,22 @@ func (c *Croc) processState(ws *websocket.Conn, cd channelData) (err error) {
// check if the state is not aligned (i.e. have h(k) but no hh(k))
// throw error if not aligned so it can exit
// if file received, then you are all done
if cd.FileReceived {
c.cs.channel.FileReceived = true
log.Debug("file recieved!")
log.Debug("sending close signal")
c.cs.channel.Close = true
ws.WriteJSON(c.cs.channel)
return
}
// if transfer ready then send file
if cd.TransferReady {
c.cs.channel.TransferReady = true
return
}
// first update the channel data
// initialize if has UUID
if cd.UUID != "" {
@ -150,10 +180,9 @@ func (c *Croc) processState(ws *websocket.Conn, cd channelData) (err error) {
return
}
// copy over the rest of the state
if cd.TransferReady {
c.cs.channel.TransferReady = true
}
c.cs.channel.Ports = cd.Ports
// update the Pake
if cd.Pake != nil && cd.Pake.Role != c.cs.channel.Role {
log.Debugf("updating pake from %d", cd.Pake.Role)
if c.cs.channel.Pake.HkA == nil {
@ -176,13 +205,100 @@ func (c *Croc) processState(ws *websocket.Conn, cd channelData) (err error) {
}
}
// TODO:
// TODO
// process the client state
log.Debugf("processing client state: %+v", c.cs.channel.String2())
if c.cs.channel.Role == 0 {
// processing for sender
} else if c.cs.channel.Role == 1 {
// processing for recipient
if c.cs.channel.Pake.IsVerified() && !c.cs.channel.isReady {
// spawn TCP connections
c.cs.channel.isReady = true
go func(role int) {
err = c.dialUp()
if err == nil {
if role == 1 {
c.cs.Lock()
c.cs.channel.Update = true
c.cs.channel.FileReceived = true
log.Debugf("got file successfully")
errWrite := ws.WriteJSON(c.cs.channel)
if errWrite != nil {
log.Error(errWrite)
}
c.cs.channel.Update = false
c.cs.Unlock()
}
} else {
log.Error(err)
}
}(c.cs.channel.Role)
}
return
}
func (c *Croc) dialUp() (err error) {
c.cs.Lock()
ports := c.cs.channel.Ports
channel := c.cs.channel.Channel
uuid := c.cs.channel.UUID
role := c.cs.channel.Role
c.cs.Unlock()
errorChan := make(chan error)
for i, port := range ports {
go func(channel, uuid, port string, i int) {
if i == 0 {
log.Debug("dialing up")
}
log.Debugf("connecting to %s", "localhost:"+port)
connection, err := net.Dial("tcp", "localhost:"+port)
if err != nil {
errorChan <- err
return
}
defer connection.Close()
connection.SetReadDeadline(time.Now().Add(1 * time.Hour))
connection.SetDeadline(time.Now().Add(1 * time.Hour))
connection.SetWriteDeadline(time.Now().Add(1 * time.Hour))
message, err := receiveMessage(connection)
if err != nil {
errorChan <- err
return
}
log.Debugf("relay says: %s", message)
err = sendMessage(channel, connection)
if err != nil {
errorChan <- err
return
}
err = sendMessage(uuid, connection)
if err != nil {
errorChan <- err
return
}
// wait for transfer to be ready
for {
c.cs.RLock()
ready := c.cs.channel.TransferReady
c.cs.RUnlock()
if ready {
break
}
time.Sleep(10 * time.Millisecond)
}
if role == 0 {
log.Debug("send file")
} else {
log.Debug("receive file")
}
time.Sleep(3 * time.Second)
errorChan <- nil
}(channel, uuid, port, i)
}
// collect errors
for i := 0; i < len(ports); i++ {
errOne := <-errorChan
if errOne != nil {
log.Warn(errOne)
}
}
return
}

View File

@ -73,7 +73,7 @@ type channelData struct {
Channel string `json:"channel,omitempty"`
// Pake contains the information for
// generating the session key over an insecure channel
Pake *pake.Pake
Pake *pake.Pake `json:"pake"`
// TransferReady is set by the relaying when both parties have connected
// with their credentials
TransferReady bool `json:"transfer_ready"`
@ -82,6 +82,8 @@ type channelData struct {
// Curve is the type of elliptic curve to use
Curve string `json:"curve"`
// FileReceived specifies that everything was done right
FileReceived bool `json:"file_received"`
// Error is sent if there is an error
Error string `json:"error"`
@ -100,7 +102,7 @@ type channelData struct {
passPhrase string
// sessionKey
sessionKey []byte
pakeDone bool
isReady bool
// relay parameters
// isopen determine whether or not the channel has been opened

View File

@ -64,7 +64,7 @@ func (c *Croc) clientCommuncation(port string, connection net.Conn) (err error)
if err != nil {
return
}
log.Debugf("%s connected with channel %s and uuid %s", connection.RemoteAddr().String(), channel, uuid)
log.Debugf("%s connected to port %s on channel %s and uuid %s", connection.RemoteAddr().String(), port, channel, uuid)
// validate channel and UUID
c.rs.Lock()
@ -87,27 +87,34 @@ func (c *Croc) clientCommuncation(port string, connection net.Conn) (err error)
con1 = c.rs.channel[channel].connection[0]
con2 = c.rs.channel[channel].connection[1]
ports := c.rs.channel[channel].Ports
c.rs.Unlock()
if con1 != nil && con2 != nil {
log.Debugf("beginning the piping")
var wg sync.WaitGroup
wg.Add(2)
// first start piping
wg.Add(1)
// start piping
go func(con1 net.Conn, con2 net.Conn, wg *sync.WaitGroup) {
pipe(con1, con2)
wg.Done()
log.Debug("done piping")
}(con1, con2, &wg)
// then set transfer ready
go func(channel string, wg *sync.WaitGroup) {
// set the channels to ready
if port == ports[0] {
// then set transfer ready
c.rs.Lock()
c.rs.channel[channel].TransferReady = true
c.rs.channel[channel].websocketConn[0].WriteJSON(c.rs.channel[channel])
c.rs.channel[channel].websocketConn[1].WriteJSON(c.rs.channel[channel])
c.rs.Unlock()
wg.Done()
}(channel, &wg)
log.Debugf("sent ready signal")
}
wg.Wait()
log.Debugf("finished transfer")
}
log.Debug("finished client communication")
return
}

View File

@ -75,6 +75,8 @@ func (c *Croc) updateChannel(cd channelData) (err error) {
}
// update each
c.rs.channel[cd.Channel].Error = cd.Error
c.rs.channel[cd.Channel].FileReceived = cd.FileReceived
if c.rs.channel[cd.Channel].Pake == nil {
c.rs.channel[cd.Channel].Pake = new(pake.Pake)
}