예제 #1
0
파일: Program.cs 프로젝트: RuiPenetra/spks
 public void AdicionarSala(Sala nova_sala)
 {
     listaSalas.Add(nova_sala);
 }
예제 #2
0
파일: Program.cs 프로젝트: RuiPenetra/spks
        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();
        }