Programando Socket em C 0xC2
continuando nossa saga sobre socket's, o estudo de socket's na minha opinião vale muito a pena o investimento do seu tempo porque ele é a porta pra você desenvolver as tool malignas do mundo blackhat, vamos estudar algumas dessas ferramentas futuramente.Na última vez que nós aprendemos sobre o headers que ele tem uma função muito importante e declaramos nosso primeiro socket agora, vamos relembrar um pouco.
#include < sys/types.h >
#include < sys/sockets.h >
#include < netinet/in.h >
A função socket() ela retorna -1 caso for erro então vamos declarar nossa variável novamente e ver como funciona, há lembrando para usar socket em windows é preciso de uma API, no linux não precisamos de nenhuma dll, porque o linux é foda heehheeh vamos lá declaramos nosso socket e ver alguns detalhes da criação do mesmo :D.
int SoCk;
meu_socket = socket (AF_INET, SOCK_STREAM, 0);
simples não é, vamos entender o que nós fizemos agora, criamos um variável do tipo Int(inteiro) e logo depois pegando a função socket(){que return -1 no caso de erro :D}. e atribuirmos a família AF_INET o tipo do protocolo SOCK_STREAM (potocolo TCP) e o Zero que indica que vamos fazer uma conexão IP. há é se eu colocar 1 no lugar do 0, agora vai ser uma conexão ICMP, vou deixar uma pequena lista com os tipos.
0 - IP - INTERNET PROTOCOL 1 - ICMP - INTERNET CONTROL MESSAGE PROTOCOL 2 - IGMP - INTERNET GROUP MULTICAST PROTOCOL 3 - GGP - GATEWAY-GATEWAY PROTOCOL 6 - TCP - TRANSMISSION CONTROL PROTOCOL 17 - UDP - USER DATAGRAMA PROTOCOL
É assim que funciona mano, vamos lá para a família que até esqueci não só tem a AF_INET tem mais algumas como essas ae:
+ AF_INET (ARPA INTERNET PROTOCOLS) - A mais usada + AF_UNIX (UNIX INTERNET PROTOCOLS) + AF_ISO (ISO PROTOCOLS) + AF_NS (XEROX NETWORK SYSTEM PROTOCOLS)
Não me interessa essas outras família e tudo mais deixa isso pros psicopatas que querem saber de tudo e nem vou perder meu tempo aprendendo uma coisa que nunca irei usar isso é coisa de doente. kkk Família = tudo que eh da mesma laia, ou seja, quando eu inicio 1 socket especificando o AF_INET, eu estou querendo dizer q to afim d programar p/ a familia TCP/IP. Comumente voce encontrara' um tal de PF_INET ao inves de AF_INET nos sources da galera, por isso vou adiantando logo que AF_INET se trata de um alias para PF_INET e por esse mesmo motivo, podemos dizer q de certa forma eh mais "correto" usarmos o PF_INET (PF_INET eh mais Hacko! As pessoas na rua vão começar a dizer que você realmente sabe o que ta fazendo eheh =).
Vamos avançar nos estudos falar um pouco sobre as estruturas (struct), precisamos usa-las para definimos as funções que citei acima.Vamos ver agora como ta ficando nosso código =). Existe 1 truque amigo, eh bastante simples, quando vc
nao souber em qual header uma determinada função esta, basta você ir neste diretório:
/usr/include
E procurar a tal função baseando-se pelo diretiva de compilação '#include' no codigo
fonte do programa q você quer estudar, abra o header e procure pela função, simples =)
sys/types.h -> definição de tipos de dados sys/socket.h -> funções e constantes relativas a sockets netinet/in.h -> para manipulação de estruturas, dados e endereços da famàlia internet arpa/inet.h -> definições para funções destinadas a operações com sockets netdb.h -> permite o uso de funções relativas a hostnames, portas, etc
#include < sys/types.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
#include < netdb.h >
int main()
{
int meu_socket;
meu_socket = socket(AF_INET,SOCK_STREAM,0); // cria meu socket
if(meu_socket == -1)
printf("Erro ao criar o socket!\n");
else
printf("Socket criado com sucesso!\n");
return 0;
}
EHHEH ta ficando Bom, e como disse se nosso descritor nosso socket voltar o valor -1 vai dar uma msg falando onde ta ocorrendo o ERROR poderíamos usar a função Perror mais deixa pra próxima. temos agora que criar a struct com as informações ,Deu até saudade de python como isso é simples, com python.
#include < stdio.h > /* para usar printf() */
#include < sys/types.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
#include < netdb.h >
int main()
{
int meu_socket;
struct sockaddr_in addr;
meu_socket = socket(AF_INET,SOCK_STREAM,0);
if(meu_socket == -1)
{
printf("Erro ao criar o socket!\n");
return 1;
}
addr.sin_family = AF_INET; // 2 Bytes (short)
addr.sin_port = htons(1234); //2 Bytes (short)
addr.sin_addr.s_addr = INADDR_ANY; //4 bytes
memset(&addr.sin_zero,0,sizeof(addr.sin_zero)); //preenche os 16 bit
if(bind(meu_socket,(struct sockaddr*)&addr,sizeof(addr)) == -1)
{
printf("Erro na funcao bind()\n");
return 1;
struct sockaddr_in addr; Declaração de uma estrutura sockaddr_in responsável por fornecer ao socket as informações sobre a famàlia, endereço e porta que devem ser utilizados para a comunicação. depois é preciso preencher a struct com as informações, como Familia, porta e IP. a memset vai preencher o restante da estrutura com zero, isso acontece porque IP e a porta são convertido para bytes, a porta com htons e o Ip com INADDR. tudo isso dar 16 bit's. logo depois vem um tratramento com a função BIND, A função bind() é respondível por associar um endereço e porta locais a um socket.
Casa der Erro return -1, agora vem a função listen()coloca o socket em modo de espera definindo um mínimo de 1 conexão pendente que deve ser aguardada.
if(listen(meu_socket,1) == -1)
{
printf("Erro na funcao listen()\n");
return 1;
}
// Accept() essa função como o nome já demostra ela aceita a primeira conexão que passa pelo Bind(). :D simples
meu_socket = accept(meu_socket,0,0);
if(meu_socket == -1)
{
printf("Erro na funcao accept()\n");
return 1;
}
Agora vejamos a estrutura e as funções utilizadas com mais detalhes:
struct sockaddr_in addr;
Uma estrutura do tipo sockaddr_in, definida no header netinet/in.h, contém as informações necessírias para estabelecer uma comunicação entre o computador local e um remoto, baseando-se no seu endereço IP e na sua porta.
Esta estrutura foi criada para simplificar o uso de sockets. A verdade é que funções como bind() e connect(), por exemplo, que utilizam informações sobre porta e endereço para realizarem operações com socket - conectar a um host remoto, configurar localmente um socket, etc -, as obtém de uma estrutura chamada sockaddr.
A estrutura sockaddr é definida da seguinte forma:
strut sockaddr
{
unsigned short sa_family; /* 2 bytes */
char sa_data[14]; /* 14 bytes */
}; /* Total = 16 bytes */
Agora vamos ver como ta ficando nosso código:
#include < stdio.h > /* para usar printf() */
#include < sys/types.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
#include < netdb.h >
int main()
{
int meu_socket;
int sock_cliente;
struct sockaddr_in addr;
meu_socket = socket(AF_INET,SOCK_STREAM,0);
if(meu_socket == -1)
{
printf("Erro ao criar o socket!\n");
return 1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
addr.sin_addr.s_addr = INADDR_ANY;
memset(&addr.sin_zero,0,sizeof(addr.sin_zero));
if(bind(meu_socket,(struct sockaddr*)&addr,sizeof(addr)) == -1)
{
printf("Erro na funcao bind()\n");
return 1;
}
if(listen(meu_socket,1) == -1)
{
printf("Erro na funcao listen()\n");
return 1;
}
printf("Aguardando conexoes...\n");
sock_cliente = accept(meu_socket,0,0);
if(sock_cliente == -1)
{
printf("Erro na funcao accept()\n");
return 1;
}
printf("Pedido de conexao feito!\n");
if(send(sock_cliente,"Hi",2,0) == -1)
{
printf("Erro ao enviar dados!\n");
return 1;
}
close(sock_cliente);
close(meu_socket);
return 0;
}
A função close() fecha o socket, claro né :D. mas é apenas uma demostração ta faltando agora duas. vamos lá logo abaixo vera que a função send precisa de alguns parâmetros a ser passado como socket, conteudo,tamanho,e a frag.
char mensagem[] = "qualquer coisa";
send(sock_cliente,mensagem,strlen(mensagem),0);
Nesse caso tou criando uma string e enviando, a frag não mexa normalmente é usada a frag zero são os valores opcionais. Agora para receber os cados utilizamos a função recv(), para pegar os dados enviando pelo cliente e fazer alguma coisa nesse caso vamos aprenas dar um printf.
int bytes = recv(sock_cliente,resposta,100,0);
if(bytes == -1) /* Erro */
printf("Erro ao receber dados!\n");
else
printf("Recebidos %d bytes.\nDados: %s",bytes,resposta); /* Mostra a resposta do cliente */
Agora a função Connect() que o nome ja diz tudo né, Utilizamos a função connect() para fazer com que um socket se conecte a um host remoto.
if(connect(meu_socket,(struct sockaddr*)&addr,sizeof(addr)) == -1)
{
printf("Erro ao se conectar!\n");
return 1;
}
A essa função pega aquela struct que definimos e coloca no pacote lógico ela precisa saber o IP e porta e familia para conectar com o cliente. outra coisa quando enviamos algum é preciso inserir um caracter nulo para não da algum tipo de erro usando send(meu_socket,"Alguma coisa\n",13,0);, ou seja precisa qualquer o tamanho exato da string que vai ser enviando. para não perder o costume vamos ver agora um exemplo de como seria enviar uma string para o servidor.
ServidorChat.c
#include < stdio.h >
#include <sys/types.h >
#include <sys/socket.h >
#include <netinet/in.h >
#include < arpa/inet.h >
#include < netdb.h >
int main()
{
int meu_socket;
struct sockaddr_in addr_local, addr;
meu_socket = socket(AF_INET,SOCK_STREAM,0);
if(meu_socket == -1)
{
printf("Erro ao criar o socket!\n");
return 1;
}
/* Preenche a estrutura que serí utilizada com a função bind() */
addr_local.sin_family = AF_INET;
addr_local.sin_port = htons(4321);
addr_local.sin_addr.s_addr = INADDR_ANY;
memset(&addr_local.sin_zero,0,sizeof(addr_local.sin_zero));
if(bind(meu_socket,(struct sockaddr*)&addr_local,sizeof(addr_local)) == -1)
{
printf("Erro na funcao bind()\n");
return 1;
}
/* Estrutura destinada à função connect() */
addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(&addr.sin_zero,0,sizeof(addr.sin_zero));
printf("Tentando se conectar...\n");
if(connect(meu_socket,(struct sockaddr*)&addr,sizeof(addr)) == -1)
{
printf("Erro ao se conectar!\n");
return 1;
}
printf("Conectado!\nEnviando dados...\n");
send(meu_socket,"Alguma coisa\n",13,0);
close(meu_socket);
return 0;
}
Pronto! vamos para uma demostração, vamos criar um simples chat para estabelecer uma comunicação entre cliente e servidor, use o GCC e compile os dois códigos abaixo lembando que você pode alterar a porta que você quiser. é simples calma vamos ainda para a parte maliciosa ehhehe, por em quanto o aprendizado fala mais alto.
clienteChat.c
#include < stdio.h >
#include < sys/types.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
#include < netdb.h >
int main()
{
int meu_socket;
struct sockaddr_in addr;
meu_socket = socket(AF_INET,SOCK_STREAM,0);
if(meu_socket == -1)
{
printf("Erro ao criar o socket!\n");
return 1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
memset(&addr.sin_zero,0,sizeof(addr.sin_zero));
printf("Tentando se conectar ao servidor...\n");
if(connect(meu_socket,(struct sockaddr*)&addr,sizeof(addr)) == -1)
{
printf("Erro ao se conectar!\n");
return 1;
}
printf("Conectado!\n\n");
int recebidos, enviados;
char mensagem[256];
char resposta[256];
do
{
/* O processo inverso é feito aqui. Como o servidor espera uma mensagem inicialmente, o cliente deverí fornecê-la */
printf("Cliente: ");
fgets(mensagem,256,stdin);
mensagem[strlen(mensagem)-1] = '\0';
enviados = send(meu_socket,mensagem,strlen(mensagem),0);
/* Após enviar a mensagem, espera-se a resposta do servidor */
recebidos = recv(meu_socket,resposta,256,0);
resposta[recebidos] = '\0';
printf("Servidor: %s\n",resposta);
}while(recebidos != -1 && enviados != -1);
close(meu_socket);
return 0;
}
#include < stdio.h >
#include < string.h >
#include < sys/types.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
#include < netdb.h >
int main()
{
int meu_socket;
int sock_cliente;
struct sockaddr_in addr;
meu_socket = socket(AF_INET,SOCK_STREAM,0);
if(meu_socket == -1)
{
printf("Erro ao criar o socket!\n");
return 1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(1234);
addr.sin_addr.s_addr = INADDR_ANY;
memset(&addr.sin_zero,0,sizeof(addr.sin_zero));
if(bind(meu_socket,(struct sockaddr*)&addr,sizeof(addr)) == -1)
{
printf("Erro na funcao bind()\n");
return 1;
}
if(listen(meu_socket,1) == -1)
{
printf("Erro na funcao listen()\n");
return 1;
}
printf("Aguardando cliente...\n");
sock_cliente = accept(meu_socket,0,0);
if(sock_cliente == -1)
{
printf("Erro na funcao accept()\n");
return 1;
}
printf("Cliente conectado!\n\n");
int recebidos, enviados; /* Controle de bytes enviados e recebidos */
char mensagem[256]; /* Buffer para envio de mensagens */
char resposta[256]; /* Buffer para receber mensagens */
do /* Executa as instruções abaixo ... */
{
recebidos = recv(sock_cliente,resposta,256,0); /* Recebe mensagem do cliente */
resposta[recebidos] = '\0'; /* Finaliza a string com o caractere NULO */
printf("Cliente: %s\n",resposta); /* Mostra a mensagem do cliente */
printf("Servidor: "); /* Simplesmente informa que deve-se preencher uma mensagem */
fgets(mensagem,256,stdin); /* Obtém uma mensagem digitada */
mensagem[strlen(mensagem)-1] = '\0'; /* Finaliza a string */
enviados = send(sock_cliente,mensagem,strlen(mensagem),0); /* Envia a string */
}while(recebidos != -1 && enviados != -1); /* ... enquanto as funções send() e recv() não retornarem -1 = ERRO */
close(sock_cliente);
close(meu_socket);
return 0;
}
gretz: Dark_Side