public void AdicionarSala(Sala nova_sala) { listaSalas.Add(nova_sala); }
private void threadHandler() { // OBTEM O STREAM DE COMUNICAÇÃO COM O CLIENTE networkStream = this.client.GetStream(); // INICIA O PROTOCOLO SI protocolSI = new ProtocolSI(); // para a hash para validação de dados (integridade) sha512 = SHA512.Create(); byte[] ack; string ficheiro_sala = ""; StreamWriter streamWriter; // directoria do projeto (Servidor/) string directoriaProjeto = Directory.GetParent(Environment.CurrentDirectory).Parent.FullName; // ENQUANTO NÃO RECEBE UMA MSG DO TIPO EOT while (protocolSI.GetCmdType() != ProtocolSICmdType.EOT) { // RECEBE AS MENSAGENS DO CLIENTE int bytesRead = networkStream.Read(protocolSI.Buffer, 0, protocolSI.Buffer.Length); // VERIFICA O TIPO DE MENSAGEM RECEBIDO switch (protocolSI.GetCmdType()) { //enviar chaves case ProtocolSICmdType.USER_OPTION_1: //guardar chave do cliente this.chavePublicaCliente = protocolSI.GetStringFromData(); Enviar_ACK(); //criar chave simetrica aes = new AesCryptoServiceProvider(); //guardar chave e vetor da chave simetrica this.key = aes.Key; this.IV = aes.IV; // cifrar com a chave publica do cliente string keycifrada = cifrarComChavePublica(this.key, this.chavePublicaCliente); string ivcifrado = cifrarComChavePublica(this.IV, this.chavePublicaCliente); // enviar chave simetrica para o cliente //chave byte[] enviarKey = protocolSI.Make(ProtocolSICmdType.SECRET_KEY, Convert.FromBase64String(keycifrada)); networkStream.Write(enviarKey, 0, enviarKey.Length); // se o cliente respondeu ACK, continua a enviar coisas if (Receber_ACK()) { //enviar vetor byte[] enviarIV = protocolSI.Make(ProtocolSICmdType.IV, Convert.FromBase64String(ivcifrado)); networkStream.Write(enviarIV, 0, enviarIV.Length); if (Receber_ACK()) { //cifrar a chave publica com a chave simetrica byte[] chavePublica = Encoding.UTF8.GetBytes(chavepublicaServidor); byte[] chavePublicaCifrada = cifrarComChaveSimetrica(chavePublica); //enviar para o cliente byte[] enviarChavePublicaCifrada = protocolSI.Make(ProtocolSICmdType.SYM_CIPHER_DATA, chavePublicaCifrada); networkStream.Write(enviarChavePublicaCifrada, 0, enviarChavePublicaCifrada.Length); if (!Receber_ACK()) { Enviar_NACK(); // algo correu mal } } else { Enviar_NACK(); // algo correu mal } } else { Enviar_NACK(); // algo correu mal } break; // login case ProtocolSICmdType.USER_OPTION_2: dados_recebidos = protocolSI.GetData(); byte[] dados_login_bytes = decifrarComChaveSimetrica(dados_recebidos); string dados_login = emString(dados_login_bytes); // separar dados string[] split_dados_login = Regex.Split(dados_login, "_Ø_"); // username_Ø_password string user = split_dados_login[0]; string pass = split_dados_login[1]; Enviar_ACK(); // recebe a hash, decifra-a e envia ACK hash_recebida = Receber_Hash(); // integridade if (Integridade_Valida(hash_recebida, dados_login_bytes)) { // caso login com sucesso, manda o OK e guarda o username if (VerifyLogin(user, pass)) { this.nickname = user; Enviar_ACK(); } else { Enviar_NACK(); } } else { Enviar_NACK(); } break; // registo case ProtocolSICmdType.USER_OPTION_3: dados_recebidos = protocolSI.GetData(); byte[] dados_bytes = decifrarComChaveSimetrica(dados_recebidos); string dados = emString(dados_bytes); Enviar_ACK(); // separar dados string[] split_dados = Regex.Split(dados, "_Ø_"); // username_Ø_password //guarda a password e passa para bytes string username = split_dados[0]; byte[] password = Encoding.UTF8.GetBytes(split_dados[1]); //gera o salt e "salga" a password byte[] salt = GenerateSalt(SALTSIZE); byte[] passwordSalt = GenerateSaltedHash(password, salt); // recebe a hash, decifra-a e envia ACK hash_recebida = Receber_Hash(); if (Integridade_Valida(hash_recebida, dados_bytes)) { // caso registe com sucesso, manda o OK if (Register(username, passwordSalt, salt)) { Console.WriteLine("Novo registo de utilizador: " + username); Enviar_ACK(); } else { Enviar_NACK(); } } else { Enviar_NACK(); } break; // sala case ProtocolSICmdType.USER_OPTION_4: //receber dados dados_recebidos = protocolSI.GetData(); //decifrar dados byte[] nome_sala_bytes = decifrarComChaveSimetrica(dados_recebidos); string nome_sala = emString(nome_sala_bytes); //enviar ack Enviar_ACK(); //receber hash, decifrar e enviar ack hash_recebida = Receber_Hash(); if (Integridade_Valida(hash_recebida, nome_sala_bytes)) { // remove caracteres especiais do nome da sala // e constroi o caminho da directoria ficheiro_sala = directoriaProjeto + "/Salas/" + CleanInput(nome_sala) + ".txt"; IdSala = getIdSala(listSalas, nome_sala); //verificar se a sala ja existe if (File.Exists(ficheiro_sala) && IdSala > -1) { //Verifica se esta disponivel if (listSalas[IdSala].numeroClientes >= 2) //Caso nao esteja disponivel { //dizer que nao ta ok Enviar_NACK(); Console.WriteLine("Cliente: " + nickname + " tentou entrar na sala " + nome_sala + " "); Console.WriteLine("<O cliente {0} desconectou-se!>", clientID); // FECHA AS COMUNICAÇOES COM O CLIENTE networkStream.Close(); client.Close(); return; } else { //Adiciona o cliente ClassePrincipalServidor.AdicionarCliente(IdSala); if (listSalas[IdSala].nickname1 == null) { listSalas[IdSala].nickname1 = nickname; } else { listSalas[IdSala].nickname2 = nickname; } // colocar a sinalização de que ha msgs novas para o novo cliente ler ClassePrincipalServidor.ExistemMensagensNovas(IdSala); // avisar sala de que entrou streamWriter = new StreamWriter(ficheiro_sala, true); // append true streamWriter.WriteLine(nickname + " entrou. "); streamWriter.Dispose(); } Enviar_ACK(); if (listSalas[IdSala].Atac == null) { listSalas[IdSala].Atac = nickname; enviarCliente("Ataque"); } else { listSalas[IdSala].Defensor = nickname; enviarCliente("Defesa"); } } else { //Criar sala(ficheiro); // guarda o nome da sala no cliente Sala Sala = new Sala(nome_sala); streamWriter = new StreamWriter(ficheiro_sala, false); // append false streamWriter.WriteLine("Chat Iniciado"); // avisar sala de que entrou streamWriter.WriteLine(nickname + " entrou. "); streamWriter.Dispose(); // cria nova sala na lista geral ClassePrincipalServidor.AdicionarSala(Sala); // atualiza a lista local this.listSalas = ClassePrincipalServidor.ListaDeSalas(); IdSala = listSalas.LastIndexOf(Sala); Enviar_ACK(); listSalas[IdSala].nickname1 = nickname; listSalas[IdSala].Atac = nickname; enviarCliente("Ataque"); Console.WriteLine("Sala criada: " + Sala.nome + " "); Console.WriteLine("Total de salas: " + listSalas.Count() + " "); } break; } else { Enviar_NACK(); } break; //resposta ao pedido de dados do cliente case ProtocolSICmdType.USER_OPTION_5: //receber dados dados_recebidos = protocolSI.GetData(); //passar para bytes byte[] dados_Bytes = decifrarComChaveSimetrica(dados_recebidos); //decifrar dados int lenghtClienteTem = Int32.Parse(emString(dados_Bytes)); //enviar ack Enviar_ACK(); //receber hash e enviar ack hash_recebida = Receber_Hash(); // verificar a integridade if (Integridade_Valida(hash_recebida, dados_Bytes)) { Enviar_ACK(); // vai buscar o ficheiro da sala para ler ficheiro_sala = directoriaProjeto + "/Salas/" + CleanInput(listSalas[IdSala].nome) + ".txt"; string response = File.ReadAllText(ficheiro_sala); response = response.Substring(lenghtClienteTem); // LIMPA A VARIAVEL AUXILIAR string stringChunk = ""; // TAMANHO PARA LER DE CADA VEZ (USAR COMO MÁX 64 CARACTERES) int chunkSize = 60; // VAI BUSCAR O TAMANHO DA RESPOSTA int stringLength = response.Length; // variavel para o pacote byte[] packet; //packet = protocolSI.Make(ProtocolSICmdType.ACK, stringLength); //networkStream.Write(packet, 0, packet.Length); Console.WriteLine(" -> " + stringLength); // PERCORRE A RESPOSTA E VAI DIVIDINDO EM PEDAÇOS PEQUENOS (CHUNKS) for (int i = 0; i < response.Length; i = i + chunkSize) { // CASE SEJA O ÚLTIMO CHUNK if (chunkSize > stringLength) { // ENVIA TUDO O QUE FALTA stringChunk = response.Substring(i); } // CASO SEJA UM CHUNK NORMAL else { // DECREMENTA O TOTAL DE CARACTERES JÁ LIDOS stringLength = stringLength - chunkSize; // OBTEM ESSE CHUNK stringChunk = response.Substring(i, chunkSize); } //cifrar e enviar byte[] chunckCifrado = cifrarComChaveSimetrica(Encoding.UTF8.GetBytes(stringChunk)); // CRIA A MENSAGEM DO TIPO DATA UTILIZANDO O PROTOCOLO SI packet = protocolSI.Make(ProtocolSICmdType.DATA, chunckCifrado); // ENVIA A RESPOSTA PARA O CLIENTE (WRITE) networkStream.Write(packet, 0, packet.Length); } Console.WriteLine(" -> " + nickname + " atualizou os dados"); // CRIA O EOF PARA ENVIAR PARA O CLIENTE byte[] eof = protocolSI.Make(ProtocolSICmdType.EOF); // ENVIA A RESPOSTA PARA O CLIENTE (WRITE) networkStream.Write(eof, 0, eof.Length); Console.WriteLine(" -> " + nickname + " EOF "); // Receber ACK, se o cliente recebeu tudo, continua if (Receber_ACK()) { // enviar hash de tudo byte[] hashing = CriarHash_Assinar_Cifrar(Encoding.UTF8.GetBytes(response)); // CRIA A MENSAGEM DO TIPO NORMAL (para enviar a hash) UTILIZANDO O PROTOCOLO SI packet = protocolSI.Make(ProtocolSICmdType.NORMAL, hashing); //escreve na stream networkStream.Write(packet, 0, packet.Length); if (!Receber_ACK()) // nao recebeu hash { Console.WriteLine(" -> " + nickname + "não recebeu hash "); } } } else { Enviar_NACK(); } break; // saber se existem novas msgs case ProtocolSICmdType.USER_OPTION_6: if (ClassePrincipalServidor.VerificarMensagensNovas(IdSala) > 0) { // CRIA O ACK PARA ENVIAR PARA O CLIENTE para ele verificar as novas msgs Enviar_ACK(); Console.WriteLine(" -> " + nickname + " precisa de novos dados"); } else { Enviar_NACK(); // nao necessita de atualizar } break; case ProtocolSICmdType.USER_OPTION_7: //receber dados dados_recebidos = protocolSI.GetData(); byte[] msg = decifrarComChaveSimetrica(dados_recebidos); string pos = emString(msg); //enviar ack Enviar_ACK(); //receber hash e enviar ack hash_recebida = Receber_Hash(); // verificar a integridade if (Integridade_Valida(hash_recebida, msg)) { if (nickname == listSalas[IdSala].Atac) { listSalas[IdSala].PosAtac = pos; Console.Write("Estou a atacar na pos:" + pos); } else { listSalas[IdSala].PosDefensor = pos; Console.Write("Estou a defender na pos:" + pos); } } else { Enviar_NACK(); } break; case ProtocolSICmdType.USER_OPTION_8: if (listSalas[IdSala].PosAtac == null || listSalas[IdSala].PosDefensor == null) { Enviar_NACK(); break; } Enviar_ACK(); if (listSalas[IdSala].PosAtac == listSalas[IdSala].PosDefensor) { if (nickname == listSalas[IdSala].Atac) { enviarCliente(NAOACERTOU); listSalas[IdSala].msg++; } else { enviarCliente(DEFENDEU); listSalas[IdSala].msg++; listSalas[IdSala].adicionarPontos(nickname); } } else { if (nickname == listSalas[IdSala].Atac) { enviarCliente(MARCOU); listSalas[IdSala].msg++; listSalas[IdSala].adicionarPontos(nickname); } else { enviarCliente(NAOACERTOU); listSalas[IdSala].msg++; } } if (listSalas[IdSala].msg == 2) { listSalas[IdSala].msg = 0; listSalas[IdSala].PosAtac = null; listSalas[IdSala].PosDefensor = null; listSalas[IdSala].trocarFuncoes(); listSalas[IdSala].QuantosJogaram++; if (listSalas[IdSala].QuantosJogaram == 5) { listSalas[IdSala].QuantosJogaram = 0; listSalas[IdSala].PontosJogador1 = 0; listSalas[IdSala].PontosJogador2 = 0; } } break; // SE FOR DO TIPO DATA É UMA MENSAGEM PARA MOSTRAR case ProtocolSICmdType.DATA: //receber dados dados_recebidos = protocolSI.GetData(); //decifrar dados byte[] mensagemBytes = decifrarComChaveSimetrica(dados_recebidos); // passar para string string mensagem = emString(mensagemBytes); //enviar ack Enviar_ACK(); //receber hash e enviar hack hash_recebida = Receber_Hash(); // integridade if (Integridade_Valida(hash_recebida, mensagemBytes)) { // mensagens normais //Console.WriteLine(" (" + nickname + "): " + protocolSI.GetStringFromData()); streamWriter = new StreamWriter(ficheiro_sala, true); // append true streamWriter.WriteLine(nickname + " disse: " + mensagem); streamWriter.Dispose(); // colocar a sinalização de que ha msgs novas ClassePrincipalServidor.ExistemMensagensNovas(IdSala); Enviar_ACK(); } else { Enviar_NACK(); } break; // SE FOR DO TIPO EOT É PARA FECHAR A COMUNICAÇÃO case ProtocolSICmdType.EOT: Console.WriteLine("<O cliente {0} desconectou-se!>", clientID); // avisar sala if (!ficheiro_sala.Equals("")) { streamWriter = new StreamWriter(ficheiro_sala, true); // append true streamWriter.WriteLine(nickname + " desligou-se. "); streamWriter.Dispose(); // colocar a sinalização de que ha msgs novas ClassePrincipalServidor.ExistemMensagensNovas(IdSala); // remover utilizador da sala ClassePrincipalServidor.RemoverCliente(IdSala); // atualiza a lista local this.listSalas[IdSala].numeroClientes--; } Enviar_ACK(); break; } } // FECHA AS COMUNICAÇOES COM O CLIENTE networkStream.Dispose(); client.Dispose(); }