From 9d949edfade2e2c21fee5a9de6dc3f935c309ba9 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Wed, 11 Oct 2023 01:05:20 +0300 Subject: [PATCH 01/29] add id to connections --- go.mod | 1 + go.sum | 2 ++ src/comm/comm.go | 7 +++++++ 3 files changed, 10 insertions(+) diff --git a/go.mod b/go.mod index 5e9a79bc2..e3ebcf1cc 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/uuid v1.3.1 github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect diff --git a/go.sum b/go.sum index afae9c75c..37dd7b4a7 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ= github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kalafut/imohash v1.0.2 h1:j/cUPa15YvXv7abJlM+kdJIycbBMpmO7WqhPl4YB76I= github.com/kalafut/imohash v1.0.2/go.mod h1:PjHBF0vpo1q7zMqiTn0qwSTQU2wDn5QIe8S8sFQuZS8= diff --git a/src/comm/comm.go b/src/comm/comm.go index d578ac14e..8cf648bd3 100644 --- a/src/comm/comm.go +++ b/src/comm/comm.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/google/uuid" "github.com/magisterquis/connectproxy" "github.com/schollz/croc/v9/src/utils" log "github.com/schollz/logger" @@ -23,6 +24,7 @@ var MAGIC_BYTES = []byte("croc") // Comm is some basic TCP communication type Comm struct { + id string connection net.Conn } @@ -100,6 +102,7 @@ func New(c net.Conn) *Comm { log.Errorf("error setting write deadline: %v", err) } comm := new(Comm) + comm.id = uuid.New().String() comm.connection = c return comm } @@ -109,6 +112,10 @@ func (c *Comm) Connection() net.Conn { return c.connection } +func (c *Comm) ID() string { + return c.id +} + // Close closes the connection func (c *Comm) Close() { if err := c.connection.Close(); err != nil { From bf704948dcb839b33ad8f296a832c75ff6de319d Mon Sep 17 00:00:00 2001 From: RCL98 Date: Wed, 11 Oct 2023 01:06:11 +0300 Subject: [PATCH 02/29] add multiple file transfers and tiemout options in cli --- src/cli/cli.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/cli/cli.go b/src/cli/cli.go index 64502fe40..1bd8f2794 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -65,6 +65,8 @@ func Run() (err error) { ArgsUsage: "[filename(s) or folder]", Flags: []cli.Flag{ &cli.BoolFlag{Name: "zip", Usage: "zip folder before sending"}, + &cli.IntFlag{Name: "timelimit", Value: 30, Usage: "timelimit in secods for sender to allow all transfers"}, + &cli.IntFlag{Name: "multiple", Value: 2, Usage: "maximum number of transfers"}, &cli.StringFlag{Name: "code", Aliases: []string{"c"}, Usage: "codephrase used to connect to relay"}, &cli.StringFlag{Name: "hash", Value: "xxhash", Usage: "hash algorithm (xxhash, imohash, md5)"}, &cli.StringFlag{Name: "text", Aliases: []string{"t"}, Usage: "send some text"}, @@ -179,6 +181,8 @@ func send(c *cli.Context) (err error) { crocOptions := croc.Options{ SharedSecret: c.String("code"), IsSender: true, + TimeLimit: c.Int("timeout"), + MaxTransfers: c.Int("multiple"), Debug: c.Bool("debug"), NoPrompt: c.Bool("yes"), RelayAddress: c.String("relay"), @@ -199,11 +203,21 @@ func send(c *cli.Context) (err error) { ThrottleUpload: c.String("throttleUpload"), ZipFolder: c.Bool("zip"), } + + if crocOptions.TimeLimit <= 0 { + fmt.Println("timelimit must be greater than 0. Defaulting to 30 seconds.") + crocOptions.TimeLimit = 30 + } + if crocOptions.MaxTransfers <= 1 { + fmt.Println("multiple must be greater than 1. Defaulting to 2 transfers.") + crocOptions.MaxTransfers = 2 + } if crocOptions.RelayAddress != models.DEFAULT_RELAY { crocOptions.RelayAddress6 = "" } else if crocOptions.RelayAddress6 != models.DEFAULT_RELAY6 { crocOptions.RelayAddress = "" } + b, errOpen := os.ReadFile(getConfigFile()) if errOpen == nil && !c.Bool("remember") { var rememberedOptions croc.Options From e838fb87146f141062be0b726bbb44850f948fdc Mon Sep 17 00:00:00 2001 From: RCL98 Date: Wed, 11 Oct 2023 01:07:34 +0300 Subject: [PATCH 03/29] allow tcp server to keep room for multiple transfers --- src/tcp/tcp.go | 327 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 289 insertions(+), 38 deletions(-) diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index b7803b000..488b4a149 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -2,8 +2,10 @@ package tcp import ( "bytes" + "container/list" "fmt" "net" + "strconv" "strings" "sync" "time" @@ -26,14 +28,19 @@ type server struct { } type roomInfo struct { - first *comm.Comm - second *comm.Comm - opened time.Time - full bool + first *comm.Comm + second *comm.Comm + queue *list.List + isMainRoom bool + maxOccupants int + doneTransfers int + opened time.Time + full bool } type roomMap struct { - rooms map[string]roomInfo + rooms map[string]roomInfo + roomLocks map[string]*sync.Mutex sync.Mutex } @@ -59,6 +66,7 @@ func (s *server) start() (err error) { log.Debugf("starting with password '%s'", s.password) s.rooms.Lock() s.rooms.rooms = make(map[string]roomInfo) + s.rooms.roomLocks = make(map[string]*sync.Mutex) s.rooms.Unlock() // delete old rooms @@ -249,12 +257,30 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er return } - // wait for client to tell me which room they want - log.Debug("waiting for answer") + isSender := false + log.Debug("wait for client to tell if they want to send or receive") enc, err := c.Receive() if err != nil { return } + data, err := crypt.Decrypt(enc, strongKeyForEncryption) + if err != nil { + return + } + if !bytes.Equal(data, []byte("send")) && !bytes.Equal(data, []byte("receive")) { + err = fmt.Errorf("got bad response: %s", data) + return + } else if bytes.Equal(data, []byte("send")) { + log.Debug("client wants to send") + isSender = true + } + + // wait for client to tell me which room they want + log.Debug("waiting for room") + enc, err = c.Receive() + if err != nil { + return + } roomBytes, err := crypt.Decrypt(enc, strongKeyForEncryption) if err != nil { return @@ -263,14 +289,54 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er s.rooms.Lock() // create the room if it is new - if _, ok := s.rooms.rooms[room]; !ok { + if _, ok := s.rooms.rooms[room]; !ok && isSender { + log.Debug("Check if this is a main room") + enc, err = c.Receive() + if err != nil { + return + } + data, err = crypt.Decrypt(enc, strongKeyForEncryption) + if err != nil { + return + } + if !bytes.Equal(data, []byte("main")) && !bytes.Equal(data, []byte("secondary")) { + err = fmt.Errorf("got bad response: %s", data) + return + } + isMainRoom := bytes.Equal(data, []byte("main")) + log.Debugf("isMainRoom: %v", isMainRoom) + + maxOccupants := 2 + if isMainRoom { + log.Debug("Wait for maxOccupants") + enc, err = c.Receive() + if err != nil { + return + } + data, err = crypt.Decrypt(enc, strongKeyForEncryption) + if err != nil { + return + } + + maxOccupants, err = strconv.Atoi(string(data)) + if err != nil { + return + } + log.Debugf("maxOccupants: %v", maxOccupants) + } + s.rooms.rooms[room] = roomInfo{ - first: c, - opened: time.Now(), + first: c, + second: nil, + isMainRoom: isMainRoom, + maxOccupants: maxOccupants, + doneTransfers: 0, + opened: time.Now(), } + s.rooms.roomLocks[room] = &sync.Mutex{} s.rooms.Unlock() - // tell the client that they got the room + // tell the client that they got the room bSend, err = crypt.Encrypt([]byte("ok"), strongKeyForEncryption) if err != nil { return @@ -285,25 +351,107 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er return } if s.rooms.rooms[room].full { - s.rooms.Unlock() - bSend, err = crypt.Encrypt([]byte("room full"), strongKeyForEncryption) - if err != nil { + if s.rooms.rooms[room].isMainRoom { + log.Debugf("room %s is full, adding to queue", room) + queue := s.rooms.rooms[room].queue + if queue == nil { + queue = list.New() + } + queue.PushBack(c.ID()) + s.rooms.rooms[room] = roomInfo{ + first: s.rooms.rooms[room].first, + second: s.rooms.rooms[room].second, + opened: s.rooms.rooms[room].opened, + maxOccupants: s.rooms.rooms[room].maxOccupants, + doneTransfers: s.rooms.rooms[room].doneTransfers, + queue: queue, + full: true, + } + s.rooms.Unlock() + + for { + s.rooms.roomLocks[room].Lock() + + if s.rooms.rooms[room].doneTransfers >= s.rooms.rooms[room].maxOccupants { + s.rooms.roomLocks[room].Unlock() + // tell the client that the sender is no longer available + bSend, err = crypt.Encrypt([]byte("sender_gone"), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + return + } else if s.rooms.rooms[room].second != nil || s.rooms.rooms[room].queue.Front().Value.(string) != c.ID() { + s.rooms.roomLocks[room].Unlock() + time.Sleep(1 * time.Second) + // tell the client that they need to wait + bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + } else { + newQueue := s.rooms.rooms[room].queue + newQueue.Remove(newQueue.Front()) + s.rooms.rooms[room] = roomInfo{ + first: s.rooms.rooms[room].first, + second: c, + queue: newQueue, + isMainRoom: s.rooms.rooms[room].isMainRoom, + maxOccupants: s.rooms.rooms[room].maxOccupants, + doneTransfers: s.rooms.rooms[room].doneTransfers, + opened: s.rooms.rooms[room].opened, + full: true, + } + break + } + } + // tell the client that they got the room + bSend, err = crypt.Encrypt([]byte("ok"), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + } else { + s.rooms.Unlock() + bSend, err = crypt.Encrypt([]byte("room full"), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } return } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return + } else { + log.Debugf("room %s has 2", room) + s.rooms.rooms[room] = roomInfo{ + first: s.rooms.rooms[room].first, + second: c, + queue: s.rooms.rooms[room].queue, + isMainRoom: s.rooms.rooms[room].isMainRoom, + maxOccupants: s.rooms.rooms[room].maxOccupants, + doneTransfers: s.rooms.rooms[room].doneTransfers, + opened: s.rooms.rooms[room].opened, + full: true, } - return - } - log.Debugf("room %s has 2", room) - s.rooms.rooms[room] = roomInfo{ - first: s.rooms.rooms[room].first, - second: c, - opened: s.rooms.rooms[room].opened, - full: true, + s.rooms.roomLocks[room].Lock() } + otherConnection := s.rooms.rooms[room].first s.rooms.Unlock() @@ -331,8 +479,32 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er } wg.Wait() - // delete room - s.deleteRoom(room) + newDoneTransfers := s.rooms.rooms[room].doneTransfers + 1 + if newDoneTransfers == s.rooms.rooms[room].maxOccupants { + log.Debugf("room %s is done", room) + // delete room + s.deleteRoom(room) + } else { + log.Debugf("room %s has %d done", room, newDoneTransfers) + s.rooms.Lock() + lengthOfQueue := 0 + if s.rooms.rooms[room].queue != nil { + lengthOfQueue = s.rooms.rooms[room].queue.Len() + } + s.rooms.rooms[room] = roomInfo{ + first: s.rooms.rooms[room].first, + second: nil, + queue: s.rooms.rooms[room].queue, + isMainRoom: s.rooms.rooms[room].isMainRoom, + maxOccupants: s.rooms.rooms[room].maxOccupants, + doneTransfers: newDoneTransfers, + opened: s.rooms.rooms[room].opened, + full: lengthOfQueue > 0, + } + s.rooms.Unlock() + s.rooms.roomLocks[room].Unlock() + } + return } @@ -342,6 +514,17 @@ func (s *server) deleteRoom(room string) { if _, ok := s.rooms.rooms[room]; !ok { return } + if s.rooms.rooms[room].queue != nil && s.rooms.rooms[room].queue.Len() > 0 && s.rooms.roomLocks[room] != nil { + // signal to all waiting that the room will be deleted + for { + s.rooms.roomLocks[room].Unlock() + if s.rooms.rooms[room].queue.Len() == 0 { + break + } + s.rooms.roomLocks[room].Lock() + } + delete(s.rooms.roomLocks, room) + } log.Debugf("deleting room: %s", room) if s.rooms.rooms[room].first != nil { s.rooms.rooms[room].first.Close() @@ -436,7 +619,7 @@ func PingServer(address string) (err error) { // ConnectToTCPServer will initiate a new connection // to the specified address, room with optional time limit -func ConnectToTCPServer(address, password, room string, timelimit ...time.Duration) (c *comm.Comm, banner string, ipaddr string, err error) { +func ConnectToTCPServer(address, password, room string, isSender, isMainRoom bool, maxOccupants int, timelimit ...time.Duration) (c *comm.Comm, banner string, ipaddr string, err error) { if len(timelimit) > 0 { c, err = comm.NewConnection(address, timelimit[0]) } else { @@ -516,8 +699,13 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati } banner = strings.Split(string(data), "|||")[0] ipaddr = strings.Split(string(data), "|||")[1] - log.Debug("sending room") - bSend, err = crypt.Encrypt([]byte(room), strongKeyForEncryption) + + log.Debug("tell server if you want to send or receive") + clientType := "receive" + if isSender { + clientType = "send" + } + bSend, err = crypt.Encrypt([]byte(clientType), strongKeyForEncryption) if err != nil { log.Debug(err) return @@ -527,22 +715,85 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati log.Debug(err) return } - log.Debug("waiting for room confirmation") - enc, err = c.Receive() + + log.Debug("sending room") + bSend, err = crypt.Encrypt([]byte(room), strongKeyForEncryption) if err != nil { log.Debug(err) return } - data, err = crypt.Decrypt(enc, strongKeyForEncryption) + err = c.Send(bSend) if err != nil { log.Debug(err) return } - if !bytes.Equal(data, []byte("ok")) { - err = fmt.Errorf("got bad response: %s", data) - log.Debug(err) - return + + if isSender { + log.Debug("tell server if this is a main room") + roomType := "secondary" + if isMainRoom { + roomType = "main" + } + bSend, err = crypt.Encrypt([]byte(roomType), strongKeyForEncryption) + if err != nil { + log.Debug(err) + return + } + err = c.Send(bSend) + if err != nil { + log.Debug(err) + return + } + + if isMainRoom { + log.Debug("tell server maxOccupants") + bSend, err = crypt.Encrypt([]byte(strconv.Itoa(maxOccupants)), strongKeyForEncryption) + if err != nil { + log.Debug(err) + return + } + err = c.Send(bSend) + if err != nil { + log.Debug(err) + return + } + } } + + log.Debug("waiting for room confirmation") + for { + enc, err = c.Receive() + if err != nil { + log.Debug(err) + return + } + data, err = crypt.Decrypt(enc, strongKeyForEncryption) + if err != nil { + log.Debug(err) + return + } + if !isSender { + if bytes.Equal(data, []byte("wait")) { + log.Debug("waiting for sender to be free") + time.Sleep(1 * time.Second) + continue + } else if bytes.Equal(data, []byte("sender_gone")) { + c = nil + return + } else if bytes.Equal(data, []byte("room full")) { + c = nil + return + } + } + if !bytes.Equal(data, []byte("ok")) { + err = fmt.Errorf("got bad response: %s", data) + log.Debug(err) + return + } else { + break + } + } + log.Debug("all set") return } From 3351d2d3cd669252bd50c312413a2e3f470eb3ae Mon Sep 17 00:00:00 2001 From: RCL98 Date: Wed, 11 Oct 2023 01:14:58 +0300 Subject: [PATCH 04/29] prepare croc for multiple transfers --- src/croc/croc.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/croc/croc.go b/src/croc/croc.go index 5ec7866a9..2b40c1b61 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -55,6 +55,8 @@ func Debug(debug bool) { // Options specifies user specific options type Options struct { IsSender bool + TimeLimit int + MaxTransfers int SharedSecret string Debug bool RelayAddress string @@ -480,7 +482,7 @@ func (c *Client) transferOverLocalRelay(errchan chan<- error) { time.Sleep(500 * time.Millisecond) log.Debug("establishing connection") var banner string - conn, banner, ipaddr, err := tcp.ConnectToTCPServer("127.0.0.1:"+c.Options.RelayPorts[0], c.Options.RelayPassword, c.Options.SharedSecret[:3]) + conn, banner, ipaddr, err := tcp.ConnectToTCPServer("127.0.0.1:"+c.Options.RelayPorts[0], c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, c.Options.MaxTransfers) log.Debugf("banner: %s", banner) if err != nil { err = fmt.Errorf("could not connect to 127.0.0.1:%s: %w", c.Options.RelayPorts[0], err) @@ -569,7 +571,7 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t log.Debugf("got host '%v' and port '%v'", host, port) address = net.JoinHostPort(host, port) log.Debugf("trying connection to %s", address) - conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], durations[i]) + conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, c.Options.MaxTransfers, durations[i]) if err == nil { c.Options.RelayAddress = address break @@ -766,7 +768,7 @@ func (c *Client) Receive() (err error) { log.Debugf("got host '%v' and port '%v'", host, port) address = net.JoinHostPort(host, port) log.Debugf("trying connection to %s", address) - c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], durations[i]) + c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, 2, durations[i]) if err == nil { c.Options.RelayAddress = address break @@ -824,7 +826,7 @@ func (c *Client) Receive() (err error) { } serverTry := net.JoinHostPort(ip, port) - conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], 500*time.Millisecond) + conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, 2, 500*time.Millisecond) if errConn != nil { log.Debug(errConn) log.Debugf("could not connect to " + serverTry) @@ -1175,6 +1177,9 @@ func (c *Client) processMessagePake(m message.Message) (err error) { server, c.Options.RelayPassword, fmt.Sprintf("%s-%d", utils.SHA256(c.Options.SharedSecret[:5])[:6], j), + c.Options.IsSender, + false, + 2, ) if err != nil { panic(err) From 2352dcb79238d2ac9f33ef24864efb518fec3ac3 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Fri, 13 Oct 2023 23:51:17 +0300 Subject: [PATCH 05/29] allow multiple transfers from croc --- src/croc/croc.go | 180 ++++++++++++++++++++++++++--------------------- 1 file changed, 99 insertions(+), 81 deletions(-) diff --git a/src/croc/croc.go b/src/croc/croc.go index 2b40c1b61..ec5182036 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -79,6 +79,7 @@ type Options struct { ThrottleUpload string ZipFolder bool TestFlag bool + DoubleIps bool } // Client holds the state of the croc transfer @@ -513,6 +514,90 @@ func (c *Client) transferOverLocalRelay(errchan chan<- error) { errchan <- c.transfer() } +func (c *Client) establishSecureConnectionWithTCPServer(errchan chan error) (conn *comm.Comm, ipaddr, banner string, err error) { + durations := []time.Duration{100 * time.Millisecond, 5 * time.Second} + for i, address := range []string{c.Options.RelayAddress6, c.Options.RelayAddress} { + if address == "" { + continue + } + host, port, _ := net.SplitHostPort(address) + log.Debugf("host: '%s', port: '%s'", host, port) + // Default port to :9009 + if port == "" { + host = address + port = models.DEFAULT_PORT + } + log.Debugf("got host '%v' and port '%v'", host, port) + address = net.JoinHostPort(host, port) + log.Debugf("trying connection to %s", address) + conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, c.Options.MaxTransfers, durations[i]) + if err == nil { + c.Options.RelayAddress = address + break + } + log.Debugf("could not establish '%s'", address) + } + + return +} + +func (c *Client) listenToMainConn(conn *comm.Comm, ipaddr, banner string, errchan chan error) (err error) { + for { + log.Debug("waiting for bytes") + data, errConn := conn.Receive() + if errConn != nil { + log.Debugf("[%+v] had error: %s", conn, errConn.Error()) + break + } + if bytes.Equal(data, ipRequest) { + // recipient wants to try to connect to local ips + var ips []string + // only get local ips if the local is enabled + if !c.Options.DisableLocal { + // get list of local ips + ips, err = utils.GetLocalIPs() + if err != nil { + log.Debugf("error getting local ips: %v", err) + } + // prepend the port that is being listened to + ips = append([]string{c.Options.RelayPorts[0]}, ips...) + } + bips, _ := json.Marshal(ips) + if err = conn.Send(bips); err != nil { + log.Errorf("error sending: %v", err) + } + } else if bytes.Equal(data, handshakeRequest) { + var wg sync.WaitGroup + wg.Add(1) + go c.makeTheTransfer(conn, ipaddr, banner, &wg, errchan) + wg.Wait() + } else if bytes.Equal(data, []byte{1}) { + log.Debug("got ping") + continue + } else { + log.Debugf("[%+v] got weird bytes: %+v", conn, data) + // throttle the reading + errchan <- fmt.Errorf("gracefully refusing using the public relay") + return + } + } + return +} + +func (c *Client) makeTheTransfer(conn *comm.Comm, ipaddr, banner string, wg *sync.WaitGroup, errchan chan error) (err error) { + c.conn[0] = conn + c.Options.RelayPorts = strings.Split(banner, ",") + if c.Options.NoMultiplexing { + log.Debug("no multiplexing") + c.Options.RelayPorts = []string{c.Options.RelayPorts[0]} + } + c.ExternalIP = ipaddr + log.Debug("exchanged header message") + errchan <- c.transfer() + wg.Done() + return +} + // Send will send the specified file func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, totalNumberFolders int) (err error) { c.EmptyFoldersToTransfer = emptyFoldersToTransfer @@ -553,88 +638,21 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t } if !c.Options.OnlyLocal { - go func() { - var ipaddr, banner string - var conn *comm.Comm - durations := []time.Duration{100 * time.Millisecond, 5 * time.Second} - for i, address := range []string{c.Options.RelayAddress6, c.Options.RelayAddress} { - if address == "" { - continue - } - host, port, _ := net.SplitHostPort(address) - log.Debugf("host: '%s', port: '%s'", host, port) - // Default port to :9009 - if port == "" { - host = address - port = models.DEFAULT_PORT - } - log.Debugf("got host '%v' and port '%v'", host, port) - address = net.JoinHostPort(host, port) - log.Debugf("trying connection to %s", address) - conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, c.Options.MaxTransfers, durations[i]) - if err == nil { - c.Options.RelayAddress = address - break - } - log.Debugf("could not establish '%s'", address) - } - if conn == nil && err == nil { - err = fmt.Errorf("could not connect") - } - if err != nil { - err = fmt.Errorf("could not connect to %s: %w", c.Options.RelayAddress, err) - log.Debug(err) - errchan <- err - return - } - log.Debugf("banner: %s", banner) - log.Debugf("connection established: %+v", conn) - for { - log.Debug("waiting for bytes") - data, errConn := conn.Receive() - if errConn != nil { - log.Debugf("[%+v] had error: %s", conn, errConn.Error()) - } - if bytes.Equal(data, ipRequest) { - // recipient wants to try to connect to local ips - var ips []string - // only get local ips if the local is enabled - if !c.Options.DisableLocal { - // get list of local ips - ips, err = utils.GetLocalIPs() - if err != nil { - log.Debugf("error getting local ips: %v", err) - } - // prepend the port that is being listened to - ips = append([]string{c.Options.RelayPorts[0]}, ips...) - } - bips, _ := json.Marshal(ips) - if err = conn.Send(bips); err != nil { - log.Errorf("error sending: %v", err) - } - } else if bytes.Equal(data, handshakeRequest) { - break - } else if bytes.Equal(data, []byte{1}) { - log.Debug("got ping") - continue - } else { - log.Debugf("[%+v] got weird bytes: %+v", conn, data) - // throttle the reading - errchan <- fmt.Errorf("gracefully refusing using the public relay") - return - } - } + conn, ipaddr, banner, err := c.establishSecureConnectionWithTCPServer(errchan) - c.conn[0] = conn - c.Options.RelayPorts = strings.Split(banner, ",") - if c.Options.NoMultiplexing { - log.Debug("no multiplexing") - c.Options.RelayPorts = []string{c.Options.RelayPorts[0]} - } - c.ExternalIP = ipaddr - log.Debug("exchanged header message") - errchan <- c.transfer() - }() + if conn == nil && err == nil { + err = fmt.Errorf("could not connect") + } + if err != nil { + err = fmt.Errorf("could not connect to %s: %w", c.Options.RelayAddress, err) + log.Debug(err) + errchan <- err + } + + log.Debugf("banner: %s", banner) + log.Debugf("connection established: %+v", conn) + + go c.listenToMainConn(conn, ipaddr, banner, errchan) } err = <-errchan From 976c4c5db27c1d62149ac18bd9b7cd0169ee12cb Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sat, 28 Oct 2023 00:52:30 +0300 Subject: [PATCH 06/29] multiple transfers work --- src/croc/croc.go | 30 ++++++++++++++++++----- src/tcp/tcp.go | 64 +++++++++++++++++++++++++----------------------- 2 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/croc/croc.go b/src/croc/croc.go index ec5182036..20331b756 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -550,6 +550,7 @@ func (c *Client) listenToMainConn(conn *comm.Comm, ipaddr, banner string, errcha break } if bytes.Equal(data, ipRequest) { + log.Debug("Got ip request") // recipient wants to try to connect to local ips var ips []string // only get local ips if the local is enabled @@ -571,6 +572,11 @@ func (c *Client) listenToMainConn(conn *comm.Comm, ipaddr, banner string, errcha wg.Add(1) go c.makeTheTransfer(conn, ipaddr, banner, &wg, errchan) wg.Wait() + c.Step1ChannelSecured = false + c.Step2FileInfoTransferred = false + c.Step3RecipientRequestFile = false + c.Step4FileTransferred = false + c.Step5CloseChannels = false } else if bytes.Equal(data, []byte{1}) { log.Debug("got ping") continue @@ -593,7 +599,12 @@ func (c *Client) makeTheTransfer(conn *comm.Comm, ipaddr, banner string, wg *syn } c.ExternalIP = ipaddr log.Debug("exchanged header message") - errchan <- c.transfer() + + err = c.transfer() + if err != nil { + errchan <- err + } + wg.Done() return } @@ -652,7 +663,7 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t log.Debugf("banner: %s", banner) log.Debugf("connection established: %+v", conn) - go c.listenToMainConn(conn, ipaddr, banner, errchan) + c.listenToMainConn(conn, ipaddr, banner, errchan) } err = <-errchan @@ -1248,7 +1259,7 @@ func (c *Client) processMessage(payload []byte) (done bool, err error) { log.Debug(err) return } - + log.Debugf("Got the message: %s with type: %s", m, m.Type) // only "pake" messages should be unencrypted // if a non-"pake" message is received unencrypted something // is weird @@ -1260,9 +1271,12 @@ func (c *Client) processMessage(payload []byte) (done bool, err error) { switch m.Type { case message.TypeFinished: - err = message.Send(c.conn[0], c.Key, message.Message{ - Type: message.TypeFinished, - }) + // only senders should respond to "finished" messages + if c.Options.IsSender { + err = message.Send(c.conn[0], c.Key, message.Message{ + Type: message.TypeFinished, + }) + } done = true c.SuccessfulTransfer = true return @@ -1438,6 +1452,10 @@ func (c *Client) recipientGetFileReady(finished bool) (err error) { } c.SuccessfulTransfer = true c.FilesHasFinished[c.FilesToTransferCurrentNum] = struct{}{} + + if !c.Options.IsSender { + return + } } err = c.recipientInitializeFile() diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index 488b4a149..dcf6158cb 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -46,7 +46,7 @@ type roomMap struct { const pingRoom = "pinglkasjdlfjsaldjf" -var timeToRoomDeletion = 10 * time.Minute +var timeToRoomDeletion = 60 * time.Minute // Run starts a tcp listener, run async func Run(debugLevel, host, port, password string, banner ...string) (err error) { @@ -361,6 +361,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er s.rooms.rooms[room] = roomInfo{ first: s.rooms.rooms[room].first, second: s.rooms.rooms[room].second, + isMainRoom: s.rooms.rooms[room].isMainRoom, opened: s.rooms.rooms[room].opened, maxOccupants: s.rooms.rooms[room].maxOccupants, doneTransfers: s.rooms.rooms[room].doneTransfers, @@ -389,16 +390,17 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er s.rooms.roomLocks[room].Unlock() time.Sleep(1 * time.Second) // tell the client that they need to wait - bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return - } + // bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) + // if err != nil { + // return + // } + // err = c.Send(bSend) + // if err != nil { + // log.Error(err) + // return + // } } else { + s.rooms.Lock() newQueue := s.rooms.rooms[room].queue newQueue.Remove(newQueue.Front()) s.rooms.rooms[room] = roomInfo{ @@ -414,16 +416,6 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er break } } - // tell the client that they got the room - bSend, err = crypt.Encrypt([]byte("ok"), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return - } } else { s.rooms.Unlock() bSend, err = crypt.Encrypt([]byte("room full"), strongKeyForEncryption) @@ -480,7 +472,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er wg.Wait() newDoneTransfers := s.rooms.rooms[room].doneTransfers + 1 - if newDoneTransfers == s.rooms.rooms[room].maxOccupants { + if newDoneTransfers == s.rooms.rooms[room].maxOccupants-1 { log.Debugf("room %s is done", room) // delete room s.deleteRoom(room) @@ -539,7 +531,7 @@ func (s *server) deleteRoom(room string) { // chanFromConn creates a channel from a Conn object, and sends everything it // // Read()s from the socket to the channel. -func chanFromConn(conn net.Conn) chan []byte { +func chanFromConn(conn net.Conn, isSender bool) chan []byte { c := make(chan []byte, 1) if err := conn.SetReadDeadline(time.Now().Add(3 * time.Hour)); err != nil { log.Warnf("can't set read deadline: %v", err) @@ -554,14 +546,19 @@ func chanFromConn(conn net.Conn) chan []byte { // Copy the buffer so it doesn't get changed while read by the recipient. copy(res, b[:n]) c <- res + //if finished, then we must exit in order to prevent zombie listeners + if strings.Contains(string(res), "finished") && isSender { + log.Debugf("closing channel for %s", conn.RemoteAddr().String()) + close(c) + break + } } if err != nil { - log.Debug(err) + log.Debugf("closing channel for %s: %v", conn.RemoteAddr().String(), err) c <- nil break } } - log.Debug("exiting") }() return c @@ -570,27 +567,34 @@ func chanFromConn(conn net.Conn) chan []byte { // pipe creates a full-duplex pipe between the two sockets and // transfers data from one to the other. func pipe(conn1 net.Conn, conn2 net.Conn) { - chan1 := chanFromConn(conn1) - chan2 := chanFromConn(conn2) + chan1 := chanFromConn(conn1, true) + chan2 := chanFromConn(conn2, false) for { + log.Debugf("running in pipe %v - %v", conn1.RemoteAddr().String(), conn2.RemoteAddr().String()) select { - case b1 := <-chan1: - if b1 == nil { + case b1, ok := <-chan1: + if b1 == nil || !ok { return } + log.Debugf("got %s bytes from conn 1, sending it to conn 2", b1) if _, err := conn2.Write(b1); err != nil { log.Errorf("write error on channel 1: %v", err) } - case b2 := <-chan2: - if b2 == nil { + case b2, ok := <-chan2: + if b2 == nil || !ok { return } + log.Debugf("got %s bytes from conn 2, sending it to conn 1", b2) if _, err := conn1.Write(b2); err != nil { log.Errorf("write error on channel 2: %v", err) } } + + if chan1 == nil || chan2 == nil { + break + } } } From edd2db82dfacc9900631a73ec68d6f86a89ae731 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sun, 29 Oct 2023 01:00:30 +0300 Subject: [PATCH 07/29] fix multiple transfers --- src/croc/croc.go | 7 +++++-- src/message/message.go | 8 ++++++-- src/tcp/tcp.go | 34 +++++++++++++++++----------------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/croc/croc.go b/src/croc/croc.go index 20331b756..a695d5cb7 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -541,7 +541,9 @@ func (c *Client) establishSecureConnectionWithTCPServer(errchan chan error) (con return } -func (c *Client) listenToMainConn(conn *comm.Comm, ipaddr, banner string, errchan chan error) (err error) { +func (c *Client) listenToMainConn(conn *comm.Comm, ipaddr, banner string, errchan chan error) { + var err error + err = nil for { log.Debug("waiting for bytes") data, errConn := conn.Receive() @@ -572,6 +574,7 @@ func (c *Client) listenToMainConn(conn *comm.Comm, ipaddr, banner string, errcha wg.Add(1) go c.makeTheTransfer(conn, ipaddr, banner, &wg, errchan) wg.Wait() + c.Key = nil c.Step1ChannelSecured = false c.Step2FileInfoTransferred = false c.Step3RecipientRequestFile = false @@ -587,7 +590,7 @@ func (c *Client) listenToMainConn(conn *comm.Comm, ipaddr, banner string, errcha return } } - return + errchan <- err } func (c *Client) makeTheTransfer(conn *comm.Comm, ipaddr, banner string, wg *sync.WaitGroup, errchan chan error) (err error) { diff --git a/src/message/message.go b/src/message/message.go index 1d94b80ae..fa3908a3a 100644 --- a/src/message/message.go +++ b/src/message/message.go @@ -1,6 +1,7 @@ package message import ( + "bytes" "encoding/json" "github.com/schollz/croc/v9/src/comm" @@ -21,6 +22,7 @@ const ( TypeCloseSender Type = "close-sender" TypeRecipientReady Type = "recipientready" TypeFileInfo Type = "fileinfo" + TypeCloseChannel Type = "close-channel" ) // Message is the possible payload for messaging @@ -56,7 +58,9 @@ func Encode(key []byte, m Message) (b []byte, err error) { b = compress.Compress(b) if key != nil { log.Debugf("writing %s message (encrypted)", m.Type) - b, err = crypt.Encrypt(b, key) + if m.Type != TypeFinished { + b, err = crypt.Encrypt(b, key) + } } else { log.Debugf("writing %s message (unencrypted)", m.Type) } @@ -65,7 +69,7 @@ func Encode(key []byte, m Message) (b []byte, err error) { // Decode will convert from bytes func Decode(key []byte, b []byte) (m Message, err error) { - if key != nil { + if key != nil && !bytes.Contains(b, []byte(TypeFinished)) { b, err = crypt.Decrypt(b, key) if err != nil { return diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index dcf6158cb..261f42aba 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -32,7 +32,7 @@ type roomInfo struct { second *comm.Comm queue *list.List isMainRoom bool - maxOccupants int + maxTransfers int doneTransfers int opened time.Time full bool @@ -306,9 +306,9 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er isMainRoom := bytes.Equal(data, []byte("main")) log.Debugf("isMainRoom: %v", isMainRoom) - maxOccupants := 2 + maxTransfers := 1 if isMainRoom { - log.Debug("Wait for maxOccupants") + log.Debug("Wait for maxTransfers") enc, err = c.Receive() if err != nil { return @@ -318,18 +318,18 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er return } - maxOccupants, err = strconv.Atoi(string(data)) + maxTransfers, err = strconv.Atoi(string(data)) if err != nil { return } - log.Debugf("maxOccupants: %v", maxOccupants) + log.Debugf("maxTransfers: %v", maxTransfers) } s.rooms.rooms[room] = roomInfo{ first: c, second: nil, isMainRoom: isMainRoom, - maxOccupants: maxOccupants, + maxTransfers: maxTransfers, doneTransfers: 0, opened: time.Now(), } @@ -363,7 +363,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er second: s.rooms.rooms[room].second, isMainRoom: s.rooms.rooms[room].isMainRoom, opened: s.rooms.rooms[room].opened, - maxOccupants: s.rooms.rooms[room].maxOccupants, + maxTransfers: s.rooms.rooms[room].maxTransfers, doneTransfers: s.rooms.rooms[room].doneTransfers, queue: queue, full: true, @@ -373,7 +373,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er for { s.rooms.roomLocks[room].Lock() - if s.rooms.rooms[room].doneTransfers >= s.rooms.rooms[room].maxOccupants { + if s.rooms.rooms[room].doneTransfers >= s.rooms.rooms[room].maxTransfers { s.rooms.roomLocks[room].Unlock() // tell the client that the sender is no longer available bSend, err = crypt.Encrypt([]byte("sender_gone"), strongKeyForEncryption) @@ -408,7 +408,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er second: c, queue: newQueue, isMainRoom: s.rooms.rooms[room].isMainRoom, - maxOccupants: s.rooms.rooms[room].maxOccupants, + maxTransfers: s.rooms.rooms[room].maxTransfers, doneTransfers: s.rooms.rooms[room].doneTransfers, opened: s.rooms.rooms[room].opened, full: true, @@ -436,7 +436,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er second: c, queue: s.rooms.rooms[room].queue, isMainRoom: s.rooms.rooms[room].isMainRoom, - maxOccupants: s.rooms.rooms[room].maxOccupants, + maxTransfers: s.rooms.rooms[room].maxTransfers, doneTransfers: s.rooms.rooms[room].doneTransfers, opened: s.rooms.rooms[room].opened, full: true, @@ -472,7 +472,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er wg.Wait() newDoneTransfers := s.rooms.rooms[room].doneTransfers + 1 - if newDoneTransfers == s.rooms.rooms[room].maxOccupants-1 { + if newDoneTransfers == s.rooms.rooms[room].maxTransfers { log.Debugf("room %s is done", room) // delete room s.deleteRoom(room) @@ -488,7 +488,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er second: nil, queue: s.rooms.rooms[room].queue, isMainRoom: s.rooms.rooms[room].isMainRoom, - maxOccupants: s.rooms.rooms[room].maxOccupants, + maxTransfers: s.rooms.rooms[room].maxTransfers, doneTransfers: newDoneTransfers, opened: s.rooms.rooms[room].opened, full: lengthOfQueue > 0, @@ -547,8 +547,8 @@ func chanFromConn(conn net.Conn, isSender bool) chan []byte { copy(res, b[:n]) c <- res //if finished, then we must exit in order to prevent zombie listeners - if strings.Contains(string(res), "finished") && isSender { - log.Debugf("closing channel for %s", conn.RemoteAddr().String()) + if bytes.Contains(res, []byte("finished")) && isSender { + log.Debugf("closing sender channel for %s", conn.RemoteAddr().String()) close(c) break } @@ -623,7 +623,7 @@ func PingServer(address string) (err error) { // ConnectToTCPServer will initiate a new connection // to the specified address, room with optional time limit -func ConnectToTCPServer(address, password, room string, isSender, isMainRoom bool, maxOccupants int, timelimit ...time.Duration) (c *comm.Comm, banner string, ipaddr string, err error) { +func ConnectToTCPServer(address, password, room string, isSender, isMainRoom bool, maxTransfers int, timelimit ...time.Duration) (c *comm.Comm, banner string, ipaddr string, err error) { if len(timelimit) > 0 { c, err = comm.NewConnection(address, timelimit[0]) } else { @@ -750,8 +750,8 @@ func ConnectToTCPServer(address, password, room string, isSender, isMainRoom boo } if isMainRoom { - log.Debug("tell server maxOccupants") - bSend, err = crypt.Encrypt([]byte(strconv.Itoa(maxOccupants)), strongKeyForEncryption) + log.Debug("tell server maxTransfers") + bSend, err = crypt.Encrypt([]byte(strconv.Itoa(maxTransfers)), strongKeyForEncryption) if err != nil { log.Debug(err) return From 4711d9e74f32719692d38477d621df757c400947 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sun, 29 Oct 2023 01:17:40 +0300 Subject: [PATCH 08/29] added wait and transfer conclusions messages --- src/croc/croc.go | 18 ++++++++++++------ src/tcp/tcp.go | 18 +++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/croc/croc.go b/src/croc/croc.go index a695d5cb7..a441497b8 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -574,12 +574,6 @@ func (c *Client) listenToMainConn(conn *comm.Comm, ipaddr, banner string, errcha wg.Add(1) go c.makeTheTransfer(conn, ipaddr, banner, &wg, errchan) wg.Wait() - c.Key = nil - c.Step1ChannelSecured = false - c.Step2FileInfoTransferred = false - c.Step3RecipientRequestFile = false - c.Step4FileTransferred = false - c.Step5CloseChannels = false } else if bytes.Equal(data, []byte{1}) { log.Debug("got ping") continue @@ -606,8 +600,20 @@ func (c *Client) makeTheTransfer(conn *comm.Comm, ipaddr, banner string, wg *syn err = c.transfer() if err != nil { errchan <- err + fmt.Print("Did not transfer successfully\n") + } else { + fmt.Print("Transfer successful!\n") } + // reset flags and keys for next transfer + c.Key = nil + c.Step1ChannelSecured = false + c.Step2FileInfoTransferred = false + c.Step3RecipientRequestFile = false + c.Step4FileTransferred = false + c.Step5CloseChannels = false + c.SuccessfulTransfer = false + wg.Done() return } diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index 261f42aba..68ccfd7ae 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -390,15 +390,15 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er s.rooms.roomLocks[room].Unlock() time.Sleep(1 * time.Second) // tell the client that they need to wait - // bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) - // if err != nil { - // return - // } - // err = c.Send(bSend) - // if err != nil { - // log.Error(err) - // return - // } + bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } } else { s.rooms.Lock() newQueue := s.rooms.rooms[room].queue From 3c3ed988606ba9735faa134638d9db9187acbf29 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sun, 29 Oct 2023 01:34:57 +0300 Subject: [PATCH 09/29] small clean up --- src/croc/croc.go | 3 +-- src/message/message.go | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/croc/croc.go b/src/croc/croc.go index a441497b8..3df808ad0 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -79,7 +79,6 @@ type Options struct { ThrottleUpload string ZipFolder bool TestFlag bool - DoubleIps bool } // Client holds the state of the croc transfer @@ -1268,7 +1267,7 @@ func (c *Client) processMessage(payload []byte) (done bool, err error) { log.Debug(err) return } - log.Debugf("Got the message: %s with type: %s", m, m.Type) + // only "pake" messages should be unencrypted // if a non-"pake" message is received unencrypted something // is weird diff --git a/src/message/message.go b/src/message/message.go index fa3908a3a..00433e620 100644 --- a/src/message/message.go +++ b/src/message/message.go @@ -22,7 +22,6 @@ const ( TypeCloseSender Type = "close-sender" TypeRecipientReady Type = "recipientready" TypeFileInfo Type = "fileinfo" - TypeCloseChannel Type = "close-channel" ) // Message is the possible payload for messaging From fec1d3cb70bf2d99960e331040eaa4a1cab58af3 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sun, 29 Oct 2023 02:01:43 +0300 Subject: [PATCH 10/29] refactor tcp --- src/tcp/tcp.go | 319 +++++++++++++++++++++++++++---------------------- 1 file changed, 174 insertions(+), 145 deletions(-) diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index 68ccfd7ae..78da15e60 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -28,8 +28,8 @@ type server struct { } type roomInfo struct { - first *comm.Comm - second *comm.Comm + sender *comm.Comm + receiver *comm.Comm queue *list.List isMainRoom bool maxTransfers int @@ -157,13 +157,13 @@ func (s *server) run() (err error) { return } log.Debugf("room: %+v", s.rooms.rooms[room]) - if s.rooms.rooms[room].first != nil && s.rooms.rooms[room].second != nil { + if s.rooms.rooms[room].sender != nil && s.rooms.rooms[room].receiver != nil { log.Debug("rooms ready") s.rooms.Unlock() break } else { - if s.rooms.rooms[room].first != nil { - errSend := s.rooms.rooms[room].first.Send([]byte{1}) + if s.rooms.rooms[room].sender != nil { + errSend := s.rooms.rooms[room].sender.Send([]byte{1}) if errSend != nil { log.Debug(errSend) deleteIt = true @@ -288,163 +288,192 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er room = string(roomBytes) s.rooms.Lock() - // create the room if it is new if _, ok := s.rooms.rooms[room]; !ok && isSender { - log.Debug("Check if this is a main room") - enc, err = c.Receive() + // create the room if it is new + err = s.createRoom(c, room, strongKeyForEncryption) if err != nil { - return + log.Error(err) } - data, err = crypt.Decrypt(enc, strongKeyForEncryption) + // sender is done + return + } else if s.rooms.rooms[room].full { + // if the room is full, then add to waiting room + err = s.handleWaitingRoomForReceivers(c, room, strongKeyForEncryption) if err != nil { + log.Error(err) return } - if !bytes.Equal(data, []byte("main")) && !bytes.Equal(data, []byte("secondary")) { - err = fmt.Errorf("got bad response: %s", data) - return + } else { + log.Debugf("room %s has 2", room) + s.rooms.rooms[room] = roomInfo{ + sender: s.rooms.rooms[room].sender, + receiver: c, + queue: s.rooms.rooms[room].queue, + isMainRoom: s.rooms.rooms[room].isMainRoom, + maxTransfers: s.rooms.rooms[room].maxTransfers, + doneTransfers: s.rooms.rooms[room].doneTransfers, + opened: s.rooms.rooms[room].opened, + full: true, } - isMainRoom := bytes.Equal(data, []byte("main")) - log.Debugf("isMainRoom: %v", isMainRoom) + s.rooms.roomLocks[room].Lock() + } - maxTransfers := 1 - if isMainRoom { - log.Debug("Wait for maxTransfers") - enc, err = c.Receive() - if err != nil { - return - } - data, err = crypt.Decrypt(enc, strongKeyForEncryption) - if err != nil { - return - } + err = s.beginTransfer(c, room, strongKeyForEncryption) + if err != nil { + log.Error(err) + } - maxTransfers, err = strconv.Atoi(string(data)) - if err != nil { - return - } - log.Debugf("maxTransfers: %v", maxTransfers) - } + return +} - s.rooms.rooms[room] = roomInfo{ - first: c, - second: nil, - isMainRoom: isMainRoom, - maxTransfers: maxTransfers, - doneTransfers: 0, - opened: time.Now(), - } - s.rooms.roomLocks[room] = &sync.Mutex{} - s.rooms.Unlock() +func (s *server) createRoom(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error) { + var enc, data, bSend []byte + log.Debug("Check if this is a main room") + enc, err = c.Receive() + if err != nil { + return + } + data, err = crypt.Decrypt(enc, strongKeyForEncryption) + if err != nil { + return + } + if !bytes.Equal(data, []byte("main")) && !bytes.Equal(data, []byte("secondary")) { + err = fmt.Errorf("got bad response: %s", data) + return + } + isMainRoom := bytes.Equal(data, []byte("main")) + log.Debugf("isMainRoom: %v", isMainRoom) - // tell the client that they got the room - bSend, err = crypt.Encrypt([]byte("ok"), strongKeyForEncryption) + maxTransfers := 1 + if isMainRoom { + log.Debug("Wait for maxTransfers") + enc, err = c.Receive() if err != nil { return } - err = c.Send(bSend) + data, err = crypt.Decrypt(enc, strongKeyForEncryption) if err != nil { - log.Error(err) - s.deleteRoom(room) return } - log.Debugf("room %s has 1", room) + + maxTransfers, err = strconv.Atoi(string(data)) + if err != nil { + return + } + log.Debugf("maxTransfers: %v", maxTransfers) + } + + s.rooms.rooms[room] = roomInfo{ + sender: c, + receiver: nil, + isMainRoom: isMainRoom, + maxTransfers: maxTransfers, + doneTransfers: 0, + opened: time.Now(), + } + s.rooms.roomLocks[room] = &sync.Mutex{} + s.rooms.Unlock() + + // tell the client that they got the room + bSend, err = crypt.Encrypt([]byte("ok"), strongKeyForEncryption) + if err != nil { return } - if s.rooms.rooms[room].full { - if s.rooms.rooms[room].isMainRoom { - log.Debugf("room %s is full, adding to queue", room) - queue := s.rooms.rooms[room].queue - if queue == nil { - queue = list.New() - } - queue.PushBack(c.ID()) - s.rooms.rooms[room] = roomInfo{ - first: s.rooms.rooms[room].first, - second: s.rooms.rooms[room].second, - isMainRoom: s.rooms.rooms[room].isMainRoom, - opened: s.rooms.rooms[room].opened, - maxTransfers: s.rooms.rooms[room].maxTransfers, - doneTransfers: s.rooms.rooms[room].doneTransfers, - queue: queue, - full: true, - } - s.rooms.Unlock() + err = c.Send(bSend) + if err != nil { + log.Error(err) + s.deleteRoom(room) + return + } + log.Debugf("room %s has 1", room) + return +} - for { - s.rooms.roomLocks[room].Lock() - - if s.rooms.rooms[room].doneTransfers >= s.rooms.rooms[room].maxTransfers { - s.rooms.roomLocks[room].Unlock() - // tell the client that the sender is no longer available - bSend, err = crypt.Encrypt([]byte("sender_gone"), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return - } - return - } else if s.rooms.rooms[room].second != nil || s.rooms.rooms[room].queue.Front().Value.(string) != c.ID() { - s.rooms.roomLocks[room].Unlock() - time.Sleep(1 * time.Second) - // tell the client that they need to wait - bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return - } - } else { - s.rooms.Lock() - newQueue := s.rooms.rooms[room].queue - newQueue.Remove(newQueue.Front()) - s.rooms.rooms[room] = roomInfo{ - first: s.rooms.rooms[room].first, - second: c, - queue: newQueue, - isMainRoom: s.rooms.rooms[room].isMainRoom, - maxTransfers: s.rooms.rooms[room].maxTransfers, - doneTransfers: s.rooms.rooms[room].doneTransfers, - opened: s.rooms.rooms[room].opened, - full: true, - } - break - } - } - } else { - s.rooms.Unlock() - bSend, err = crypt.Encrypt([]byte("room full"), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return - } - return +func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error) { + var bSend []byte + if s.rooms.rooms[room].isMainRoom { + log.Debugf("room %s is full, adding to queue", room) + queue := s.rooms.rooms[room].queue + if queue == nil { + queue = list.New() } - } else { - log.Debugf("room %s has 2", room) + queue.PushBack(c.ID()) s.rooms.rooms[room] = roomInfo{ - first: s.rooms.rooms[room].first, - second: c, - queue: s.rooms.rooms[room].queue, + sender: s.rooms.rooms[room].sender, + receiver: s.rooms.rooms[room].receiver, isMainRoom: s.rooms.rooms[room].isMainRoom, + opened: s.rooms.rooms[room].opened, maxTransfers: s.rooms.rooms[room].maxTransfers, doneTransfers: s.rooms.rooms[room].doneTransfers, - opened: s.rooms.rooms[room].opened, + queue: queue, full: true, } - s.rooms.roomLocks[room].Lock() + s.rooms.Unlock() + + for { + s.rooms.roomLocks[room].Lock() + + if s.rooms.rooms[room].doneTransfers >= s.rooms.rooms[room].maxTransfers { + s.rooms.roomLocks[room].Unlock() + // tell the client that the sender is no longer available + bSend, err = crypt.Encrypt([]byte("sender_gone"), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + return + } else if s.rooms.rooms[room].receiver != nil || s.rooms.rooms[room].queue.Front().Value.(string) != c.ID() { + s.rooms.roomLocks[room].Unlock() + time.Sleep(1 * time.Second) + // tell the client that they need to wait + bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + } else { + s.rooms.Lock() + newQueue := s.rooms.rooms[room].queue + newQueue.Remove(newQueue.Front()) + s.rooms.rooms[room] = roomInfo{ + sender: s.rooms.rooms[room].sender, + receiver: c, + queue: newQueue, + isMainRoom: s.rooms.rooms[room].isMainRoom, + maxTransfers: s.rooms.rooms[room].maxTransfers, + doneTransfers: s.rooms.rooms[room].doneTransfers, + opened: s.rooms.rooms[room].opened, + full: true, + } + break + } + } + } else { + s.rooms.Unlock() + bSend, err = crypt.Encrypt([]byte("room full"), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + return } + return +} - otherConnection := s.rooms.rooms[room].first +func (s *server) beginTransfer(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error) { + otherConnection := s.rooms.rooms[room].sender s.rooms.Unlock() // second connection is the sender, time to staple connections @@ -459,8 +488,8 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er log.Debug("done piping") }(otherConnection, c, &wg) - // tell the sender everything is ready - bSend, err = crypt.Encrypt([]byte("ok"), strongKeyForEncryption) + // tell the receiver everything is ready + bSend, err := crypt.Encrypt([]byte("ok"), strongKeyForEncryption) if err != nil { return } @@ -471,6 +500,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er } wg.Wait() + // check if room is done and delete it if so newDoneTransfers := s.rooms.rooms[room].doneTransfers + 1 if newDoneTransfers == s.rooms.rooms[room].maxTransfers { log.Debugf("room %s is done", room) @@ -484,8 +514,8 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er lengthOfQueue = s.rooms.rooms[room].queue.Len() } s.rooms.rooms[room] = roomInfo{ - first: s.rooms.rooms[room].first, - second: nil, + sender: s.rooms.rooms[room].sender, + receiver: nil, queue: s.rooms.rooms[room].queue, isMainRoom: s.rooms.rooms[room].isMainRoom, maxTransfers: s.rooms.rooms[room].maxTransfers, @@ -496,7 +526,6 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er s.rooms.Unlock() s.rooms.roomLocks[room].Unlock() } - return } @@ -518,13 +547,13 @@ func (s *server) deleteRoom(room string) { delete(s.rooms.roomLocks, room) } log.Debugf("deleting room: %s", room) - if s.rooms.rooms[room].first != nil { - s.rooms.rooms[room].first.Close() + if s.rooms.rooms[room].sender != nil { + s.rooms.rooms[room].sender.Close() } - if s.rooms.rooms[room].second != nil { - s.rooms.rooms[room].second.Close() + if s.rooms.rooms[room].receiver != nil { + s.rooms.rooms[room].receiver.Close() } - s.rooms.rooms[room] = roomInfo{first: nil, second: nil} + s.rooms.rooms[room] = roomInfo{sender: nil, receiver: nil} delete(s.rooms.rooms, room) } @@ -546,7 +575,7 @@ func chanFromConn(conn net.Conn, isSender bool) chan []byte { // Copy the buffer so it doesn't get changed while read by the recipient. copy(res, b[:n]) c <- res - //if finished, then we must exit in order to prevent zombie listeners + // if finished, then we must exit in order to prevent zombie listeners if bytes.Contains(res, []byte("finished")) && isSender { log.Debugf("closing sender channel for %s", conn.RemoteAddr().String()) close(c) @@ -685,7 +714,7 @@ func ConnectToTCPServer(address, password, room string, isSender, isMainRoom boo log.Debug(err) return } - log.Debug("waiting for first ok") + log.Debug("waiting for sender ok") enc, err := c.Receive() if err != nil { log.Debug(err) From 1c7e72d63b22ff0c71c7fc7a34dd56a9019083d7 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sun, 29 Oct 2023 21:32:09 +0200 Subject: [PATCH 11/29] cli final from for timelimit and multiple --- src/cli/cli.go | 21 ++++++++++++--------- src/croc/croc.go | 12 +++++------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/cli/cli.go b/src/cli/cli.go index 1bd8f2794..3d76a6d5a 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -65,8 +65,8 @@ func Run() (err error) { ArgsUsage: "[filename(s) or folder]", Flags: []cli.Flag{ &cli.BoolFlag{Name: "zip", Usage: "zip folder before sending"}, - &cli.IntFlag{Name: "timelimit", Value: 30, Usage: "timelimit in secods for sender to allow all transfers"}, - &cli.IntFlag{Name: "multiple", Value: 2, Usage: "maximum number of transfers"}, + &cli.IntFlag{Name: "timelimit", Value: 5, Usage: "timelimit in secods for sender to allow all transfers"}, + &cli.IntFlag{Name: "multiple", Value: 1, Usage: "maximum number of transfers"}, &cli.StringFlag{Name: "code", Aliases: []string{"c"}, Usage: "codephrase used to connect to relay"}, &cli.StringFlag{Name: "hash", Value: "xxhash", Usage: "hash algorithm (xxhash, imohash, md5)"}, &cli.StringFlag{Name: "text", Aliases: []string{"t"}, Usage: "send some text"}, @@ -181,7 +181,7 @@ func send(c *cli.Context) (err error) { crocOptions := croc.Options{ SharedSecret: c.String("code"), IsSender: true, - TimeLimit: c.Int("timeout"), + TimeLimit: c.Int("timelimit"), MaxTransfers: c.Int("multiple"), Debug: c.Bool("debug"), NoPrompt: c.Bool("yes"), @@ -205,12 +205,15 @@ func send(c *cli.Context) (err error) { } if crocOptions.TimeLimit <= 0 { - fmt.Println("timelimit must be greater than 0. Defaulting to 30 seconds.") - crocOptions.TimeLimit = 30 - } - if crocOptions.MaxTransfers <= 1 { - fmt.Println("multiple must be greater than 1. Defaulting to 2 transfers.") - crocOptions.MaxTransfers = 2 + fmt.Println("timelimit must be greater than 0. Defaulting to 360 seconds.") + crocOptions.TimeLimit = 5 + } else if crocOptions.TimeLimit > 3600 { + fmt.Println("timelimit must be less than 3600. Defaulting to 30 seconds.") + crocOptions.TimeLimit = 5 + } + if crocOptions.MaxTransfers <= 0 { + fmt.Println("multiple must be greater than 0. Defaulting to 1 transfers.") + crocOptions.MaxTransfers = 1 } if crocOptions.RelayAddress != models.DEFAULT_RELAY { crocOptions.RelayAddress6 = "" diff --git a/src/croc/croc.go b/src/croc/croc.go index 3df808ad0..e4e0e62a3 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -514,8 +514,7 @@ func (c *Client) transferOverLocalRelay(errchan chan<- error) { } func (c *Client) establishSecureConnectionWithTCPServer(errchan chan error) (conn *comm.Comm, ipaddr, banner string, err error) { - durations := []time.Duration{100 * time.Millisecond, 5 * time.Second} - for i, address := range []string{c.Options.RelayAddress6, c.Options.RelayAddress} { + for _, address := range []string{c.Options.RelayAddress6, c.Options.RelayAddress} { if address == "" { continue } @@ -529,7 +528,7 @@ func (c *Client) establishSecureConnectionWithTCPServer(errchan chan error) (con log.Debugf("got host '%v' and port '%v'", host, port) address = net.JoinHostPort(host, port) log.Debugf("trying connection to %s", address) - conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, c.Options.MaxTransfers, durations[i]) + conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, c.Options.MaxTransfers, time.Duration(c.Options.TimeLimit)*time.Second) if err == nil { c.Options.RelayAddress = address break @@ -789,9 +788,8 @@ func (c *Client) Receive() (err error) { log.Debug("establishing connection") } var banner string - durations := []time.Duration{200 * time.Millisecond, 5 * time.Second} err = fmt.Errorf("found no addresses to connect") - for i, address := range []string{c.Options.RelayAddress6, c.Options.RelayAddress} { + for _, address := range []string{c.Options.RelayAddress6, c.Options.RelayAddress} { if address == "" { continue } @@ -805,7 +803,7 @@ func (c *Client) Receive() (err error) { log.Debugf("got host '%v' and port '%v'", host, port) address = net.JoinHostPort(host, port) log.Debugf("trying connection to %s", address) - c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, 2, durations[i]) + c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, 1, time.Duration(c.Options.TimeLimit)*time.Second) if err == nil { c.Options.RelayAddress = address break @@ -863,7 +861,7 @@ func (c *Client) Receive() (err error) { } serverTry := net.JoinHostPort(ip, port) - conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, 2, 500*time.Millisecond) + conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, 1, 500*time.Millisecond) if errConn != nil { log.Debug(errConn) log.Debugf("could not connect to " + serverTry) From 8f7e7f763e01bc54f1efa5dea60bc080193c2885 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Wed, 1 Nov 2023 00:37:55 +0200 Subject: [PATCH 12/29] update tcp test and fix sender gone problems --- src/tcp/tcp.go | 245 +++++++++++++++++++++++++------------------- src/tcp/tcp_test.go | 221 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 345 insertions(+), 121 deletions(-) diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index 78da15e60..0d5d4528c 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -35,7 +35,7 @@ type roomInfo struct { maxTransfers int doneTransfers int opened time.Time - full bool + transfering bool } type roomMap struct { @@ -46,6 +46,12 @@ type roomMap struct { const pingRoom = "pinglkasjdlfjsaldjf" +var ( + fullRoom = []byte("room_full") + senderGone = []byte("sender_gone") + noRoom = []byte("room_non_existent") +) + var timeToRoomDeletion = 60 * time.Minute // Run starts a tcp listener, run async @@ -288,19 +294,52 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er room = string(roomBytes) s.rooms.Lock() - if _, ok := s.rooms.rooms[room]; !ok && isSender { - // create the room if it is new - err = s.createRoom(c, room, strongKeyForEncryption) - if err != nil { - log.Error(err) + if _, ok := s.rooms.rooms[room]; !ok { + if isSender { + // create the room if it is new + err = s.createRoom(c, room, strongKeyForEncryption) + if err != nil { + log.Error(err) + } + // sender is done + return + } else { + // if the room does not exist and the client is a receiver, then tell them + // that the room does not exist + s.rooms.Unlock() + bSend, err = crypt.Encrypt([]byte(noRoom), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + + return "", fmt.Errorf("reciever tried to connect to room that does not exist") } - // sender is done - return - } else if s.rooms.rooms[room].full { - // if the room is full, then add to waiting room - err = s.handleWaitingRoomForReceivers(c, room, strongKeyForEncryption) - if err != nil { - log.Error(err) + } else if s.rooms.rooms[room].transfering { + // if the room has a transfer going on + if s.rooms.rooms[room].maxTransfers > 1 { + // if the room is a multi-transfer room then add to queue + err = s.handleWaitingRoomForReceivers(c, room, strongKeyForEncryption) + if err != nil { + log.Error(err) + return + } + } else { + // otherwise, tell the client that the room is full + s.rooms.Unlock() + bSend, err = crypt.Encrypt([]byte(fullRoom), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } return } } else { @@ -313,7 +352,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er maxTransfers: s.rooms.rooms[room].maxTransfers, doneTransfers: s.rooms.rooms[room].doneTransfers, opened: s.rooms.rooms[room].opened, - full: true, + transfering: true, } s.rooms.roomLocks[room].Lock() } @@ -391,83 +430,69 @@ func (s *server) createRoom(c *comm.Comm, room string, strongKeyForEncryption [] func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error) { var bSend []byte - if s.rooms.rooms[room].isMainRoom { - log.Debugf("room %s is full, adding to queue", room) - queue := s.rooms.rooms[room].queue - if queue == nil { - queue = list.New() - } - queue.PushBack(c.ID()) - s.rooms.rooms[room] = roomInfo{ - sender: s.rooms.rooms[room].sender, - receiver: s.rooms.rooms[room].receiver, - isMainRoom: s.rooms.rooms[room].isMainRoom, - opened: s.rooms.rooms[room].opened, - maxTransfers: s.rooms.rooms[room].maxTransfers, - doneTransfers: s.rooms.rooms[room].doneTransfers, - queue: queue, - full: true, - } - s.rooms.Unlock() + log.Debugf("room %s is full, adding to queue", room) + queue := s.rooms.rooms[room].queue + if queue == nil { + queue = list.New() + } + queue.PushBack(c.ID()) + s.rooms.rooms[room] = roomInfo{ + sender: s.rooms.rooms[room].sender, + receiver: s.rooms.rooms[room].receiver, + isMainRoom: s.rooms.rooms[room].isMainRoom, + opened: s.rooms.rooms[room].opened, + maxTransfers: s.rooms.rooms[room].maxTransfers, + doneTransfers: s.rooms.rooms[room].doneTransfers, + queue: queue, + transfering: true, + } + s.rooms.Unlock() - for { - s.rooms.roomLocks[room].Lock() + for { + s.rooms.roomLocks[room].Lock() - if s.rooms.rooms[room].doneTransfers >= s.rooms.rooms[room].maxTransfers { - s.rooms.roomLocks[room].Unlock() - // tell the client that the sender is no longer available - bSend, err = crypt.Encrypt([]byte("sender_gone"), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return - } + if s.rooms.rooms[room].doneTransfers >= s.rooms.rooms[room].maxTransfers { + // tell the client that the sender is no longer available + bSend, err = crypt.Encrypt([]byte(senderGone), strongKeyForEncryption) + if err != nil { return - } else if s.rooms.rooms[room].receiver != nil || s.rooms.rooms[room].queue.Front().Value.(string) != c.ID() { - s.rooms.roomLocks[room].Unlock() - time.Sleep(1 * time.Second) - // tell the client that they need to wait - bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return - } - } else { - s.rooms.Lock() - newQueue := s.rooms.rooms[room].queue - newQueue.Remove(newQueue.Front()) - s.rooms.rooms[room] = roomInfo{ - sender: s.rooms.rooms[room].sender, - receiver: c, - queue: newQueue, - isMainRoom: s.rooms.rooms[room].isMainRoom, - maxTransfers: s.rooms.rooms[room].maxTransfers, - doneTransfers: s.rooms.rooms[room].doneTransfers, - opened: s.rooms.rooms[room].opened, - full: true, - } - break } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + s.rooms.roomLocks[room].Unlock() + break + } else if s.rooms.rooms[room].receiver != nil || s.rooms.rooms[room].queue.Front().Value.(string) != c.ID() { + time.Sleep(1 * time.Second) + // tell the client that they need to wait + bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + s.rooms.roomLocks[room].Unlock() + } else { + s.rooms.Lock() + newQueue := s.rooms.rooms[room].queue + newQueue.Remove(newQueue.Front()) + s.rooms.rooms[room] = roomInfo{ + sender: s.rooms.rooms[room].sender, + receiver: c, + queue: newQueue, + isMainRoom: s.rooms.rooms[room].isMainRoom, + maxTransfers: s.rooms.rooms[room].maxTransfers, + doneTransfers: s.rooms.rooms[room].doneTransfers, + opened: s.rooms.rooms[room].opened, + transfering: true, + } + break } - } else { - s.rooms.Unlock() - bSend, err = crypt.Encrypt([]byte("room full"), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return - } - return } return } @@ -495,35 +520,37 @@ func (s *server) beginTransfer(c *comm.Comm, room string, strongKeyForEncryption } err = c.Send(bSend) if err != nil { - s.deleteRoom(room) return } wg.Wait() // check if room is done and delete it if so newDoneTransfers := s.rooms.rooms[room].doneTransfers + 1 + + // update the room info + s.rooms.Lock() + lengthOfQueue := 0 + if s.rooms.rooms[room].queue != nil { + lengthOfQueue = s.rooms.rooms[room].queue.Len() + } + s.rooms.rooms[room] = roomInfo{ + sender: s.rooms.rooms[room].sender, + receiver: nil, + queue: s.rooms.rooms[room].queue, + isMainRoom: s.rooms.rooms[room].isMainRoom, + maxTransfers: s.rooms.rooms[room].maxTransfers, + doneTransfers: newDoneTransfers, + opened: s.rooms.rooms[room].opened, + transfering: lengthOfQueue > 0 && newDoneTransfers < s.rooms.rooms[room].maxTransfers, + } + s.rooms.Unlock() + + // delete the room if it is done or unlock it if it is not if newDoneTransfers == s.rooms.rooms[room].maxTransfers { log.Debugf("room %s is done", room) - // delete room s.deleteRoom(room) } else { log.Debugf("room %s has %d done", room, newDoneTransfers) - s.rooms.Lock() - lengthOfQueue := 0 - if s.rooms.rooms[room].queue != nil { - lengthOfQueue = s.rooms.rooms[room].queue.Len() - } - s.rooms.rooms[room] = roomInfo{ - sender: s.rooms.rooms[room].sender, - receiver: nil, - queue: s.rooms.rooms[room].queue, - isMainRoom: s.rooms.rooms[room].isMainRoom, - maxTransfers: s.rooms.rooms[room].maxTransfers, - doneTransfers: newDoneTransfers, - opened: s.rooms.rooms[room].opened, - full: lengthOfQueue > 0, - } - s.rooms.Unlock() s.rooms.roomLocks[room].Unlock() } return @@ -810,10 +837,16 @@ func ConnectToTCPServer(address, password, room string, isSender, isMainRoom boo log.Debug("waiting for sender to be free") time.Sleep(1 * time.Second) continue - } else if bytes.Equal(data, []byte("sender_gone")) { + } else if bytes.Equal(data, []byte(senderGone)) { + err = fmt.Errorf("sender is gone") + c = nil + return + } else if bytes.Equal(data, []byte(fullRoom)) { + err = fmt.Errorf("room is full") c = nil return - } else if bytes.Equal(data, []byte("room full")) { + } else if bytes.Equal(data, []byte(noRoom)) { + err = fmt.Errorf("room does not exist") c = nil return } diff --git a/src/tcp/tcp_test.go b/src/tcp/tcp_test.go index 769d999eb..6fd48133f 100644 --- a/src/tcp/tcp_test.go +++ b/src/tcp/tcp_test.go @@ -3,9 +3,11 @@ package tcp import ( "bytes" "fmt" + "strings" "testing" "time" + "github.com/schollz/croc/v9/src/comm" log "github.com/schollz/logger" "github.com/stretchr/testify/assert" ) @@ -16,35 +18,41 @@ func BenchmarkConnection(b *testing.B) { time.Sleep(100 * time.Millisecond) b.ResetTimer() for i := 0; i < b.N; i++ { - c, _, _, _ := ConnectToTCPServer("127.0.0.1:8283", "pass123", fmt.Sprintf("testroom%d", i), 1*time.Minute) + c, _, _, _ := ConnectToTCPServer("127.0.0.1:8283", "pass123", fmt.Sprintf("testroom%d", i), true, true, 1, 1*time.Minute) c.Close() } } -func TestTCP(t *testing.T) { +func TestTCPServerPing(t *testing.T) { log.SetLevel("error") - timeToRoomDeletion = 100 * time.Millisecond go Run("debug", "127.0.0.1", "8381", "pass123", "8382") - time.Sleep(timeToRoomDeletion) + time.Sleep(100 * time.Millisecond) err := PingServer("127.0.0.1:8381") assert.Nil(t, err) err = PingServer("127.0.0.1:8333") assert.NotNil(t, err) +} - time.Sleep(timeToRoomDeletion) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", 1*time.Minute) - assert.Equal(t, banner, "8382") - assert.Nil(t, err) - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom") - assert.Nil(t, err) - _, _, _, err = ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom") - assert.NotNil(t, err) - _, _, _, err = ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", 1*time.Nanosecond) +// Test that a reeciver cannot connect to a non-existent room +func TestTCPServerNonExistentRoom(t *testing.T) { + log.SetLevel("error") + go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + time.Sleep(100 * time.Millisecond) + + c1, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1) assert.NotNil(t, err) + assert.True(t, strings.Contains(err.Error(), "room does not exist")) + assert.Nil(t, c1) +} - // try sending data - assert.Nil(t, c1.Send([]byte("hello, c2"))) +// This is helper function to test that a mocks a transfer +// between two clients connected to the server, +// and checks that the data is transferred correctly +func mockTransfer(c1, c2 *comm.Comm, t *testing.T) { + // try sending data to check the pipe is working properly var data []byte + var err error + assert.Nil(t, c1.Send([]byte("hello, c2"))) for { data, err = c2.Receive() if bytes.Equal(data, []byte{1}) { @@ -65,7 +73,190 @@ func TestTCP(t *testing.T) { } assert.Nil(t, err) assert.Equal(t, []byte("hello, c1"), data) +} + +// Test that a successful transfer can be made +func TestTCPServerSingleConnectionTransfer(t *testing.T) { + log.SetLevel("error") + go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + time.Sleep(100 * time.Millisecond) + + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 1*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c1) + assert.Equal(t, banner, "8382") + + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1) + assert.Nil(t, err) + assert.NotNil(t, c2) + + mockTransfer(c1, c2, t) c1.Close() + c2.Close() time.Sleep(300 * time.Millisecond) } + +// Test that a third client cannot connect +// to a room that already has two clients +// connected to it with maxTransfers=1 +func TestTCPSingleConnectionOnly2Clients(t *testing.T) { + log.SetLevel("error") + go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + time.Sleep(100 * time.Millisecond) + + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 10*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c1) + assert.Equal(t, banner, "8382") + + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 10*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c2) + closeChan := make(chan int) + + // we need to run this transfer in a goroutine because + // otherwise connections will be idle and the server will + // close them when we try to connect a third client + go func() { + for { + select { + case <-closeChan: + fmt.Println("Closing go routine") + return + default: + mockTransfer(c1, c2, t) + } + } + }() + + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 5*time.Minute) + assert.NotNil(t, err) + assert.True(t, strings.Contains(err.Error(), "room is full")) + assert.Nil(t, c3) + closeChan <- 1 +} + +// Test that the server can handle multiple +// successive transfers from the same sender +func TestTCPMultipleConnectionTransfer(t *testing.T) { + log.SetLevel("error") + go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + time.Sleep(100 * time.Millisecond) + + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 2, 10*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c1) + assert.Equal(t, banner, "8382") + + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 10*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c2) + + mockTransfer(c1, c2, t) + c2.Close() + // tell c1 to close pipe listener + c1.Send([]byte("finished")) + time.Sleep(100 * time.Millisecond) + + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 5*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c3) + + mockTransfer(c1, c3, t) +} + +// Test that for a room with maxTransfers>=2, +// the receivers are queued if there is a transfer +// in progress already, and the receiver is allowed +// to connect when the transfer is finished +func TestTCPMultipleConnectionWaitingRoom(t *testing.T) { + log.SetLevel("error") + go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + time.Sleep(100 * time.Millisecond) + + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 2, 10*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c1) + assert.Equal(t, banner, "8382") + + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 10*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c2) + + // we need to run this transfer in a goroutine because + // otherwise connections will be idle and the server will + // close them when we try to connect a third client + go func() { + counter := 1 + time.Sleep(100 * time.Millisecond) + for { + mockTransfer(c1, c2, t) + if counter == 5 { + c2.Close() + // tell c1 to close pipe listener + c1.Send([]byte("finished")) + break + } + counter++ + } + }() + + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 5*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c3) + + mockTransfer(c1, c3, t) +} + +// Test that for a room with maxTransfers>=2, +// if there are receivers queued they will get a +// nottification that the room is no longer available +// when the sender the maxTransfers limit is reached +func TestTCPMultipleConnectionWaitingRoomCloses(t *testing.T) { + log.SetLevel("error") + go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + time.Sleep(100 * time.Millisecond) + + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 2, 10*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c1) + assert.Equal(t, banner, "8382") + + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 10*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c2) + + // one transfer + mockTransfer(c1, c2, t) + c2.Close() + // tell c1 to close pipe listener + c1.Send([]byte("finished")) + + c2, _, _, err = ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 10*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c2) + + // we need to run this transfer in a goroutine because + // otherwise connections will be idle and the server will + // close them when we try to connect a third client + go func() { + counter := 1 + time.Sleep(100 * time.Millisecond) + for { + mockTransfer(c1, c2, t) + if counter == 5 { + c2.Close() + // tell c1 to close pipe listener + c1.Send([]byte("finished")) + break + } + counter++ + } + }() + + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 5*time.Minute) + assert.NotNil(t, err) + assert.True(t, strings.Contains(err.Error(), "sender is gone")) + assert.Nil(t, c3) +} From 51427f0db1ae8ffc3666e61fafa9b248d85296ae Mon Sep 17 00:00:00 2001 From: RCL98 Date: Wed, 1 Nov 2023 00:44:51 +0200 Subject: [PATCH 13/29] update message test --- src/message/message_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/message/message_test.go b/src/message/message_test.go index eae58f5f0..b0f3a67c8 100644 --- a/src/message/message_test.go +++ b/src/message/message_test.go @@ -1,6 +1,7 @@ package message import ( + "bytes" "crypto/rand" "fmt" "net" @@ -15,6 +16,18 @@ import ( var TypeMessage Type = "message" +// Test that the finished message is not encrypted +func TestMessageDontEncryptFinished(t *testing.T) { + log.SetLevel("debug") + m := Message{Type: TypeFinished, Message: "hello, world"} + e, salt, err := crypt.New([]byte("pass"), nil) + assert.Nil(t, err) + fmt.Println(string(salt)) + b, err := Encode(e, m) + assert.Nil(t, err) + assert.True(t, bytes.Contains(b, []byte("hello, world"))) +} + func TestMessage(t *testing.T) { log.SetLevel("debug") m := Message{Type: TypeMessage, Message: "hello, world"} From 394675274382932635bb207fe4bb354266991bdb Mon Sep 17 00:00:00 2001 From: RCL98 Date: Tue, 7 Nov 2023 00:17:13 +0200 Subject: [PATCH 14/29] update croc unit test and fix tcp and crocs bugs --- src/croc/croc.go | 12 ++++----- src/croc/croc_test.go | 10 +++++++ src/tcp/tcp.go | 61 +++++++++++++++++++++++++------------------ 3 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/croc/croc.go b/src/croc/croc.go index e4e0e62a3..00e70e191 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -665,12 +665,12 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t err = fmt.Errorf("could not connect to %s: %w", c.Options.RelayAddress, err) log.Debug(err) errchan <- err - } - - log.Debugf("banner: %s", banner) - log.Debugf("connection established: %+v", conn) + } else { + log.Debugf("banner: %s", banner) + log.Debugf("connection established: %+v", conn) - c.listenToMainConn(conn, ipaddr, banner, errchan) + c.listenToMainConn(conn, ipaddr, banner, errchan) + } } err = <-errchan @@ -966,7 +966,7 @@ func (c *Client) transfer() (err error) { } } - if c.Options.Stdout && !c.Options.IsSender { + if c.Options.Stdout && !c.Options.IsSender && c.FilesToTransfer != nil && len(c.FilesToTransfer) > 0 { pathToFile := path.Join( c.FilesToTransfer[c.FilesToTransferCurrentNum].FolderRemote, c.FilesToTransfer[c.FilesToTransferCurrentNum].Name, diff --git a/src/croc/croc_test.go b/src/croc/croc_test.go index dec48abc9..46163c40c 100644 --- a/src/croc/croc_test.go +++ b/src/croc/croc_test.go @@ -31,6 +31,8 @@ func TestCrocReadme(t *testing.T) { log.Debug("setting up sender") sender, err := New(Options{ IsSender: true, + TimeLimit: 30, + MaxTransfers: 1, SharedSecret: "8123-testingthecroc", Debug: true, RelayAddress: "127.0.0.1:8281", @@ -97,6 +99,8 @@ func TestCrocEmptyFolder(t *testing.T) { log.Debug("setting up sender") sender, err := New(Options{ IsSender: true, + TimeLimit: 30, + MaxTransfers: 1, SharedSecret: "8123-testingthecroc", Debug: true, RelayAddress: "127.0.0.1:8281", @@ -164,6 +168,8 @@ func TestCrocSymlink(t *testing.T) { log.Debug("setting up sender") sender, err := New(Options{ IsSender: true, + TimeLimit: 30, + MaxTransfers: 1, SharedSecret: "8124-testingthecroc", Debug: true, RelayAddress: "127.0.0.1:8281", @@ -239,6 +245,8 @@ func TestCrocLocal(t *testing.T) { log.Debug("setting up sender") sender, err := New(Options{ IsSender: true, + TimeLimit: 30, + MaxTransfers: 1, SharedSecret: "8123-testingthecroc", Debug: true, RelayAddress: "127.0.0.1:8181", @@ -318,6 +326,8 @@ func TestCrocError(t *testing.T) { log.SetLevel("warn") sender, _ := New(Options{ IsSender: true, + TimeLimit: 30, + MaxTransfers: 1, SharedSecret: "8123-testingthecroc2", Debug: true, RelayAddress: "doesntexistok.com:8381", diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index 0d5d4528c..f0eeb00b5 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -294,8 +294,8 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er room = string(roomBytes) s.rooms.Lock() - if _, ok := s.rooms.rooms[room]; !ok { - if isSender { + if isSender { + if _, ok := s.rooms.rooms[room]; !ok { // create the room if it is new err = s.createRoom(c, room, strongKeyForEncryption) if err != nil { @@ -304,21 +304,27 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er // sender is done return } else { - // if the room does not exist and the client is a receiver, then tell them - // that the room does not exist - s.rooms.Unlock() - bSend, err = crypt.Encrypt([]byte(noRoom), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return - } + // if the room already exists, then tell the client that the room is full + err = s.sendRoomIsFull(c, strongKeyForEncryption) + return + } + } - return "", fmt.Errorf("reciever tried to connect to room that does not exist") + if _, ok := s.rooms.rooms[room]; !ok { + // if the room does not exist and the client is a receiver, then tell them + // that the room does not exist + s.rooms.Unlock() + bSend, err = crypt.Encrypt([]byte(noRoom), strongKeyForEncryption) + if err != nil { + return } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + + return "", fmt.Errorf("reciever tried to connect to room that does not exist") } else if s.rooms.rooms[room].transfering { // if the room has a transfer going on if s.rooms.rooms[room].maxTransfers > 1 { @@ -330,16 +336,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er } } else { // otherwise, tell the client that the room is full - s.rooms.Unlock() - bSend, err = crypt.Encrypt([]byte(fullRoom), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - return - } + err = s.sendRoomIsFull(c, strongKeyForEncryption) return } } else { @@ -365,6 +362,20 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er return } +func (s *server) sendRoomIsFull(c *comm.Comm, strongKeyForEncryption []byte) (err error) { + s.rooms.Unlock() + bSend, err := crypt.Encrypt([]byte(fullRoom), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + return + } + return +} + func (s *server) createRoom(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error) { var enc, data, bSend []byte log.Debug("Check if this is a main room") From 15960a697d9e3555ee3764eec0a928117f7c0783 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Tue, 7 Nov 2023 00:23:32 +0200 Subject: [PATCH 15/29] update comm test --- src/comm/comm_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/comm/comm_test.go b/src/comm/comm_test.go index afba91f80..ea89dcb07 100644 --- a/src/comm/comm_test.go +++ b/src/comm/comm_test.go @@ -51,6 +51,7 @@ func TestComm(t *testing.T) { time.Sleep(300 * time.Millisecond) a, err := NewConnection("127.0.0.1:"+port, 10*time.Minute) assert.Nil(t, err) + assert.NotNil(t, a.id) data, err := a.Receive() assert.Equal(t, []byte("hello, world"), data) assert.Nil(t, err) From 26144d6684d2607d3c4ee5b9fdfb7ba8713b7d69 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Tue, 7 Nov 2023 00:55:26 +0200 Subject: [PATCH 16/29] allow multiple transfers on local connection --- src/croc/croc.go | 63 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/src/croc/croc.go b/src/croc/croc.go index 00e70e191..a1d2ae862 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -37,6 +37,7 @@ import ( var ( ipRequest = []byte("ips?") handshakeRequest = []byte("handshake") + wgTransfer sync.WaitGroup ) func init() { @@ -478,7 +479,20 @@ func (c *Client) broadcastOnLocalNetwork(useipv6 bool) { } } -func (c *Client) transferOverLocalRelay(errchan chan<- error) { +func (c *Client) resetFlagsAndRealeaseLock() { + // reset flags and keys for next transfer + c.Key = nil + c.Step1ChannelSecured = false + c.Step2FileInfoTransferred = false + c.Step3RecipientRequestFile = false + c.Step4FileTransferred = false + c.Step5CloseChannels = false + c.SuccessfulTransfer = false + + wgTransfer.Done() +} + +func (c *Client) transferOverLocalRelay(errchan chan error) { time.Sleep(500 * time.Millisecond) log.Debug("establishing connection") var banner string @@ -491,16 +505,27 @@ func (c *Client) transferOverLocalRelay(errchan chan<- error) { return } log.Debugf("local connection established: %+v", conn) + err = nil for { - data, _ := conn.Receive() - if bytes.Equal(data, handshakeRequest) { + data, errConn := conn.Receive() + if errConn != nil { + log.Debugf("[%+v] had error: %s", conn, errConn.Error()) break + } + if bytes.Equal(data, handshakeRequest) { + wgTransfer.Add(1) + go c.makeLocalTransfer(conn, ipaddr, banner, errchan) + wgTransfer.Wait() } else if bytes.Equal(data, []byte{1}) { log.Debug("got ping") } else { log.Debugf("instead of handshake got: %s", data) } } + errchan <- err +} + +func (c *Client) makeLocalTransfer(conn *comm.Comm, ipaddr, banner string, errchan chan error) (err error) { c.conn[0] = conn log.Debug("exchanged header message") c.Options.RelayAddress = "127.0.0.1" @@ -510,7 +535,18 @@ func (c *Client) transferOverLocalRelay(errchan chan<- error) { c.Options.RelayPorts = []string{c.Options.RelayPorts[0]} } c.ExternalIP = ipaddr - errchan <- c.transfer() + + err = c.transfer() + if err != nil { + errchan <- err + fmt.Print("Did not transfer successfully locally\n") + } else { + fmt.Print("Local transfer was successful!\n") + } + + c.resetFlagsAndRealeaseLock() + + return } func (c *Client) establishSecureConnectionWithTCPServer(errchan chan error) (conn *comm.Comm, ipaddr, banner string, err error) { @@ -568,10 +604,9 @@ func (c *Client) listenToMainConn(conn *comm.Comm, ipaddr, banner string, errcha log.Errorf("error sending: %v", err) } } else if bytes.Equal(data, handshakeRequest) { - var wg sync.WaitGroup - wg.Add(1) - go c.makeTheTransfer(conn, ipaddr, banner, &wg, errchan) - wg.Wait() + wgTransfer.Add(1) + go c.makeTheTransfer(conn, ipaddr, banner, errchan) + wgTransfer.Wait() } else if bytes.Equal(data, []byte{1}) { log.Debug("got ping") continue @@ -585,7 +620,7 @@ func (c *Client) listenToMainConn(conn *comm.Comm, ipaddr, banner string, errcha errchan <- err } -func (c *Client) makeTheTransfer(conn *comm.Comm, ipaddr, banner string, wg *sync.WaitGroup, errchan chan error) (err error) { +func (c *Client) makeTheTransfer(conn *comm.Comm, ipaddr, banner string, errchan chan error) (err error) { c.conn[0] = conn c.Options.RelayPorts = strings.Split(banner, ",") if c.Options.NoMultiplexing { @@ -603,16 +638,8 @@ func (c *Client) makeTheTransfer(conn *comm.Comm, ipaddr, banner string, wg *syn fmt.Print("Transfer successful!\n") } - // reset flags and keys for next transfer - c.Key = nil - c.Step1ChannelSecured = false - c.Step2FileInfoTransferred = false - c.Step3RecipientRequestFile = false - c.Step4FileTransferred = false - c.Step5CloseChannels = false - c.SuccessfulTransfer = false + c.resetFlagsAndRealeaseLock() - wg.Done() return } From 6f8039be465b3c1a56c337d316cccfd62e5270de Mon Sep 17 00:00:00 2001 From: RCL98 Date: Tue, 7 Nov 2023 23:30:55 +0200 Subject: [PATCH 17/29] update readme and cli --- README.md | 30 +++++++++++++++++++++++++++++- src/cli/cli.go | 22 +++++++++++----------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 803316a4b..86c8ac04d 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Status"> - provides **end-to-end encryption** (using PAKE) - enables easy **cross-platform** transfers (Windows, Linux, Mac) - allows **multiple file** transfers +- allows **multiple sequential** transfers - allows **resuming transfers** that are interrupted - local server or port-forwarding **not needed** - **ipv6-first** with ipv4 fallback @@ -134,6 +135,33 @@ The code phrase is used to establish password-authenticated key agreement ([PAKE There are a number of configurable options (see `--help`). A set of options (like custom relay, ports, and code phrase) can be set using `--remember`. +### Transfer on LAN only + +You can transfer files using only local connections. + +``` +crorc --local send [file(s)-or-folder] +``` + +### Allow multiple sequential transfers + +By default, after a transfer was done the program stops. +You can allow more than one transfer to happen (one after another) by using the `--multuple` flag which requires a value >= 1. + +``` +croc send --multiple [nr-of-transfers] [file(s)-or-folder] +``` + +After all `[nr-of-transfers]` were done, the program will stop. To prevent keeping the program running forever if not all transfers +possibilities are used, a timeout is set on the connection with the relay. By default, this `timeout` is set to `30 seconds`, which is +likely not enough. If you want to keep the connection alive for more time you can use the `--timeout` flag like this: + +``` +croc send --timeout [nr-of-seconds] (--multiple [nr-of-transfers]) [file(s)-or-folder] +``` + +*NOTE*: You can't keep the connection alive for more than `1 hour`. + ### Custom code phrase You can send with your own code phrase (must be more than 6 characters). @@ -213,7 +241,7 @@ The relay is needed to staple the parallel incoming and outgoing connections. By croc relay ``` -By default it uses TCP ports 9009-9013. Make sure to open those up. You can customize the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer. +By default it uses TCP ports 9009-9013. Make sure to open those up. You can customized the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer. You can send files using your relay by entering `--relay` to change the relay that you are using if you want to custom host your own. diff --git a/src/cli/cli.go b/src/cli/cli.go index 567a346ca..fd7bf97bb 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -44,7 +44,6 @@ func Run() (err error) { app.UsageText = `Send a file: croc send file.txt - -git to respect your .gitignore Send multiple files: croc send file1.txt file2.txt file3.txt or @@ -66,14 +65,13 @@ func Run() (err error) { ArgsUsage: "[filename(s) or folder]", Flags: []cli.Flag{ &cli.BoolFlag{Name: "zip", Usage: "zip folder before sending"}, - &cli.IntFlag{Name: "timelimit", Value: 5, Usage: "timelimit in secods for sender to allow all transfers"}, + &cli.IntFlag{Name: "timelimit", Value: 30, Usage: "timelimit in secods for sender to allow all transfers"}, &cli.IntFlag{Name: "multiple", Value: 1, Usage: "maximum number of transfers"}, &cli.StringFlag{Name: "code", Aliases: []string{"c"}, Usage: "codephrase used to connect to relay"}, &cli.StringFlag{Name: "hash", Value: "xxhash", Usage: "hash algorithm (xxhash, imohash, md5)"}, &cli.StringFlag{Name: "text", Aliases: []string{"t"}, Usage: "send some text"}, &cli.BoolFlag{Name: "no-local", Usage: "disable local relay when sending"}, &cli.BoolFlag{Name: "no-multi", Usage: "disable multiplexing"}, - &cli.BoolFlag{Name: "git", Usage: "enable .gitignore respect / don't send ignored files"}, &cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"}, }, HelpName: "croc send", @@ -204,20 +202,25 @@ func send(c *cli.Context) (err error) { HashAlgorithm: c.String("hash"), ThrottleUpload: c.String("throttleUpload"), ZipFolder: c.Bool("zip"), - GitIgnore: c.Bool("git"), } if crocOptions.TimeLimit <= 0 { - fmt.Println("timelimit must be greater than 0. Defaulting to 360 seconds.") - crocOptions.TimeLimit = 5 + fmt.Println("timelimit must be greater than 0. Defaulting to 30 seconds.") + crocOptions.TimeLimit = 30 } else if crocOptions.TimeLimit > 3600 { fmt.Println("timelimit must be less than 3600. Defaulting to 30 seconds.") - crocOptions.TimeLimit = 5 + crocOptions.TimeLimit = 30 } + if crocOptions.MaxTransfers <= 0 { fmt.Println("multiple must be greater than 0. Defaulting to 1 transfers.") crocOptions.MaxTransfers = 1 + } else if crocOptions.MaxTransfers > 1 { + fmt.Println("Allowing multiple transfers.") + fmt.Println("The connection will stay open until all transfers are complete, or the timelimit is reached.") + fmt.Printf("The current timelimit is %d seconds.\n", crocOptions.TimeLimit) } + if crocOptions.RelayAddress != models.DEFAULT_RELAY { crocOptions.RelayAddress6 = "" } else if crocOptions.RelayAddress6 != models.DEFAULT_RELAY6 { @@ -263,9 +266,6 @@ func send(c *cli.Context) (err error) { if !c.IsSet("hash") { crocOptions.HashAlgorithm = rememberedOptions.HashAlgorithm } - if !c.IsSet("git") { - crocOptions.GitIgnore = rememberedOptions.GitIgnore - } } var fnames []string @@ -304,7 +304,7 @@ func send(c *cli.Context) (err error) { // generate code phrase crocOptions.SharedSecret = utils.GetRandomName() } - minimalFileInfos, emptyFoldersToTransfer, totalNumberFolders, err := croc.GetFilesInfo(fnames, crocOptions.ZipFolder, crocOptions.GitIgnore) + minimalFileInfos, emptyFoldersToTransfer, totalNumberFolders, err := croc.GetFilesInfo(fnames, crocOptions.ZipFolder) if err != nil { return } From 20fb6cee36ab9ba8d9f50604a6dac6a320eebed1 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sat, 11 Nov 2023 22:32:59 +0200 Subject: [PATCH 18/29] fix build problem --- src/cli/cli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/cli.go b/src/cli/cli.go index fd7bf97bb..022192027 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -304,7 +304,7 @@ func send(c *cli.Context) (err error) { // generate code phrase crocOptions.SharedSecret = utils.GetRandomName() } - minimalFileInfos, emptyFoldersToTransfer, totalNumberFolders, err := croc.GetFilesInfo(fnames, crocOptions.ZipFolder) + minimalFileInfos, emptyFoldersToTransfer, totalNumberFolders, err := croc.GetFilesInfo(fnames, crocOptions.ZipFolder, crocOptions.GitIgnore) if err != nil { return } From cb5da0c7c26c4a9a0682ddce47772328ddbc6721 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sat, 11 Nov 2023 23:31:06 +0200 Subject: [PATCH 19/29] allow both sender and receiver to reserve a room --- src/croc/croc.go | 11 ++- src/tcp/tcp.go | 178 ++++++++++++++++++++++---------------------- src/tcp/tcp_test.go | 61 ++++++++------- 3 files changed, 128 insertions(+), 122 deletions(-) diff --git a/src/croc/croc.go b/src/croc/croc.go index af8bb2da3..8c130e709 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -614,7 +614,12 @@ func (c *Client) transferOverLocalRelay(errchan chan error) { log.Debugf("[%+v] had error: %s", conn, errConn.Error()) break } - if bytes.Equal(data, handshakeRequest) { + if bytes.Equal(data, ipRequest) { + log.Debug("Got ip request, sending nil since we are local") + if err = conn.Send(nil); err != nil { + log.Errorf("error sending: %v", err) + } + } else if bytes.Equal(data, handshakeRequest) { wgTransfer.Add(1) go c.makeLocalTransfer(conn, ipaddr, banner, errchan) wgTransfer.Wait() @@ -989,7 +994,7 @@ func (c *Client) Receive() (err error) { } serverTry := net.JoinHostPort(ip, port) - conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, 1, 500*time.Millisecond) + conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, false, 1, 500*time.Millisecond) if errConn != nil { log.Debug(errConn) log.Debugf("could not connect to " + serverTry) @@ -1009,6 +1014,7 @@ func (c *Client) Receive() (err error) { } } + log.Debug("sending handshake message") if err = c.conn[0].Send(handshakeRequest); err != nil { log.Errorf("handshake send error: %v", err) } @@ -1037,6 +1043,7 @@ func (c *Client) transfer() (err error) { // if recipient, initialize with sending pake information log.Debug("ready") if !c.Options.IsSender && !c.Step1ChannelSecured { + log.Debug("sending pake information") err = message.Send(c.conn[0], c.Key, message.Message{ Type: message.TypePAKE, Bytes: c.Pake.Bytes(), diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index f0eeb00b5..ad3d5cca4 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -35,7 +35,6 @@ type roomInfo struct { maxTransfers int doneTransfers int opened time.Time - transfering bool } type roomMap struct { @@ -293,39 +292,36 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er } room = string(roomBytes) - s.rooms.Lock() - if isSender { - if _, ok := s.rooms.rooms[room]; !ok { - // create the room if it is new - err = s.createRoom(c, room, strongKeyForEncryption) - if err != nil { - log.Error(err) - } - // sender is done - return - } else { - // if the room already exists, then tell the client that the room is full - err = s.sendRoomIsFull(c, strongKeyForEncryption) - return - } + log.Debug("Check if this is a main room") + enc, err = c.Receive() + if err != nil { + return + } + data, err = crypt.Decrypt(enc, strongKeyForEncryption) + if err != nil { + return + } + if !bytes.Equal(data, []byte("main")) && !bytes.Equal(data, []byte("secondary")) { + err = fmt.Errorf("got bad response: %s", data) + return } + isMainRoom := bytes.Equal(data, []byte("main")) + log.Debugf("isMainRoom: %v", isMainRoom) - if _, ok := s.rooms.rooms[room]; !ok { - // if the room does not exist and the client is a receiver, then tell them - // that the room does not exist - s.rooms.Unlock() - bSend, err = crypt.Encrypt([]byte(noRoom), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) + s.rooms.Lock() + _, roomExists := s.rooms.rooms[room] + // create the room if it is new + if !roomExists || isSender { + err = s.createOrUpdateRoom(c, room, strongKeyForEncryption, isMainRoom, isSender, roomExists) if err != nil { log.Error(err) - return } - return "", fmt.Errorf("reciever tried to connect to room that does not exist") - } else if s.rooms.rooms[room].transfering { + // if the room is new then return + if !roomExists { + return + } + } else if s.rooms.rooms[room].receiver != nil { // if the room has a transfer going on if s.rooms.rooms[room].maxTransfers > 1 { // if the room is a multi-transfer room then add to queue @@ -349,7 +345,6 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er maxTransfers: s.rooms.rooms[room].maxTransfers, doneTransfers: s.rooms.rooms[room].doneTransfers, opened: s.rooms.rooms[room].opened, - transfering: true, } s.rooms.roomLocks[room].Lock() } @@ -376,26 +371,11 @@ func (s *server) sendRoomIsFull(c *comm.Comm, strongKeyForEncryption []byte) (er return } -func (s *server) createRoom(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error) { +func (s *server) createOrUpdateRoom(c *comm.Comm, room string, strongKeyForEncryption []byte, isMainRoom, isSender, updateRoom bool) (err error) { var enc, data, bSend []byte - log.Debug("Check if this is a main room") - enc, err = c.Receive() - if err != nil { - return - } - data, err = crypt.Decrypt(enc, strongKeyForEncryption) - if err != nil { - return - } - if !bytes.Equal(data, []byte("main")) && !bytes.Equal(data, []byte("secondary")) { - err = fmt.Errorf("got bad response: %s", data) - return - } - isMainRoom := bytes.Equal(data, []byte("main")) - log.Debugf("isMainRoom: %v", isMainRoom) maxTransfers := 1 - if isMainRoom { + if isMainRoom && isSender { log.Debug("Wait for maxTransfers") enc, err = c.Receive() if err != nil { @@ -413,29 +393,53 @@ func (s *server) createRoom(c *comm.Comm, room string, strongKeyForEncryption [] log.Debugf("maxTransfers: %v", maxTransfers) } + var sender, receiver *comm.Comm + var queue *list.List + opened := time.Now() + if isSender { + sender = c + if updateRoom { + receiver = s.rooms.rooms[room].receiver + queue = s.rooms.rooms[room].queue + opened = s.rooms.rooms[room].opened + } + } else { + receiver = c + if updateRoom { + sender = s.rooms.rooms[room].sender + queue = s.rooms.rooms[room].queue + opened = s.rooms.rooms[room].opened + } + } + s.rooms.rooms[room] = roomInfo{ - sender: c, - receiver: nil, + sender: sender, + receiver: receiver, + queue: queue, isMainRoom: isMainRoom, maxTransfers: maxTransfers, doneTransfers: 0, - opened: time.Now(), + opened: opened, } - s.rooms.roomLocks[room] = &sync.Mutex{} - s.rooms.Unlock() - // tell the client that they got the room - bSend, err = crypt.Encrypt([]byte("ok"), strongKeyForEncryption) - if err != nil { - return - } - err = c.Send(bSend) - if err != nil { - log.Error(err) - s.deleteRoom(room) - return + if !updateRoom { + log.Debugf("Client crated main room %s, %v", room, isSender) + s.rooms.roomLocks[room] = &sync.Mutex{} + // tell the client that they got the room + bSend, err = crypt.Encrypt([]byte("ok"), strongKeyForEncryption) + if err != nil { + return + } + err = c.Send(bSend) + if err != nil { + log.Error(err) + s.deleteRoom(room) + return + } + log.Debugf("room %s has 1", room) + s.rooms.Unlock() } - log.Debugf("room %s has 1", room) + return } @@ -455,7 +459,6 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong maxTransfers: s.rooms.rooms[room].maxTransfers, doneTransfers: s.rooms.rooms[room].doneTransfers, queue: queue, - transfering: true, } s.rooms.Unlock() @@ -500,7 +503,6 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong maxTransfers: s.rooms.rooms[room].maxTransfers, doneTransfers: s.rooms.rooms[room].doneTransfers, opened: s.rooms.rooms[room].opened, - transfering: true, } break } @@ -509,7 +511,6 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong } func (s *server) beginTransfer(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error) { - otherConnection := s.rooms.rooms[room].sender s.rooms.Unlock() // second connection is the sender, time to staple connections @@ -522,9 +523,10 @@ func (s *server) beginTransfer(c *comm.Comm, room string, strongKeyForEncryption pipe(com1.Connection(), com2.Connection()) wg.Done() log.Debug("done piping") - }(otherConnection, c, &wg) + }(s.rooms.rooms[room].sender, s.rooms.rooms[room].receiver, &wg) - // tell the receiver everything is ready + // tell the client everything is ready + log.Debug("sending ok to client") bSend, err := crypt.Encrypt([]byte("ok"), strongKeyForEncryption) if err != nil { return @@ -544,6 +546,7 @@ func (s *server) beginTransfer(c *comm.Comm, room string, strongKeyForEncryption if s.rooms.rooms[room].queue != nil { lengthOfQueue = s.rooms.rooms[room].queue.Len() } + log.Debugf("room %s has %d left in queue", room, lengthOfQueue) s.rooms.rooms[room] = roomInfo{ sender: s.rooms.rooms[room].sender, receiver: nil, @@ -552,7 +555,6 @@ func (s *server) beginTransfer(c *comm.Comm, room string, strongKeyForEncryption maxTransfers: s.rooms.rooms[room].maxTransfers, doneTransfers: newDoneTransfers, opened: s.rooms.rooms[room].opened, - transfering: lengthOfQueue > 0 && newDoneTransfers < s.rooms.rooms[room].maxTransfers, } s.rooms.Unlock() @@ -799,13 +801,25 @@ func ConnectToTCPServer(address, password, room string, isSender, isMainRoom boo return } - if isSender { - log.Debug("tell server if this is a main room") - roomType := "secondary" - if isMainRoom { - roomType = "main" - } - bSend, err = crypt.Encrypt([]byte(roomType), strongKeyForEncryption) + log.Debug("tell server if this is a main room") + roomType := "secondary" + if isMainRoom { + roomType = "main" + } + bSend, err = crypt.Encrypt([]byte(roomType), strongKeyForEncryption) + if err != nil { + log.Debug(err) + return + } + err = c.Send(bSend) + if err != nil { + log.Debug(err) + return + } + + if isMainRoom && isSender { + log.Debug("tell server maxTransfers") + bSend, err = crypt.Encrypt([]byte(strconv.Itoa(maxTransfers)), strongKeyForEncryption) if err != nil { log.Debug(err) return @@ -815,20 +829,6 @@ func ConnectToTCPServer(address, password, room string, isSender, isMainRoom boo log.Debug(err) return } - - if isMainRoom { - log.Debug("tell server maxTransfers") - bSend, err = crypt.Encrypt([]byte(strconv.Itoa(maxTransfers)), strongKeyForEncryption) - if err != nil { - log.Debug(err) - return - } - err = c.Send(bSend) - if err != nil { - log.Debug(err) - return - } - } } log.Debug("waiting for room confirmation") diff --git a/src/tcp/tcp_test.go b/src/tcp/tcp_test.go index 6fd48133f..456216fbf 100644 --- a/src/tcp/tcp_test.go +++ b/src/tcp/tcp_test.go @@ -33,18 +33,6 @@ func TestTCPServerPing(t *testing.T) { assert.NotNil(t, err) } -// Test that a reeciver cannot connect to a non-existent room -func TestTCPServerNonExistentRoom(t *testing.T) { - log.SetLevel("error") - go Run("debug", "127.0.0.1", "8381", "pass123", "8382") - time.Sleep(100 * time.Millisecond) - - c1, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1) - assert.NotNil(t, err) - assert.True(t, strings.Contains(err.Error(), "room does not exist")) - assert.Nil(t, c1) -} - // This is helper function to test that a mocks a transfer // between two clients connected to the server, // and checks that the data is transferred correctly @@ -97,6 +85,28 @@ func TestTCPServerSingleConnectionTransfer(t *testing.T) { time.Sleep(300 * time.Millisecond) } +// Test that a receiver can connect before a sender +func TestTCPRecieverFirst(t *testing.T) { + log.SetLevel("error") + go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + time.Sleep(100 * time.Millisecond) + + receiver, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 1*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, receiver) + assert.Equal(t, banner, "8382") + + sender, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 1*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, sender) + + mockTransfer(receiver, sender, t) + + receiver.Close() + sender.Close() + time.Sleep(300 * time.Millisecond) +} + // Test that a third client cannot connect // to a room that already has two clients // connected to it with maxTransfers=1 @@ -237,26 +247,15 @@ func TestTCPMultipleConnectionWaitingRoomCloses(t *testing.T) { assert.Nil(t, err) assert.NotNil(t, c2) - // we need to run this transfer in a goroutine because - // otherwise connections will be idle and the server will - // close them when we try to connect a third client go func() { - counter := 1 - time.Sleep(100 * time.Millisecond) - for { - mockTransfer(c1, c2, t) - if counter == 5 { - c2.Close() - // tell c1 to close pipe listener - c1.Send([]byte("finished")) - break - } - counter++ - } + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 5*time.Minute) + assert.NotNil(t, err) + assert.True(t, strings.Contains(err.Error(), "sender is gone")) + assert.Nil(t, c3) }() - c3, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 5*time.Minute) - assert.NotNil(t, err) - assert.True(t, strings.Contains(err.Error(), "sender is gone")) - assert.Nil(t, c3) + time.Sleep(100 * time.Millisecond) + c2.Close() + // tell c1 to close pipe listener + c1.Send([]byte("finished")) } From 990d3fb15377defba4c881782ec45f43f0499122 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sat, 11 Nov 2023 23:37:16 +0200 Subject: [PATCH 20/29] fix croc ignore git unit test --- .gitignore | 2 ++ src/croc/croc_test.go | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3a1d586da..37e4c9ba9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ bash_autocomplete dist bin croc-stdin* +/transfer_folder +/test .idea/ .vscode/ diff --git a/src/croc/croc_test.go b/src/croc/croc_test.go index 445ec5afe..ca17b4ce8 100644 --- a/src/croc/croc_test.go +++ b/src/croc/croc_test.go @@ -232,7 +232,8 @@ func TestCrocSymlink(t *testing.T) { t.Errorf("symlink transfer failed: %s", err.Error()) } } -func testCrocIgnoreGit(t *testing.T) { + +func TestCrocIgnoreGit(t *testing.T) { log.SetLevel("trace") defer os.Remove(".gitignore") time.Sleep(300 * time.Millisecond) @@ -380,6 +381,14 @@ func TestCleanUp(t *testing.T) { log.Debug("Full cleanup") var err error + for _, file := range []string{".gitignore", ".gitignore"} { + err = os.Remove(file) + if err == nil { + log.Debugf("Successfully purged %s", file) + } else { + log.Debugf("%s was already purged.", file) + } + } for _, file := range []string{"README.md", "./README.md"} { err = os.Remove(file) if err == nil { From 0dd51426c10980dd660f351c0e9faa04d9225f8e Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sat, 11 Nov 2023 23:45:10 +0200 Subject: [PATCH 21/29] clean up --- .gitignore | 2 -- README.md | 2 +- src/cli/cli.go | 3 +++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 37e4c9ba9..3a1d586da 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,6 @@ bash_autocomplete dist bin croc-stdin* -/transfer_folder -/test .idea/ .vscode/ diff --git a/README.md b/README.md index 13bb0eb6f..8bd9ef65b 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ The relay is needed to staple the parallel incoming and outgoing connections. By croc relay ``` -By default it uses TCP ports 9009-9013. Make sure to open those up. You can customized the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer. +By default it uses TCP ports 9009-9013. Make sure to open those up. You can customise the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer. You can send files using your relay by entering `--relay` to change the relay that you are using if you want to custom host your own. diff --git a/src/cli/cli.go b/src/cli/cli.go index d40f8a3ac..99e2371d4 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -266,6 +266,9 @@ func send(c *cli.Context) (err error) { if !c.IsSet("hash") { crocOptions.HashAlgorithm = rememberedOptions.HashAlgorithm } + if !c.IsSet("git") { + crocOptions.GitIgnore = rememberedOptions.GitIgnore + } } var fnames []string From 5a541d7277d5f45ccfff2baae9ae3de29ee79c62 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sat, 11 Nov 2023 23:47:24 +0200 Subject: [PATCH 22/29] more clean up --- README.md | 2 +- src/cli/cli.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bd9ef65b..3bfebba16 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ The relay is needed to staple the parallel incoming and outgoing connections. By croc relay ``` -By default it uses TCP ports 9009-9013. Make sure to open those up. You can customise the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer. +By default it uses TCP ports 9009-9013. Make sure to open those up. You can customize the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer. You can send files using your relay by entering `--relay` to change the relay that you are using if you want to custom host your own. diff --git a/src/cli/cli.go b/src/cli/cli.go index 99e2371d4..878b1d1ca 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -72,6 +72,7 @@ func Run() (err error) { &cli.StringFlag{Name: "text", Aliases: []string{"t"}, Usage: "send some text"}, &cli.BoolFlag{Name: "no-local", Usage: "disable local relay when sending"}, &cli.BoolFlag{Name: "no-multi", Usage: "disable multiplexing"}, + &cli.BoolFlag{Name: "git", Usage: "enable .gitignore respect / don't send ignored files"}, &cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"}, }, HelpName: "croc send", @@ -202,6 +203,7 @@ func send(c *cli.Context) (err error) { HashAlgorithm: c.String("hash"), ThrottleUpload: c.String("throttleUpload"), ZipFolder: c.Bool("zip"), + GitIgnore: c.Bool("git"), } if crocOptions.TimeLimit <= 0 { From 345f46a78c39dda293478ddbad745ca0b8e83ae7 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sat, 11 Nov 2023 23:47:24 +0200 Subject: [PATCH 23/29] more clean up --- README.md | 2 +- src/cli/cli.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bd9ef65b..3bfebba16 100644 --- a/README.md +++ b/README.md @@ -241,7 +241,7 @@ The relay is needed to staple the parallel incoming and outgoing connections. By croc relay ``` -By default it uses TCP ports 9009-9013. Make sure to open those up. You can customise the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer. +By default it uses TCP ports 9009-9013. Make sure to open those up. You can customize the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer. You can send files using your relay by entering `--relay` to change the relay that you are using if you want to custom host your own. diff --git a/src/cli/cli.go b/src/cli/cli.go index 99e2371d4..eeff0f521 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -44,6 +44,7 @@ func Run() (err error) { app.UsageText = `Send a file: croc send file.txt + -git to respect your .gitignore Send multiple files: croc send file1.txt file2.txt file3.txt or @@ -72,6 +73,7 @@ func Run() (err error) { &cli.StringFlag{Name: "text", Aliases: []string{"t"}, Usage: "send some text"}, &cli.BoolFlag{Name: "no-local", Usage: "disable local relay when sending"}, &cli.BoolFlag{Name: "no-multi", Usage: "disable multiplexing"}, + &cli.BoolFlag{Name: "git", Usage: "enable .gitignore respect / don't send ignored files"}, &cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"}, }, HelpName: "croc send", @@ -202,6 +204,7 @@ func send(c *cli.Context) (err error) { HashAlgorithm: c.String("hash"), ThrottleUpload: c.String("throttleUpload"), ZipFolder: c.Bool("zip"), + GitIgnore: c.Bool("git"), } if crocOptions.TimeLimit <= 0 { From e5ba8c5580ba025139a3769dd37bb35fde9bd0cd Mon Sep 17 00:00:00 2001 From: RCL98 Date: Sun, 12 Nov 2023 01:15:01 +0200 Subject: [PATCH 24/29] fix remove receivers from queue --- src/cli/cli.go | 2 +- src/tcp/tcp.go | 40 ++++++++++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/cli/cli.go b/src/cli/cli.go index eeff0f521..4853e3d92 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -44,7 +44,7 @@ func Run() (err error) { app.UsageText = `Send a file: croc send file.txt - -git to respect your .gitignore + -git to respect your .gitignore Send multiple files: croc send file1.txt file2.txt file3.txt or diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index ad3d5cca4..e435dabc0 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -325,9 +325,13 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er // if the room has a transfer going on if s.rooms.rooms[room].maxTransfers > 1 { // if the room is a multi-transfer room then add to queue - err = s.handleWaitingRoomForReceivers(c, room, strongKeyForEncryption) + var keepGoing bool + err, keepGoing = s.handleWaitingRoomForReceivers(c, room, strongKeyForEncryption) if err != nil { log.Error(err) + } + if !keepGoing { + s.rooms.roomLocks[room].Unlock() return } } else { @@ -443,7 +447,14 @@ func (s *server) createOrUpdateRoom(c *comm.Comm, room string, strongKeyForEncry return } -func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error) { +func removeReceiverFromQueue(queue *list.List, c *comm.Comm) { + var rmElem *list.Element + for rmElem = queue.Front(); rmElem.Value != c.ID(); rmElem = rmElem.Next() { + } + queue.Remove(rmElem) +} + +func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error, keepGoing bool) { var bSend []byte log.Debugf("room %s is full, adding to queue", room) queue := s.rooms.rooms[room].queue @@ -462,10 +473,26 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong } s.rooms.Unlock() + keepGoing = false for { s.rooms.roomLocks[room].Lock() if s.rooms.rooms[room].doneTransfers >= s.rooms.rooms[room].maxTransfers { + // remove the client from the queue + newQueue := s.rooms.rooms[room].queue + removeReceiverFromQueue(newQueue, c) + s.rooms.Lock() + s.rooms.rooms[room] = roomInfo{ + sender: s.rooms.rooms[room].sender, + receiver: s.rooms.rooms[room].receiver, + isMainRoom: s.rooms.rooms[room].isMainRoom, + opened: s.rooms.rooms[room].opened, + maxTransfers: s.rooms.rooms[room].maxTransfers, + doneTransfers: s.rooms.rooms[room].doneTransfers, + queue: newQueue, + } + s.rooms.Unlock() + // tell the client that the sender is no longer available bSend, err = crypt.Encrypt([]byte(senderGone), strongKeyForEncryption) if err != nil { @@ -476,7 +503,6 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong log.Error(err) return } - s.rooms.roomLocks[room].Unlock() break } else if s.rooms.rooms[room].receiver != nil || s.rooms.rooms[room].queue.Front().Value.(string) != c.ID() { time.Sleep(1 * time.Second) @@ -493,8 +519,9 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong s.rooms.roomLocks[room].Unlock() } else { s.rooms.Lock() + // remove the client from the queue newQueue := s.rooms.rooms[room].queue - newQueue.Remove(newQueue.Front()) + removeReceiverFromQueue(newQueue, c) s.rooms.rooms[room] = roomInfo{ sender: s.rooms.rooms[room].sender, receiver: c, @@ -504,6 +531,7 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong doneTransfers: s.rooms.rooms[room].doneTransfers, opened: s.rooms.rooms[room].opened, } + keepGoing = true break } } @@ -570,8 +598,6 @@ func (s *server) beginTransfer(c *comm.Comm, room string, strongKeyForEncryption } func (s *server) deleteRoom(room string) { - s.rooms.Lock() - defer s.rooms.Unlock() if _, ok := s.rooms.rooms[room]; !ok { return } @@ -586,6 +612,8 @@ func (s *server) deleteRoom(room string) { } delete(s.rooms.roomLocks, room) } + s.rooms.Lock() + defer s.rooms.Unlock() log.Debugf("deleting room: %s", room) if s.rooms.rooms[room].sender != nil { s.rooms.rooms[room].sender.Close() From ee927bdc552889b2367491a1c9e9cc7b0450239c Mon Sep 17 00:00:00 2001 From: RCL98 Date: Mon, 13 Nov 2023 23:10:29 +0200 Subject: [PATCH 25/29] allow only one sender to connect --- src/croc/croc.go | 5 ++- src/croc/croc_test.go | 6 +-- src/tcp/tcp.go | 96 ++++++++++++++++++++++++------------------- src/tcp/tcp_test.go | 16 ++++++++ 4 files changed, 76 insertions(+), 47 deletions(-) diff --git a/src/croc/croc.go b/src/croc/croc.go index 8c130e709..9beff521d 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -606,7 +606,7 @@ func (c *Client) transferOverLocalRelay(errchan chan error) { // not really an error because it will try to connect over the actual relay return } - log.Debugf("local connection established: %+v", conn) + log.Debugf("local sender connection established: %+v", conn) err = nil for { data, errConn := conn.Receive() @@ -800,7 +800,7 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t errchan <- err } else { log.Debugf("banner: %s", banner) - log.Debugf("connection established: %+v", conn) + log.Debugf("sender connection established: %+v", conn) c.listenToMainConn(conn, ipaddr, banner, errchan) } @@ -939,6 +939,7 @@ func (c *Client) Receive() (err error) { c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], c.Options.IsSender, true, 1, time.Duration(c.Options.TimeLimit)*time.Second) if err == nil { c.Options.RelayAddress = address + log.Debug("receiver connection established") break } log.Debugf("could not establish '%s'", address) diff --git a/src/croc/croc_test.go b/src/croc/croc_test.go index 99abf4bfd..71b80d1c8 100644 --- a/src/croc/croc_test.go +++ b/src/croc/croc_test.go @@ -277,7 +277,7 @@ func TestCrocLocal(t *testing.T) { IsSender: true, TimeLimit: 30, MaxTransfers: 1, - SharedSecret: "8123-testingthecroc", + SharedSecret: "2813-testingthecroc", Debug: true, RelayAddress: "127.0.0.1:8181", RelayPorts: []string{"8181", "8182"}, @@ -297,7 +297,7 @@ func TestCrocLocal(t *testing.T) { log.Debug("setting up receiver") receiver, err := New(Options{ IsSender: false, - SharedSecret: "8123-testingthecroc", + SharedSecret: "2813-testingthecroc", Debug: true, RelayAddress: "127.0.0.1:8181", RelayPassword: "pass123", @@ -325,7 +325,7 @@ func TestCrocLocal(t *testing.T) { } wg.Done() }() - time.Sleep(100 * time.Millisecond) + time.Sleep(300 * time.Millisecond) go func() { err := receiver.Receive() if err != nil { diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index e435dabc0..a5d6cf404 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -156,8 +156,8 @@ func (s *server) run() (err error) { log.Debugf("checking connection of room %s for %+v", room, c) deleteIt := false s.rooms.Lock() - if _, ok := s.rooms.rooms[room]; !ok { - log.Debug("room is gone") + if _, ok := s.rooms.rooms[room]; !ok || (s.rooms.rooms[room].sender == nil && s.rooms.rooms[room].receiver == nil) { + log.Debugf("room %s is gone", room) s.rooms.Unlock() return } @@ -312,6 +312,12 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er _, roomExists := s.rooms.rooms[room] // create the room if it is new if !roomExists || isSender { + if roomExists && isSender && s.rooms.rooms[room].sender != nil { + // if the room exists and the sender is already connected + // then signal to the client that the room is full + err = s.sendRoomIsFull(c, strongKeyForEncryption) + return + } err = s.createOrUpdateRoom(c, room, strongKeyForEncryption, isMainRoom, isSender, roomExists) if err != nil { log.Error(err) @@ -378,6 +384,12 @@ func (s *server) sendRoomIsFull(c *comm.Comm, strongKeyForEncryption []byte) (er func (s *server) createOrUpdateRoom(c *comm.Comm, room string, strongKeyForEncryption []byte, isMainRoom, isSender, updateRoom bool) (err error) { var enc, data, bSend []byte + if !updateRoom { + log.Debugf("Creating room %s", room) + } else { + log.Debugf("Updating room %s", room) + } + maxTransfers := 1 if isMainRoom && isSender { log.Debug("Wait for maxTransfers") @@ -447,13 +459,6 @@ func (s *server) createOrUpdateRoom(c *comm.Comm, room string, strongKeyForEncry return } -func removeReceiverFromQueue(queue *list.List, c *comm.Comm) { - var rmElem *list.Element - for rmElem = queue.Front(); rmElem.Value != c.ID(); rmElem = rmElem.Next() { - } - queue.Remove(rmElem) -} - func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error, keepGoing bool) { var bSend []byte log.Debugf("room %s is full, adding to queue", room) @@ -477,24 +482,10 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong for { s.rooms.roomLocks[room].Lock() - if s.rooms.rooms[room].doneTransfers >= s.rooms.rooms[room].maxTransfers { - // remove the client from the queue - newQueue := s.rooms.rooms[room].queue - removeReceiverFromQueue(newQueue, c) - s.rooms.Lock() - s.rooms.rooms[room] = roomInfo{ - sender: s.rooms.rooms[room].sender, - receiver: s.rooms.rooms[room].receiver, - isMainRoom: s.rooms.rooms[room].isMainRoom, - opened: s.rooms.rooms[room].opened, - maxTransfers: s.rooms.rooms[room].maxTransfers, - doneTransfers: s.rooms.rooms[room].doneTransfers, - queue: newQueue, - } - s.rooms.Unlock() - - // tell the client that the sender is no longer available - bSend, err = crypt.Encrypt([]byte(senderGone), strongKeyForEncryption) + if s.rooms.rooms[room].receiver != nil || s.rooms.rooms[room].queue.Front().Value.(string) != c.ID() { + time.Sleep(1 * time.Second) + // tell the client that they need to wait + bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) if err != nil { return } @@ -503,11 +494,10 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong log.Error(err) return } - break - } else if s.rooms.rooms[room].receiver != nil || s.rooms.rooms[room].queue.Front().Value.(string) != c.ID() { - time.Sleep(1 * time.Second) - // tell the client that they need to wait - bSend, err = crypt.Encrypt([]byte("wait"), strongKeyForEncryption) + s.rooms.roomLocks[room].Unlock() + } else if s.rooms.rooms[room].doneTransfers >= s.rooms.rooms[room].maxTransfers { + // tell the client that the sender is no longer available + bSend, err = crypt.Encrypt([]byte(senderGone), strongKeyForEncryption) if err != nil { return } @@ -516,12 +506,12 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong log.Error(err) return } - s.rooms.roomLocks[room].Unlock() + break } else { s.rooms.Lock() // remove the client from the queue newQueue := s.rooms.rooms[room].queue - removeReceiverFromQueue(newQueue, c) + newQueue.Remove(newQueue.Front()) s.rooms.rooms[room] = roomInfo{ sender: s.rooms.rooms[room].sender, receiver: c, @@ -541,6 +531,12 @@ func (s *server) handleWaitingRoomForReceivers(c *comm.Comm, room string, strong func (s *server) beginTransfer(c *comm.Comm, room string, strongKeyForEncryption []byte) (err error) { s.rooms.Unlock() + // safety check (it should never happen) + if s.rooms.rooms[room].sender == nil || s.rooms.rooms[room].receiver == nil { + err = fmt.Errorf("sender or receiver is nil") + return + } + // second connection is the sender, time to staple connections var wg sync.WaitGroup wg.Add(1) @@ -587,8 +583,8 @@ func (s *server) beginTransfer(c *comm.Comm, room string, strongKeyForEncryption s.rooms.Unlock() // delete the room if it is done or unlock it if it is not - if newDoneTransfers == s.rooms.rooms[room].maxTransfers { - log.Debugf("room %s is done", room) + if newDoneTransfers >= s.rooms.rooms[room].maxTransfers { + log.Debugf("room %s is done, deleting it", room) s.deleteRoom(room) } else { log.Debugf("room %s has %d done", room, newDoneTransfers) @@ -598,6 +594,8 @@ func (s *server) beginTransfer(c *comm.Comm, room string, strongKeyForEncryption } func (s *server) deleteRoom(room string) { + s.rooms.Lock() + defer s.rooms.Unlock() if _, ok := s.rooms.rooms[room]; !ok { return } @@ -609,11 +607,21 @@ func (s *server) deleteRoom(room string) { break } s.rooms.roomLocks[room].Lock() + // remove the client from the queue + newQueue := s.rooms.rooms[room].queue + newQueue.Remove(newQueue.Front()) + s.rooms.rooms[room] = roomInfo{ + sender: s.rooms.rooms[room].sender, + receiver: s.rooms.rooms[room].receiver, + isMainRoom: s.rooms.rooms[room].isMainRoom, + opened: s.rooms.rooms[room].opened, + maxTransfers: s.rooms.rooms[room].maxTransfers, + doneTransfers: s.rooms.rooms[room].doneTransfers, + queue: newQueue, + } } delete(s.rooms.roomLocks, room) } - s.rooms.Lock() - defer s.rooms.Unlock() log.Debugf("deleting room: %s", room) if s.rooms.rooms[room].sender != nil { s.rooms.rooms[room].sender.Close() @@ -871,6 +879,13 @@ func ConnectToTCPServer(address, password, room string, isSender, isMainRoom boo log.Debug(err) return } + + if bytes.Equal(data, []byte(fullRoom)) { + err = fmt.Errorf("room is full") + c = nil + return + } + if !isSender { if bytes.Equal(data, []byte("wait")) { log.Debug("waiting for sender to be free") @@ -880,16 +895,13 @@ func ConnectToTCPServer(address, password, room string, isSender, isMainRoom boo err = fmt.Errorf("sender is gone") c = nil return - } else if bytes.Equal(data, []byte(fullRoom)) { - err = fmt.Errorf("room is full") - c = nil - return } else if bytes.Equal(data, []byte(noRoom)) { err = fmt.Errorf("room does not exist") c = nil return } } + if !bytes.Equal(data, []byte("ok")) { err = fmt.Errorf("got bad response: %s", data) log.Debug(err) diff --git a/src/tcp/tcp_test.go b/src/tcp/tcp_test.go index 456216fbf..f11a32c00 100644 --- a/src/tcp/tcp_test.go +++ b/src/tcp/tcp_test.go @@ -33,6 +33,22 @@ func TestTCPServerPing(t *testing.T) { assert.NotNil(t, err) } +func TestOnlyOneSenderPerRoom(t *testing.T) { + log.SetLevel("error") + go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + time.Sleep(100 * time.Millisecond) + + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 1*time.Minute) + assert.Nil(t, err) + assert.NotNil(t, c1) + assert.Equal(t, banner, "8382") + + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 1*time.Minute) + assert.NotNil(t, err) + assert.True(t, strings.Contains(err.Error(), "room is full")) + assert.Nil(t, c2) +} + // This is helper function to test that a mocks a transfer // between two clients connected to the server, // and checks that the data is transferred correctly From 9a76162ac68afa1bc0dfd79b7354d8c4fb57429b Mon Sep 17 00:00:00 2001 From: RCL98 Date: Mon, 13 Nov 2023 23:34:02 +0200 Subject: [PATCH 26/29] fix unit test --- src/tcp/tcp_test.go | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/tcp/tcp_test.go b/src/tcp/tcp_test.go index f11a32c00..d7e3315ac 100644 --- a/src/tcp/tcp_test.go +++ b/src/tcp/tcp_test.go @@ -35,18 +35,21 @@ func TestTCPServerPing(t *testing.T) { func TestOnlyOneSenderPerRoom(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + go Run("debug", "127.0.0.1", "8281", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 1*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8281", "pass123", "testRoom", true, true, 1, 1*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 1*time.Minute) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8281", "pass123", "testRoom", true, true, 1, 1*time.Minute) assert.NotNil(t, err) assert.True(t, strings.Contains(err.Error(), "room is full")) assert.Nil(t, c2) + + c1.Close() + time.Sleep(300 * time.Millisecond) } // This is helper function to test that a mocks a transfer @@ -96,8 +99,8 @@ func TestTCPServerSingleConnectionTransfer(t *testing.T) { mockTransfer(c1, c2, t) - c1.Close() c2.Close() + c1.Close() time.Sleep(300 * time.Millisecond) } @@ -167,15 +170,15 @@ func TestTCPSingleConnectionOnly2Clients(t *testing.T) { // successive transfers from the same sender func TestTCPMultipleConnectionTransfer(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + go Run("debug", "127.0.0.1", "8383", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 2, 10*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8383", "pass123", "testRoom", true, true, 2, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 10*time.Minute) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8383", "pass123", "testRoom", false, true, 1, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c2) @@ -185,7 +188,7 @@ func TestTCPMultipleConnectionTransfer(t *testing.T) { c1.Send([]byte("finished")) time.Sleep(100 * time.Millisecond) - c3, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 5*time.Minute) + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8383", "pass123", "testRoom", false, true, 1, 5*time.Minute) assert.Nil(t, err) assert.NotNil(t, c3) @@ -198,15 +201,15 @@ func TestTCPMultipleConnectionTransfer(t *testing.T) { // to connect when the transfer is finished func TestTCPMultipleConnectionWaitingRoom(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + go Run("debug", "127.0.0.1", "8384", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 2, 10*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8384", "pass123", "testRoom", true, true, 2, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 10*time.Minute) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8384", "pass123", "testRoom", false, true, 1, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c2) @@ -228,7 +231,7 @@ func TestTCPMultipleConnectionWaitingRoom(t *testing.T) { } }() - c3, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 5*time.Minute) + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8384", "pass123", "testRoom", false, true, 1, 5*time.Minute) assert.Nil(t, err) assert.NotNil(t, c3) @@ -241,15 +244,15 @@ func TestTCPMultipleConnectionWaitingRoom(t *testing.T) { // when the sender the maxTransfers limit is reached func TestTCPMultipleConnectionWaitingRoomCloses(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + go Run("debug", "127.0.0.1", "8380", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 2, 10*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8380", "pass123", "testRoom", true, true, 2, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 10*time.Minute) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8380", "pass123", "testRoom", false, true, 1, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c2) @@ -259,12 +262,12 @@ func TestTCPMultipleConnectionWaitingRoomCloses(t *testing.T) { // tell c1 to close pipe listener c1.Send([]byte("finished")) - c2, _, _, err = ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 10*time.Minute) + c2, _, _, err = ConnectToTCPServer("127.0.0.1:8380", "pass123", "testRoom", false, true, 1, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c2) go func() { - c3, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 5*time.Minute) + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8380", "pass123", "testRoom", false, true, 1, 5*time.Minute) assert.NotNil(t, err) assert.True(t, strings.Contains(err.Error(), "sender is gone")) assert.Nil(t, c3) From 25ee1eb4795f7653a5335762d45dceecdb903c17 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Mon, 13 Nov 2023 23:45:00 +0200 Subject: [PATCH 27/29] small clean up --- src/tcp/tcp_test.go | 52 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/tcp/tcp_test.go b/src/tcp/tcp_test.go index d7e3315ac..605024477 100644 --- a/src/tcp/tcp_test.go +++ b/src/tcp/tcp_test.go @@ -35,15 +35,15 @@ func TestTCPServerPing(t *testing.T) { func TestOnlyOneSenderPerRoom(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8281", "pass123", "8382") + go Run("debug", "127.0.0.1", "8280", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8281", "pass123", "testRoom", true, true, 1, 1*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8280", "pass123", "testRoom", true, true, 1, 1*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8281", "pass123", "testRoom", true, true, 1, 1*time.Minute) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8280", "pass123", "testRoom", true, true, 1, 1*time.Minute) assert.NotNil(t, err) assert.True(t, strings.Contains(err.Error(), "room is full")) assert.Nil(t, c2) @@ -85,15 +85,15 @@ func mockTransfer(c1, c2 *comm.Comm, t *testing.T) { // Test that a successful transfer can be made func TestTCPServerSingleConnectionTransfer(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + go Run("debug", "127.0.0.1", "8281", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 1*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8281", "pass123", "testRoom", true, true, 1, 1*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8281", "pass123", "testRoom", false, true, 1) assert.Nil(t, err) assert.NotNil(t, c2) @@ -107,15 +107,15 @@ func TestTCPServerSingleConnectionTransfer(t *testing.T) { // Test that a receiver can connect before a sender func TestTCPRecieverFirst(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + go Run("debug", "127.0.0.1", "8383", "pass123", "8382") time.Sleep(100 * time.Millisecond) - receiver, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 1*time.Minute) + receiver, banner, _, err := ConnectToTCPServer("127.0.0.1:8383", "pass123", "testRoom", false, true, 1, 1*time.Minute) assert.Nil(t, err) assert.NotNil(t, receiver) assert.Equal(t, banner, "8382") - sender, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 1*time.Minute) + sender, _, _, err := ConnectToTCPServer("127.0.0.1:8383", "pass123", "testRoom", true, true, 1, 1*time.Minute) assert.Nil(t, err) assert.NotNil(t, sender) @@ -131,15 +131,15 @@ func TestTCPRecieverFirst(t *testing.T) { // connected to it with maxTransfers=1 func TestTCPSingleConnectionOnly2Clients(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8381", "pass123", "8382") + go Run("debug", "127.0.0.1", "8384", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 10*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8384", "pass123", "testRoom", true, true, 1, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 10*time.Minute) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8384", "pass123", "testRoom", false, true, 1, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c2) closeChan := make(chan int) @@ -159,7 +159,7 @@ func TestTCPSingleConnectionOnly2Clients(t *testing.T) { } }() - c3, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1, 5*time.Minute) + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8384", "pass123", "testRoom", false, true, 1, 5*time.Minute) assert.NotNil(t, err) assert.True(t, strings.Contains(err.Error(), "room is full")) assert.Nil(t, c3) @@ -170,15 +170,15 @@ func TestTCPSingleConnectionOnly2Clients(t *testing.T) { // successive transfers from the same sender func TestTCPMultipleConnectionTransfer(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8383", "pass123", "8382") + go Run("debug", "127.0.0.1", "8385", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8383", "pass123", "testRoom", true, true, 2, 10*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8385", "pass123", "testRoom", true, true, 2, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8383", "pass123", "testRoom", false, true, 1, 10*time.Minute) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8385", "pass123", "testRoom", false, true, 1, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c2) @@ -188,7 +188,7 @@ func TestTCPMultipleConnectionTransfer(t *testing.T) { c1.Send([]byte("finished")) time.Sleep(100 * time.Millisecond) - c3, _, _, err := ConnectToTCPServer("127.0.0.1:8383", "pass123", "testRoom", false, true, 1, 5*time.Minute) + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8385", "pass123", "testRoom", false, true, 1, 5*time.Minute) assert.Nil(t, err) assert.NotNil(t, c3) @@ -201,15 +201,15 @@ func TestTCPMultipleConnectionTransfer(t *testing.T) { // to connect when the transfer is finished func TestTCPMultipleConnectionWaitingRoom(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8384", "pass123", "8382") + go Run("debug", "127.0.0.1", "8386", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8384", "pass123", "testRoom", true, true, 2, 10*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8386", "pass123", "testRoom", true, true, 2, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8384", "pass123", "testRoom", false, true, 1, 10*time.Minute) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8386", "pass123", "testRoom", false, true, 1, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c2) @@ -231,7 +231,7 @@ func TestTCPMultipleConnectionWaitingRoom(t *testing.T) { } }() - c3, _, _, err := ConnectToTCPServer("127.0.0.1:8384", "pass123", "testRoom", false, true, 1, 5*time.Minute) + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8386", "pass123", "testRoom", false, true, 1, 5*time.Minute) assert.Nil(t, err) assert.NotNil(t, c3) @@ -244,15 +244,15 @@ func TestTCPMultipleConnectionWaitingRoom(t *testing.T) { // when the sender the maxTransfers limit is reached func TestTCPMultipleConnectionWaitingRoomCloses(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8380", "pass123", "8382") + go Run("debug", "127.0.0.1", "8387", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8380", "pass123", "testRoom", true, true, 2, 10*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8387", "pass123", "testRoom", true, true, 2, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8380", "pass123", "testRoom", false, true, 1, 10*time.Minute) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8387", "pass123", "testRoom", false, true, 1, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c2) @@ -262,12 +262,12 @@ func TestTCPMultipleConnectionWaitingRoomCloses(t *testing.T) { // tell c1 to close pipe listener c1.Send([]byte("finished")) - c2, _, _, err = ConnectToTCPServer("127.0.0.1:8380", "pass123", "testRoom", false, true, 1, 10*time.Minute) + c2, _, _, err = ConnectToTCPServer("127.0.0.1:8387", "pass123", "testRoom", false, true, 1, 10*time.Minute) assert.Nil(t, err) assert.NotNil(t, c2) go func() { - c3, _, _, err := ConnectToTCPServer("127.0.0.1:8380", "pass123", "testRoom", false, true, 1, 5*time.Minute) + c3, _, _, err := ConnectToTCPServer("127.0.0.1:8387", "pass123", "testRoom", false, true, 1, 5*time.Minute) assert.NotNil(t, err) assert.True(t, strings.Contains(err.Error(), "sender is gone")) assert.Nil(t, c3) From 0318794b56f05c8a7e3705c44c704ed15192607b Mon Sep 17 00:00:00 2001 From: RCL98 Date: Tue, 14 Nov 2023 00:35:24 +0200 Subject: [PATCH 28/29] give receiver time to lock mutex --- src/tcp/tcp.go | 7 ++++--- src/tcp/tcp_test.go | 21 ++++++++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/tcp/tcp.go b/src/tcp/tcp.go index a5d6cf404..a9f49498b 100644 --- a/src/tcp/tcp.go +++ b/src/tcp/tcp.go @@ -603,9 +603,7 @@ func (s *server) deleteRoom(room string) { // signal to all waiting that the room will be deleted for { s.rooms.roomLocks[room].Unlock() - if s.rooms.rooms[room].queue.Len() == 0 { - break - } + time.Sleep(250 * time.Millisecond) s.rooms.roomLocks[room].Lock() // remove the client from the queue newQueue := s.rooms.rooms[room].queue @@ -619,6 +617,9 @@ func (s *server) deleteRoom(room string) { doneTransfers: s.rooms.rooms[room].doneTransfers, queue: newQueue, } + if s.rooms.rooms[room].queue.Len() == 0 { + break + } } delete(s.rooms.roomLocks, room) } diff --git a/src/tcp/tcp_test.go b/src/tcp/tcp_test.go index 605024477..d3a26ad8c 100644 --- a/src/tcp/tcp_test.go +++ b/src/tcp/tcp_test.go @@ -85,15 +85,15 @@ func mockTransfer(c1, c2 *comm.Comm, t *testing.T) { // Test that a successful transfer can be made func TestTCPServerSingleConnectionTransfer(t *testing.T) { log.SetLevel("error") - go Run("debug", "127.0.0.1", "8281", "pass123", "8382") + go Run("debug", "127.0.0.1", "8381", "pass123", "8382") time.Sleep(100 * time.Millisecond) - c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8281", "pass123", "testRoom", true, true, 1, 1*time.Minute) + c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", true, true, 1, 1*time.Minute) assert.Nil(t, err) assert.NotNil(t, c1) assert.Equal(t, banner, "8382") - c2, _, _, err := ConnectToTCPServer("127.0.0.1:8281", "pass123", "testRoom", false, true, 1) + c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", false, true, 1) assert.Nil(t, err) assert.NotNil(t, c2) @@ -164,6 +164,10 @@ func TestTCPSingleConnectionOnly2Clients(t *testing.T) { assert.True(t, strings.Contains(err.Error(), "room is full")) assert.Nil(t, c3) closeChan <- 1 + + c1.Close() + c2.Close() + time.Sleep(300 * time.Millisecond) } // Test that the server can handle multiple @@ -193,6 +197,10 @@ func TestTCPMultipleConnectionTransfer(t *testing.T) { assert.NotNil(t, c3) mockTransfer(c1, c3, t) + + c1.Close() + c3.Close() + time.Sleep(300 * time.Millisecond) } // Test that for a room with maxTransfers>=2, @@ -236,6 +244,11 @@ func TestTCPMultipleConnectionWaitingRoom(t *testing.T) { assert.NotNil(t, c3) mockTransfer(c1, c3, t) + + c1.Close() + c2.Close() + c3.Close() + time.Sleep(300 * time.Millisecond) } // Test that for a room with maxTransfers>=2, @@ -277,4 +290,6 @@ func TestTCPMultipleConnectionWaitingRoomCloses(t *testing.T) { c2.Close() // tell c1 to close pipe listener c1.Send([]byte("finished")) + c1.Close() + time.Sleep(300 * time.Millisecond) } From ffe4d7e75685e543b3cf12de52ab0703fcf6a589 Mon Sep 17 00:00:00 2001 From: RCL98 Date: Wed, 27 Dec 2023 16:36:39 +0200 Subject: [PATCH 29/29] fix spelling mistakes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3bfebba16..9b4e352c8 100644 --- a/README.md +++ b/README.md @@ -140,13 +140,13 @@ There are a number of configurable options (see `--help`). A set of options (lik You can transfer files using only local connections. ``` -crorc --local send [file(s)-or-folder] +croc --local send [file(s)-or-folder] ``` ### Allow multiple sequential transfers -By default, after a transfer was done the program stops. -You can allow more than one transfer to happen (one after another) by using the `--multuple` flag which requires a value >= 1. +By default, after a transfer is done, the program stops. +You can allow more than one transfer to happen (one after another) by using the `--multiple` flag, which requires a value >= 1. ``` croc send --multiple [nr-of-transfers] [file(s)-or-folder]