BSD Sockets em linguagem C

Venho neste artigo explicar como funciona sockets em ANSi C, explicar portabilidade e exemplos reais e diferentes de artigos semelhantes. Enfim, aqui você aprenderá a usar sockets na prática.

[ Hits: 112.606 ]

Por: C00L3R_ em 06/07/2010 | Blog: https://github.com/CoolerVoid


Funções listen(), bind(), accept() e exemplo de servidor HTTP



Só que ao invés de fazer POG com memset, vamos usar uma função para setar dados para zero *bzero (). Uma função que escreve zeros para uma string. No nosso caso, serve para zerar o resto da struct.

void bzero (void *s, int n);

Pega os primeiros "n" bytes da string apontada em s e muda-os para zero.

listen() - esta função faz com que um socket aguarde por conexões.

Fica na escuta:

listen (socket, numero maximo de conexoes);

Ex.:

int sock2; sock2 = socket (AF_INET, SOCK_STREAM, 0);
listen (sock2, 4); // apenas 4 clients podem se conectar nesse socket.

write() - tambem é usada para enviar mensagens para um socket.

int write (socket, *buffer, tamanho utilizado do buffer);
write (sock, msg, strlen (msg));

Você pode substituir a função send() por write() dependendo do caso...

bind() - bind serve para unir um nome ao socket que você abriu. Para que fique bem claro, observe abaixo, IP do servidor: 201.34.36.133, IP do cliente: 201.45.44.144.

Para que o cliente localizado em 201.45.44.144 se comunique com um programa (servidor) localizado em 201.34.36.133 é necessário que os dois se comuniquem por um canal em comum, uma porta. Conexões telnet são feitas normalmente pela porta 23, ftp pela 21, http pela 80 etc. A diferenciação entre os tipos de serviço está pela porta que este abre. Então se eu for mandar uma mensagem para o servidor, esta tem que ir por uma porta aberta no sistema, específica para aquele programa servidor.

A função bind() faz o papel de abrir a porta no sistema.

A sintaxe da bind() é:

int bind (socket, estrutura de conexao (local), tamanho da estrutura);

accept() - estabelece conexões em um socket. Ela cria um novo socket com as mesmas propriedades do socket anterior do seu programa e aloca um novo "int socket" para a nova conexão.

int sock2,
    newSock,   // Este sera o novo socket
    tamanho;
sock2 = socket (AF_INET, SOCK_STREAM, 0);
...
tamanho = sizeof (struct sockaddr);
listen (sock2, 3);
newSock = accept (sock2, (struct sockaddr_in *)&remote, &tamanho);

A sintaxe seria:

novo socket = accept (socket, estrutura de conexao, tamanho);

Paramos de conversa e vamos ao programa comentado:

#include "stdio.h"
#include "errno.h"
#include "sys/socket.h"
#include "resolv.h"
#include "arpa/inet.h"
#include "unistd.h"
#include "string.h"
#include "stdlib.h"
//nossa porta
#define PORT    666

int main (int argc, char *argv[]){
   int serversock;
   struct sockaddr_in self;

   /* cria o socket */
   serversock = socket (AF_INET, SOCK_STREAM, 0) ;
   if ( serversock < 0 ){
      perror("Erro no socket");
      exit(1);
   }

   /* monta o endereço */
   bzero (&self, sizeof(self));
   self.sin_family = AF_INET;
   self.sin_port   = htons(PORT);
   self.sin_addr.s_addr = INADDR_ANY; /* uso o endereço do host */

   /* associa o endereço ao socket */
   if ( bind (serversock, (struct sockaddr*)&self, sizeof(self)) ) {
      perror("Erro no bind");
      exit(1);
   }

   /* coloca o socket em escuta */
   if ( listen (serversock, 20) ) {
      perror("Erro no listen");
      exit(1);
   }

   for(;;) {
      int clientsock;  
      struct sockaddr_in client_addr;
      int addrlen = sizeof (client_addr);
      char * resposta ;

      /* aguarda e aceita uma conexão externa */
      clientsock = accept (serversock, (struct sockaddr*)&client_addr, &addrlen);

      printf ("Client %s:%d connected\n",
              inet_ntoa (client_addr.sin_addr),
              ntohs (client_addr.sin_port));

      /* envia uma resposta HTTP padrão */
      <code>resposta = "HTTP/1.1 200 Ok\n\ncodigo em html\n" ;

      write (clientsock, resposta, strlen (resposta));

      /* fecha o socket da conexão recebida */
      close (clientsock);
   }

   /* encerra o socket servidor */
   close (serversock);

   return 0;
}

Vamos lá, compile:

gcc -o code code.c; ./code

Feito isso, abra navegador na url http://localhost:666

BINGO!

Veja que o servidor é simples se agente usar netcat para conectar, por exemplo, o servidor responde do mesmo jeito, não usa uma regex ou parser para identificar apenas navegadores.

nc localhost 666
HTTP/1.1 200 Ok

Sua página HTML vai estar aqui.

Se não me engano o servidor Apache usa "pcre.h" para fazer estas regex de verificação... Bom, nosso servidor funcionou, voltando para o HTTP e estudando suas documentações podemos até fazer um post ou um get enviando dados, porém não é nosso objetivo aqui lidar com bots e spiders de web.

Se quiser aí vai o link para te ajudar nos spiders e entender HTTP, boa sorte:
Se conseguir mandar POST para um link usando "client" com socket, mande e-mail do seu programa em C para eu ver. ;)

Pulo do gato para o pessoal que não gosta de criar a roda, quer criar seus bots e spiders de forma portável, com estas bibliotecas você pode usar POST, GET e fazer AUTH, extrair links, tarefas no FTP, fazer downloads...

Vamos continuar estudando sockets a fundo e deixar as bibliotecas que usam sockets para depois.

Página anterior     Próxima página

Páginas do artigo
   1. Introdução
   2. Explanação ao TCP/IP
   3. O que é socket
   4. Funções read(), send() e exemplo cliente HTTP
   5. Funções listen(), bind(), accept() e exemplo de servidor HTTP
   6. Na prática fazendo um FUZZER TCP
   7. Servidor e cliente com fork
   8. Servidor de comandos e scanner de portas
   9. Simple socket library
   10. Explanação ao UDP e exemplo de servidor e cliente
   11. Exemplo UDP Flood
   12. Portabilidade
   13. Conclusão
Outros artigos deste autor

Buffer Overflow: Entendendo e explorando

Bind: Explorando e evitando falhas

Módulos de Web no Perl

Usando o PF - Packet Filter

Ponteiros - Saindo de Pesadelos

Leitura recomendada

O Modelo de Referência OSI

Monitorando o consumo de banda com Bwbar

Controlando UPLOAD com o CBQ

Tutorial - Aplicação em C para transferência de arquivo usando socket TCP e Thread

Gerenciando pacotes para C/C++ com Conan

  
Comentários
[1] Comentário enviado por VonNaturAustreVe em 06/07/2010 - 03:24h

Excelente cara vou ler tudo :)

[2] Comentário enviado por removido em 06/07/2010 - 05:29h

Hey C00L3R,
Parabéns, é um ótimo artigo. Minhas dúvidas surgiram após meus testes.
Um abraço.

[3] Comentário enviado por andrezc em 06/07/2010 - 08:12h

Cara, realmente esse é um dos melhores artigos que eu já li por aqui. Parabéns.

[4] Comentário enviado por werneral em 06/07/2010 - 11:18h

Muito bom! Obrigado!

[5] Comentário enviado por uberalles em 06/07/2010 - 11:43h

verdadeira aula, velho. parabéns!
muito bom "resumão" do Unix Network programming. Nunca consegui fazer nada decente em sockets e esta tua aula deverá me ajudar muito.

[6] Comentário enviado por stremer em 07/07/2010 - 15:28h

para quem interessar, ha algum tempo atras escrevi um script mostrando como criar um robo http e enviei ao VOL.

http://www.vivaolinux.com.br/script/Robo-HTTP-usando-socket-e-codigo-multiplataforma

Interessante para quem esta aprendendo sockets...

OTIMO ARTIGO

[7] Comentário enviado por andrezc em 07/07/2010 - 15:43h

Opa stremer, eu cheguei a ver este seu script, realmente fabuloso.

Um abraço.

[8] Comentário enviado por fernandopinheiro em 07/07/2010 - 20:23h

Parabens, muito bom!!

[9] Comentário enviado por brunosolar em 08/07/2010 - 09:48h

Parabens realmente muito bom. So queria fazer um comentario sobra a parte do UDP flood. Sim hoje em dia qualquer firewal simples pode recusar este tipo de pacote.

No entanto dependendo da quantidade de pacotes UDP enviados (leia-se DDOS) você poderá ser "derrubado" pois o firewall irá gastar muito processador para descatar todos os pacotes. A melhor solução (AINDA) para ataques DOS / DDOS é diretamente no ISP onde eles irão setar no roteador o IP do atacante para /dev/null (exemplo). claro que voce corre o risco de perder algum cliente que faça parte da rede redirecionada.

No mais excelente trabalho.

[10] Comentário enviado por shazaum em 24/09/2010 - 11:44h

opa, no fuzzer faltou uma lib...

#include <netinet/in.h>

[11] Comentário enviado por thomasawrd em 18/07/2014 - 12:41h

parabéns cara excelente artigo,me ajudou muito.


Contribuir com comentário