diff --git a/src/client.go b/src/client.go index a4749ce..0934bfd 100644 --- a/src/client.go +++ b/src/client.go @@ -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 } diff --git a/src/models.go b/src/models.go index d766fb8..24ea234 100644 --- a/src/models.go +++ b/src/models.go @@ -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 diff --git a/src/relay.go b/src/relay.go index 526eec1..9564644 100644 --- a/src/relay.go +++ b/src/relay.go @@ -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 } diff --git a/src/server.go b/src/server.go index 06c80f1..d6335d4 100644 --- a/src/server.go +++ b/src/server.go @@ -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) }