Plataforma: Debian (Linux debian 2.6.32-5-amd64)
Olá Pessoal,
hoje vamos trabalhar em uma aplicação cliente/servidor – client/server – onde nosso cliente irá enviar alguns comandos como (ls, mkdir, etc...) que serão executados no servidor e exibidos na tela do cliente. A topologia é bem simples, nosso servidor deverá ficar escutando – listening - em um determidada porta. Nosso servidor porvê autenticação, e a base de dados é a mesma do sistema. Assim, você irá se logar apenas se o usuário digitado, for um usuário cadastrado no sistema. Para verificar os usuários do sistema você pode verificar em: /etc/passwd.
Bom nosso servidor também loga em um arquivo quando o cliente se conecta e quando o mesmo é desconectado:
Para rodar nosso servidor executamos o seguinte comando: ./teste 4001
Onde 4001 é o número da porta que o servidor deve estar escutando. Você pode selecionar outra porta, sempre tendo o cuidado de não colocar números que estão sendo utilizados por outros serviços, como http – porta 80, hhtps: 443, etc...
Esse é o código do nosso servidor:
Cabeçários:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <crypt.h>
#include <shadow.h>
Funções Auxiliares, é sempre recomendado que você organize os seus códigos escritos na Linguagem C, em funções, assim o código ficará mais claro de ser entendido. Sempre que considerar necessários comentários são bem vindos:
Função responsável por gravar os dados dos cliente conectados. Veeja que a mesma recebe a estrutura de dados do cliente.
int gravaLog(struct sockaddr_in cliaddr, char * mensagem)
{
char buff[4096]; //armazenar informações do cliente
FILE * arqLog; //Arquivo de logs para IP e porta de conexão do cliente
int porta; //porta que o cliente usou para se conectar
time_t tem = time(NULL);
arqLog = fopen("logs","a+");
inet_ntop(AF_INET, &(cliaddr.sin_addr), buff, sizeof(buff));
porta = ntohs(cliaddr.sin_port);
fprintf(arqLog,"%s: IP %s, Porta %d as %s",mensagem,buff,porta, ctime(&tem));
fclose(arqLog);
return 1;
}
Função que faz a consulta no arquivo onde são armazenadas as senhas de nosso sistema.
int AutenPasswd(char * user, char * senha)
{
struct passwd * pw;
struct spwd * shad;
pw = getpwnam(user);
shad = getspnam(user);
if(pw == NULL)
return -1;
else
{
strcpy(pw->pw_passwd,shad->sp_pwdp);
if (strcmp(crypt(senha, pw->pw_passwd),pw->pw_passwd)==0)
return 1;
else
return 0;
}
}
Essa função recebe os dados fornecidos pelo programa cliente, ela faz uso da função AutenPasswd:
//Retorna 0 se a autenticacao falhar, 1 ser ocorrer ocm sucesso
int Autenticacao(int * connfd)
{
int err; //recebe valores de retorno das funções
char buff[256], user[256], password[256];
strcpy(buff, "1");
bzero(user,256);
bzero(password,256);
err = read(*connfd,user,sizeof(user));
err = read(*connfd,password,sizeof(password));
err = AutenPasswd(user,password);
if(err != 1) //Autenticacao falhou
{
strcpy(buff, "0");
write(*connfd,buff,sizeof(buff));
close(*connfd);
return 0;
}
else
write(*connfd,buff,sizeof(buff));
return 1;
}
Funções auxiliares para trabalhar com arquivos. O resultado do comando enviado pelo cliente é colocado em um arquivo, temos que ler este aquivo e colocá-lo em uma matriz para enviar. Aqui nós fixamos o tamanho do array, mas o mais interessante seria trabalhar com alocação de memória dinâmica, pegando o número de caracteres retornados pela função ContaCaracteres, assim o programa fica melhor estruturado, pois o tamanho do array pode passar de 4096 e comportamentos não desejados, ou até que comprometam a segurança do sistema podem ocorrer.
int ContaCaracteres()
{
int contador = 1;
char letra;
FILE *arq = fopen("arqTemp","r");
letra = fgetc(arq);
while (letra != EOF)
{
contador++;
letra = fgetc(arq);
}
fclose(arq);
return contador;
}
void EnviaResultado(int * connfd)
{
FILE * arquivo;
char buffer[4096], letra;
int contador, x=0;
contador = ContaCaracteres();
arquivo = fopen("arqTemp","r");
letra = fgetc(arquivo);
while(x<contador)
{
buffer[x] = letra;
letra = fgetc(arquivo);
x++;
}
buffer[--x]='\0';
fclose(arquivo);
write(*connfd,buffer,sizeof(buffer));
}
Enfim, nosso programa main:
int main(int argc, char ** argv)
{
int listenfd, connfd, porta; //descitores de conexao e varialvel porta que ira armazenar a porta do servidor
struct sockaddr_in servaddr, cliaddr; //estruturas sockadd_in para armazenar os dados do server e do cliente
socklen_t len; //variavel para armazenar o tamanho da estrutura do cliente
char buff[4096];//vai armazenar informacoes sobre o cliente
int n,err;
if (strcmp("-h",argv[1]) ==0)
{
printf("\n\tChamada do programa:\n\t./serv numPort\n\tOnde numPort e o numero da porta TCP\n");
return 1;
}
else
if(argc != 2)
{
printf("\n\tErro parametros invalidos\n");
return 0;
}
porta = atoi(argv[1]);
listenfd = socket(AF_INET, SOCK_STREAM,0);
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(porta);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd,1);
len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr,&len);//vamos aceitar o cliente
gravaLog(cliaddr, "Cliente conectou");
err = Autenticacao(&connfd);
if(!err)
return 0;
do
{
read(connfd,buff,sizeof(buff));
if(strcmp(buff,"exit")!=0)
strcat(buff, " > arqTemp");
);
system(buff);
EnviaResultado(&connfd);
}while(strcmp(buff,"exit"));
gravaLog(cliaddr, "Cliente desconectou");
close(connfd);
return 1;
}
Agora nosso programa cliente. O mesmo pode ser executado com o comando ./teste
O IP do servidor e a porta, já estão fixos no código. Seria interessante modificar, para que os mesmos sejam passados pela linha de comando.
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
int RequisitaAutenticacao(int * sockfd)
{
int err;
char buff[256];
char user[256], password[256];
char *ptr;
printf("\nInforme o usuário: ");
scanf("%s",user);
ptr = getpass("\nInforme sua senha:");
strcpy(password,ptr);
err = write(*sockfd, user, sizeof(user)); //envia usuario
err = write(*sockfd,password,sizeof(password)); //escreve a senha
err = read(*sockfd,buff,sizeof(buff));
if(!strcmp(buff,"1"))
return 1; //autenticacao OK
else
return -1; //autenticacao falhou
}
int main()
{
char buff[4096];
int sockfd, ret;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET,SOCK_STREAM,0);
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(4001);
inet_pton(AF_INET,"10.0.2.2", &servaddr.sin_addr);
connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr));
ret = RequisitaAutenticacao(&sockfd);
if(ret != 1)
{
printf("\n\tAutenticacao Falhou");
close(sockfd);
return 0;
}
else
printf("\n\tUsuario Autenticado com SUCESSO!!!");
do
{
printf("\n>");
fgets(buff,4095,stdin);
buff[strlen(buff)-1]='\0';
write(sockfd,buff,sizeof(buff));
if(strcmp(buff,"exit")!=0)
{
read(sockfd,buff,sizeof(buff));
printf("\n%s",buff);
}
}while(strcmp(buff,"exit"));
close(sockfd);
return 0;
}
Nenhum comentário:
Postar um comentário