diff --git a/connect.go b/connect.go index e825620..b01ff56 100644 --- a/connect.go +++ b/connect.go @@ -2,13 +2,14 @@ package main import ( "bytes" + "encoding/hex" + "encoding/json" "fmt" "io" "io/ioutil" "math" "net" "os" - "path" "strconv" "strings" "sync" @@ -18,26 +19,104 @@ import ( log "github.com/sirupsen/logrus" ) -var bars []*uiprogress.Bar +type Connection struct { + Server string + File FileMetaData + NumberOfConnections int + Code string + HashedCode string + IsSender bool + Debug bool + DontEncrypt bool + bars []*uiprogress.Bar +} + +type FileMetaData struct { + Name string + Size int + Hash string + IV string + Salt string + bytes []byte +} + +func NewConnection(flags *Flags) *Connection { + c := new(Connection) + c.Debug = flags.Debug + c.DontEncrypt = flags.DontEncrypt + c.Server = flags.Server + c.Code = flags.Code + c.NumberOfConnections = flags.NumberOfConnections + if len(flags.File) > 0 { + c.File.Name = flags.File + c.IsSender = true + } else { + c.IsSender = false + } + return c +} + +func (c *Connection) Run() { + if len(c.Code) == 0 { + if !c.IsSender { + c.Code = getInput("Enter receive code: ") + } + if len(c.Code) < 5 { + c.Code = GetRandomName() + } + } + + log.SetFormatter(&log.TextFormatter{}) + if c.Debug { + log.SetLevel(log.DebugLevel) + } else { + log.SetLevel(log.WarnLevel) + } + + if c.IsSender { + // encrypt the file + log.Debug("encrypting...") + fdata, err := ioutil.ReadFile(c.File.Name) + if err != nil { + log.Fatal(err) + return + } + c.File.bytes, c.File.Salt, c.File.IV = Encrypt(fdata, c.Code, c.DontEncrypt) + log.Debug("...finished encryption") + c.File.Hash = HashBytes(fdata) + c.File.Size = len(c.File.bytes) + if c.Debug { + ioutil.WriteFile(c.File.Name+".encrypted", c.File.bytes, 0644) + } + fmt.Printf("Sending %d byte file named '%s'\n", c.File.Size, c.File.Name) + fmt.Printf("Code is: %s\n", c.Code) + } + + c.runClient() +} // runClient spawns threads for parallel uplink/downlink via TCP -func runClient(connectionType string, codePhrase string) { +func (c *Connection) runClient() { logger := log.WithFields(log.Fields{ - "codePhrase": codePhrase, - "connection": connectionType, + "code": c.Code, + "sender?": c.IsSender, }) + + c.HashedCode = Hash(c.Code) + var wg sync.WaitGroup - wg.Add(numberConnections) + wg.Add(c.NumberOfConnections) uiprogress.Start() - if !debugFlag { - bars = make([]*uiprogress.Bar, numberConnections) + if !c.Debug { + c.bars = make([]*uiprogress.Bar, c.NumberOfConnections) } - for id := 0; id < numberConnections; id++ { + gotOK := false + for id := 0; id < c.NumberOfConnections; id++ { go func(id int) { defer wg.Done() port := strconv.Itoa(27001 + id) - connection, err := net.Dial("tcp", serverAddress+":"+port) + connection, err := net.Dial("tcp", c.Server+":"+port) if err != nil { panic(err) } @@ -45,12 +124,21 @@ func runClient(connectionType string, codePhrase string) { message := receiveMessage(connection) logger.Debugf("relay says: %s", message) - logger.Debugf("telling relay: %s", connectionType+"."+codePhrase) - - sendMessage(connectionType+"."+Hash(codePhrase), connection) - if connectionType == "s" { // this is a sender + if c.IsSender { + logger.Debugf("telling relay: %s", "s."+c.Code) + metaData, err := json.Marshal(c.File) + if err != nil { + log.Error(err) + } + encryptedMetaData, salt, iv := Encrypt(metaData, c.Code) + sendMessage("s."+c.HashedCode+"."+hex.EncodeToString(encryptedMetaData)+"-"+salt+"-"+iv, connection) + } else { + logger.Debugf("telling relay: %s", "r."+c.Code) + sendMessage("r."+c.HashedCode+".0.0.0", connection) + } + if c.IsSender { // this is a sender if id == 0 { - fmt.Println("waiting for other to connect") + fmt.Printf("\nSending (<-%s)..\n", connection.RemoteAddr().String()) } logger.Debug("waiting for ok from relay") message = receiveMessage(connection) @@ -59,62 +147,100 @@ func runClient(connectionType string, codePhrase string) { time.Sleep(100 * time.Millisecond) // Write data from file logger.Debug("send file") - sendFile(id, connection, codePhrase) + c.sendFile(id, connection) } else { // this is a receiver - // receive file + logger.Debug("waiting for meta data from sender") + message = receiveMessage(connection) + m := strings.Split(message, "-") + encryptedData, salt, iv := m[0], m[1], m[2] + encryptedBytes, err := hex.DecodeString(encryptedData) + if err != nil { + log.Error(err) + return + } + decryptedBytes, _ := Decrypt(encryptedBytes, c.Code, salt, iv, c.DontEncrypt) + err = json.Unmarshal(decryptedBytes, &c.File) + if err != nil { + log.Error(err) + return + } + log.Debugf("meta data received: %v", c.File) + // have the main thread ask for the okay + if id == 0 { + fmt.Printf("Receiving file (%d bytes) into: %s\n", c.File.Size, c.File.Name) + getOk := getInput("ok? (y/n): ") + if getOk == "y" { + gotOK = true + } else { + return + } + } + // wait for the main thread to get the okay + for limit := 0; limit < 1000; limit++ { + if gotOK { + break + } + time.Sleep(10 * time.Millisecond) + } + if !gotOK { + return + } + sendMessage("ok", connection) logger.Debug("receive file") - fileName, fileIV, fileSalt, fileHash = receiveFile(id, connection, codePhrase) + c.receiveFile(id, connection) } - }(id) } wg.Wait() - if connectionType == "r" { - catFile(fileName) - encrypted, err := ioutil.ReadFile(fileName + ".encrypted") + if !c.IsSender { + c.catFile(c.File.Name) + encrypted, err := ioutil.ReadFile(c.File.Name + ".encrypted") if err != nil { log.Error(err) return } fmt.Println("\n\ndecrypting...") - log.Debugf("codePhrase: [%s]", codePhrase) - log.Debugf("fileSalt: [%s]", fileSalt) - log.Debugf("fileIV: [%s]", fileIV) - decrypted, err := Decrypt(encrypted, codePhrase, fileSalt, fileIV) + log.Debugf("Code: [%s]", c.Code) + log.Debugf("Salt: [%s]", c.File.Salt) + log.Debugf("IV: [%s]", c.File.IV) + decrypted, err := Decrypt(encrypted, c.Code, c.File.Salt, c.File.IV, c.DontEncrypt) if err != nil { log.Error(err) return } - log.Debugf("writing %d bytes to %s", len(decrypted), fileName) - err = ioutil.WriteFile(fileName, decrypted, 0644) + log.Debugf("writing %d bytes to %s", len(decrypted), c.File.Name) + err = ioutil.WriteFile(c.File.Name, decrypted, 0644) if err != nil { log.Error(err) } - if !debugFlag { - os.Remove(fileName + ".encrypted") + if !c.Debug { + os.Remove(c.File.Name + ".encrypted") } log.Debugf("\n\n\ndownloaded hash: [%s]", HashBytes(decrypted)) - log.Debugf("\n\n\nrelayed hash: [%s]", fileHash) + log.Debugf("\n\n\nrelayed hash: [%s]", c.File.Hash) - if fileHash != HashBytes(decrypted) { - fmt.Printf("\nUh oh! %s is corrupted! Sorry, try again.\n", fileName) + if c.File.Hash != HashBytes(decrypted) { + fmt.Printf("\nUh oh! %s is corrupted! Sorry, try again.\n", c.File.Name) } else { - fmt.Printf("\nDownloaded %s!", fileName) + fmt.Printf("\nReceived file written to %s", c.File.Name) } + } else { + fmt.Println("File sent.") + // TODO: Add confirmation } } -func catFile(fileNameToReceive string) { +func (c *Connection) catFile(fname string) { // cat the file - os.Remove(fileNameToReceive) - finished, err := os.Create(fileNameToReceive + ".encrypted") + os.Remove(fname) + finished, err := os.Create(fname + ".encrypted") defer finished.Close() if err != nil { log.Fatal(err) } - for id := 0; id < numberConnections; id++ { - fh, err := os.Open(fileNameToReceive + "." + strconv.Itoa(id)) + for id := 0; id < c.NumberOfConnections; id++ { + fh, err := os.Open(fname + "." + strconv.Itoa(id)) if err != nil { log.Fatal(err) } @@ -124,74 +250,59 @@ func catFile(fileNameToReceive string) { log.Fatal(err) } fh.Close() - os.Remove(fileNameToReceive + "." + strconv.Itoa(id)) + os.Remove(fname + "." + strconv.Itoa(id)) } } -func receiveFile(id int, connection net.Conn, codePhrase string) (fileNameToReceive string, iv string, salt string, hashOfFile string) { +func (c *Connection) receiveFile(id int, connection net.Conn) error { logger := log.WithFields(log.Fields{ "function": "receiveFile #" + strconv.Itoa(id), }) - logger.Debug("waiting for file data") + logger.Debug("waiting for chunk size from sender") + fileSizeBuffer := make([]byte, 10) + connection.Read(fileSizeBuffer) + fileDataString := strings.Trim(string(fileSizeBuffer), ":") + fileSizeInt, _ := strconv.Atoi(fileDataString) + chunkSize := int64(fileSizeInt) + logger.Debugf("chunk size: %d", chunkSize) - fileDataBuffer := make([]byte, BUFFERSIZE) - connection.Read(fileDataBuffer) - fileDataString := strings.Trim(string(fileDataBuffer), ":") - pieces := strings.Split(fileDataString, "-") - fileSizeInt, _ := strconv.Atoi(pieces[0]) - fileSize := int64(fileSizeInt) - logger.Debugf("filesize: %d", fileSize) - - fileNameToReceive = pieces[1] - logger.Debugf("fileName: [%s]", fileNameToReceive) - - iv = pieces[2] - logger.Debugf("iv: [%s]", iv) - - salt = pieces[3] - logger.Debugf("salt: [%s]", salt) - - hashOfFile = pieces[4] - logger.Debugf("hashOfFile: [%s]", hashOfFile) - - os.Remove(fileNameToReceive + "." + strconv.Itoa(id)) - newFile, err := os.Create(fileNameToReceive + "." + strconv.Itoa(id)) + os.Remove(c.File.Name + "." + strconv.Itoa(id)) + newFile, err := os.Create(c.File.Name + "." + strconv.Itoa(id)) if err != nil { panic(err) } defer newFile.Close() - if !debugFlag { - bars[id] = uiprogress.AddBar(int(fileSize)/1024 + 1).AppendCompleted().PrependElapsed() + if !c.Debug { + c.bars[id] = uiprogress.AddBar(int(chunkSize)/1024 + 1).AppendCompleted().PrependElapsed() } logger.Debug("waiting for file") var receivedBytes int64 for { - if !debugFlag { - bars[id].Incr() + if !c.Debug { + c.bars[id].Incr() } - if (fileSize - receivedBytes) < BUFFERSIZE { + if (chunkSize - receivedBytes) < BUFFERSIZE { logger.Debug("at the end") - io.CopyN(newFile, connection, (fileSize - receivedBytes)) + io.CopyN(newFile, connection, (chunkSize - receivedBytes)) // Empty the remaining bytes that we don't need from the network buffer - if (receivedBytes+BUFFERSIZE)-fileSize < BUFFERSIZE { + if (receivedBytes+BUFFERSIZE)-chunkSize < BUFFERSIZE { logger.Debug("empty remaining bytes from network buffer") - connection.Read(make([]byte, (receivedBytes+BUFFERSIZE)-fileSize)) + connection.Read(make([]byte, (receivedBytes+BUFFERSIZE)-chunkSize)) } break } io.CopyN(newFile, connection, BUFFERSIZE) - //Increment the counter receivedBytes += BUFFERSIZE } logger.Debug("received file") - return + return nil } -func sendFile(id int, connection net.Conn, codePhrase string) { +func (c *Connection) sendFile(id int, connection net.Conn) { logger := log.WithFields(log.Fields{ "function": "sendFile #" + strconv.Itoa(id), }) @@ -199,41 +310,29 @@ func sendFile(id int, connection net.Conn, codePhrase string) { var err error - numChunks := math.Ceil(float64(len(fileBytes)) / float64(BUFFERSIZE)) - chunksPerWorker := int(math.Ceil(numChunks / float64(numberConnections))) + numChunks := math.Ceil(float64(c.File.Size) / float64(BUFFERSIZE)) + chunksPerWorker := int(math.Ceil(numChunks / float64(c.NumberOfConnections))) - bytesPerConnection := int64(chunksPerWorker * BUFFERSIZE) - if id+1 == numberConnections { - bytesPerConnection = int64(len(fileBytes)) - (numberConnections-1)*bytesPerConnection + chunkSize := int64(chunksPerWorker * BUFFERSIZE) + if id+1 == c.NumberOfConnections { + chunkSize = int64(c.File.Size) - int64(c.NumberOfConnections-1)*chunkSize } - if id == 0 || id == numberConnections-1 { + if id == 0 || id == c.NumberOfConnections-1 { logger.Debugf("numChunks: %v", numChunks) logger.Debugf("chunksPerWorker: %v", chunksPerWorker) - logger.Debugf("bytesPerConnection: %v", bytesPerConnection) - logger.Debugf("fileNameToSend: %v", path.Base(fileName)) + logger.Debugf("bytesPerchunkSizeConnection: %v", chunkSize) } - payload := strings.Join([]string{ - strconv.FormatInt(int64(bytesPerConnection), 10), // filesize - path.Base(fileName), - fileIV, - fileSalt, - fileHash, - }, "-") - - logger.Debugf("sending fileSize: %d", bytesPerConnection) - logger.Debugf("sending fileName: %s", path.Base(fileName)) - logger.Debugf("sending iv: %s", fileIV) - logger.Debugf("sending salt: %s", fileSalt) - logger.Debugf("sending sha256sum: %s", fileHash) - logger.Debugf("payload is %d bytes", len(payload)) - - connection.Write([]byte(fillString(payload, BUFFERSIZE))) + logger.Debugf("sending chunk size: %d", chunkSize) + connection.Write([]byte(fillString(strconv.FormatInt(int64(chunkSize), 10), 10))) sendBuffer := make([]byte, BUFFERSIZE) - file := bytes.NewBuffer(fileBytes) + file := bytes.NewBuffer(c.File.bytes) chunkI := 0 + if !c.Debug { + c.bars[id] = uiprogress.AddBar(chunksPerWorker).AppendCompleted().PrependElapsed() + } for { _, err = file.Read(sendBuffer) if err == io.EOF { @@ -241,8 +340,11 @@ func sendFile(id int, connection net.Conn, codePhrase string) { logger.Debug("EOF") break } - if (chunkI >= chunksPerWorker*id && chunkI < chunksPerWorker*id+chunksPerWorker) || (id == numberConnections-1 && chunkI >= chunksPerWorker*id) { + if (chunkI >= chunksPerWorker*id && chunkI < chunksPerWorker*id+chunksPerWorker) || (id == c.NumberOfConnections-1 && chunkI >= chunksPerWorker*id) { connection.Write(sendBuffer) + if !c.Debug { + c.bars[id].Incr() + } } chunkI++ } diff --git a/crypto.go b/crypto.go index 85681b1..091a4be 100644 --- a/crypto.go +++ b/crypto.go @@ -28,23 +28,25 @@ func GetRandomName() string { return strings.Join(result, "-") } -func Encrypt(plaintext []byte, passphrase string) ([]byte, string, string) { - if dontEncrypt { +func Encrypt(plaintext []byte, passphrase string, dontencrypt ...bool) (encrypted []byte, salt string, iv string) { + if len(dontencrypt) > 0 && dontencrypt[0] { return plaintext, "salt", "iv" } - key, salt := deriveKey(passphrase, nil) - iv := make([]byte, 12) + key, saltBytes := deriveKey(passphrase, nil) + ivBytes := make([]byte, 12) // http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf // Section 8.2 - rand.Read(iv) + rand.Read(ivBytes) b, _ := aes.NewCipher(key) aesgcm, _ := cipher.NewGCM(b) - data := aesgcm.Seal(nil, iv, plaintext, nil) - return data, hex.EncodeToString(salt), hex.EncodeToString(iv) + encrypted = aesgcm.Seal(nil, ivBytes, plaintext, nil) + salt = hex.EncodeToString(saltBytes) + iv = hex.EncodeToString(ivBytes) + return } -func Decrypt(data []byte, passphrase string, salt string, iv string) (plaintext []byte, err error) { - if dontEncrypt { +func Decrypt(data []byte, passphrase string, salt string, iv string, dontencrypt ...bool) (plaintext []byte, err error) { + if len(dontencrypt) > 0 && dontencrypt[0] { return data, nil } saltBytes, _ := hex.DecodeString(salt) diff --git a/main.go b/main.go index cd940ea..98af5ed 100644 --- a/main.go +++ b/main.go @@ -4,87 +4,39 @@ import ( "bufio" "flag" "fmt" - "io/ioutil" "os" "strings" - - log "github.com/sirupsen/logrus" ) const BUFFERSIZE = 1024 -const numberConnections = 4 -// Build flags -var server, file string - -// Global varaibles -var serverAddress, fileName, codePhraseFlag, connectionTypeFlag string -var runAsRelay, debugFlag, dontEncrypt bool -var fileSalt, fileIV, fileHash string -var fileBytes []byte +type Flags struct { + Relay bool + Debug bool + DontEncrypt bool + Server string + File string + Code string + NumberOfConnections int +} func main() { - flag.BoolVar(&runAsRelay, "relay", false, "run as relay") - flag.BoolVar(&debugFlag, "debug", false, "debug mode") - flag.StringVar(&serverAddress, "server", "cowyo.com", "address of relay server") - flag.StringVar(&fileName, "send", "", "file to send") - flag.StringVar(&codePhraseFlag, "code", "", "use your own code phrase") - flag.BoolVar(&dontEncrypt, "no-encrypt", false, "turn off encryption") + flags := new(Flags) + flag.BoolVar(&flags.Relay, "relay", false, "run as relay") + flag.BoolVar(&flags.Debug, "debug", false, "debug mode") + flag.StringVar(&flags.Server, "server", "cowyo.com", "address of relay server") + flag.StringVar(&flags.File, "send", "", "file to send") + flag.StringVar(&flags.Code, "code", "", "use your own code phrase") + flag.BoolVar(&flags.DontEncrypt, "no-encrypt", false, "turn off encryption") + flag.IntVar(&flags.NumberOfConnections, "threads", 4, "number of threads to use") flag.Parse() - // Check build flags too, which take precedent - if server != "" { - serverAddress = server - } - if file != "" { - fileName = file - } - if len(fileName) > 0 { - connectionTypeFlag = "s" // sender + if flags.Relay { + r := NewRelay(flags) + r.Run() } else { - connectionTypeFlag = "r" //receiver - } - - if !runAsRelay { - if len(codePhraseFlag) == 0 { - if connectionTypeFlag == "r" { - codePhraseFlag = getInput("What is your code phrase? ") - } - if len(codePhraseFlag) < 5 { - codePhraseFlag = GetRandomName() - fmt.Println("Your code phrase is now " + codePhraseFlag) - } - } - } - - if connectionTypeFlag == "s" { - // encrypt the file - fmt.Println("encrypting...") - fdata, err := ioutil.ReadFile(fileName) - if err != nil { - log.Fatal(err) - return - } - fileBytes, fileSalt, fileIV = Encrypt(fdata, codePhraseFlag) - fileHash = HashBytes(fdata) - if debugFlag { - ioutil.WriteFile(fileName+".encrypted", fileBytes, 0644) - } - } - - log.SetFormatter(&log.TextFormatter{}) - if debugFlag { - log.SetLevel(log.DebugLevel) - } else { - log.SetLevel(log.WarnLevel) - } - - if runAsRelay { - runServer() - } else if len(serverAddress) != 0 { - runClient(connectionTypeFlag, codePhraseFlag) - } else { - fmt.Println("You must specify either -file (for running as a server) or -server (for running as a client)") + c := NewConnection(flags) + c.Run() } } diff --git a/relay.go b/relay.go index 627d8d4..434f6b4 100644 --- a/relay.go +++ b/relay.go @@ -14,44 +14,65 @@ import ( type connectionMap struct { reciever map[string]net.Conn sender map[string]net.Conn + metadata map[string]string sync.RWMutex } -var connections connectionMap - -func init() { - connections.Lock() - connections.reciever = make(map[string]net.Conn) - connections.sender = make(map[string]net.Conn) - connections.Unlock() +type Relay struct { + connections connectionMap + Debug bool + NumberOfConnections int } -func runServer() { +func NewRelay(flags *Flags) *Relay { + r := new(Relay) + r.Debug = flags.Debug + r.NumberOfConnections = flags.NumberOfConnections + log.SetFormatter(&log.TextFormatter{}) + if r.Debug { + log.SetLevel(log.DebugLevel) + } else { + log.SetLevel(log.WarnLevel) + } + return r +} + +func (r *Relay) Run() { + r.connections = connectionMap{} + r.connections.Lock() + r.connections.reciever = make(map[string]net.Conn) + r.connections.sender = make(map[string]net.Conn) + r.connections.metadata = make(map[string]string) + r.connections.Unlock() + r.runServer() +} + +func (r *Relay) runServer() { logger := log.WithFields(log.Fields{ "function": "main", }) logger.Debug("Initializing") var wg sync.WaitGroup - wg.Add(numberConnections) - for id := 0; id < numberConnections; id++ { - go listenerThread(id, &wg) + wg.Add(r.NumberOfConnections) + for id := 0; id < r.NumberOfConnections; id++ { + go r.listenerThread(id, &wg) } wg.Wait() } -func listenerThread(id int, wg *sync.WaitGroup) { +func (r *Relay) listenerThread(id int, wg *sync.WaitGroup) { logger := log.WithFields(log.Fields{ "function": "listenerThread:" + strconv.Itoa(27000+id), }) defer wg.Done() - err := listener(id) + err := r.listener(id) if err != nil { logger.Error(err) } } -func listener(id int) (err error) { +func (r *Relay) listener(id int) (err error) { port := strconv.Itoa(27001 + id) logger := log.WithFields(log.Fields{ "function": "listener" + ":" + port, @@ -69,15 +90,16 @@ func listener(id int) (err error) { return errors.Wrap(err, "problem accepting connection") } logger.Debugf("Client %s connected", connection.RemoteAddr().String()) - go clientCommuncation(id, connection) + go r.clientCommuncation(id, connection) } } -func clientCommuncation(id int, connection net.Conn) { +func (r *Relay) clientCommuncation(id int, connection net.Conn) { sendMessage("who?", connection) - message := receiveMessage(connection) - connectionType := strings.Split(message, ".")[0] - codePhrase := strings.Split(message, ".")[1] + "-" + strconv.Itoa(id) + + m := strings.Split(receiveMessage(connection), ".") + connectionType, codePhrase, metaData := m[0], m[1], m[2] + key := codePhrase + "-" + strconv.Itoa(id) logger := log.WithFields(log.Fields{ "id": id, "codePhrase": codePhrase, @@ -85,39 +107,61 @@ func clientCommuncation(id int, connection net.Conn) { if connectionType == "s" { logger.Debug("got sender") - connections.Lock() - connections.sender[codePhrase] = connection - connections.Unlock() + r.connections.Lock() + r.connections.metadata[key] = metaData + r.connections.sender[key] = connection + r.connections.Unlock() + // wait for receiver for { - connections.RLock() - if _, ok := connections.reciever[codePhrase]; ok { + r.connections.RLock() + if _, ok := r.connections.reciever[key]; ok { logger.Debug("got reciever") - connections.RUnlock() + r.connections.RUnlock() break } - connections.RUnlock() + r.connections.RUnlock() time.Sleep(100 * time.Millisecond) } logger.Debug("telling sender ok") sendMessage("ok", connection) logger.Debug("preparing pipe") - connections.Lock() - con1 := connections.sender[codePhrase] - con2 := connections.reciever[codePhrase] - connections.Unlock() + r.connections.Lock() + con1 := r.connections.sender[key] + con2 := r.connections.reciever[key] + r.connections.Unlock() logger.Debug("piping connections") Pipe(con1, con2) logger.Debug("done piping") - connections.Lock() - delete(connections.sender, codePhrase) - delete(connections.reciever, codePhrase) - connections.Unlock() + r.connections.Lock() + delete(r.connections.sender, key) + delete(r.connections.reciever, key) + delete(r.connections.metadata, key) + r.connections.Unlock() logger.Debug("deleted sender and receiver") } else { + // wait for sender's metadata + for { + r.connections.RLock() + if _, ok := r.connections.metadata[key]; ok { + logger.Debug("got sender meta data") + r.connections.RUnlock() + break + } + r.connections.RUnlock() + time.Sleep(100 * time.Millisecond) + logger.Debug("waiting for metadata") + } + // send meta data + r.connections.RLock() + sendMessage(r.connections.metadata[key], connection) + r.connections.RUnlock() + // check for receiver's consent + consent := receiveMessage(connection) + logger.Debug("consent: %s", consent) logger.Debug("got reciever") - connections.Lock() - connections.reciever[codePhrase] = connection - connections.Unlock() + r.connections.Lock() + r.connections.reciever[key] = connection + r.connections.Unlock() } return }