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: 109.503 ]

Por: C00L3R_ em 06/07/2010 | Blog: http://bugsec.com.br


Explanação ao UDP e exemplo de servidor e cliente



O User Datagram Protocol (UDP) é um protocolo simples da camada de transporte. Ele é descrito na RFC 768 e permite que a aplicação escreva um datagrama encapsulado num pacote IPv4 ou IPv6, e então enviado ao destino. Mas não há qualquer tipo de garantia que o pacote irá chegar ou não.

O protocolo UDP não é confiável. Caso garantias sejam necessárias, é preciso implementar uma série de estruturas de controle, tais como timeouts, retransmissões, acknowlegments, controle de fluxo etc.

Cada datagrama UDP tem um tamanho e pode ser considerado como um registro indivisível, diferentemente do TCP, que é um protocolo orientado a fluxos de bytes sem início e sem fim.

Vou ilustrar:

    Cabecalho UDP 
        +-----------------------+-----------------------+ 
        |     Porta de origem   |   Porta de destino    |  \
        |       (16 bits)       |       (16 bits)       |   \
        +-----------------------+-----------------------+    -- 8 bytes
        |        Tamanho        |       Checksum        |   /
        |       (16 bits)       |       (16 bits)       |  /
        +-----------------------+-----------------------+ 
        |                                               |  \
       <                      Dados                      >  -- Max. 65507 bytes
        |                     (...)                     |  /    (teoricamente)
        +-----------------------------------------------+

Vamos a exemplos com linhas comentadas. Chega de teoria e vamos a prática, fazer um servidor:

#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#include "netdb.h"
#include "stdlib.h"
#include "stdio.h"
#include "unistd.h"
#include "string.h"

#define LOCAL_SERVER_PORT 6666
#define MAX_MSG 100

int main(int argc, char *argv[]) {
  
  int sd, rc, n, cliLen;
  struct sockaddr_in cliAddr, servAddr;
  char msg[MAX_MSG];

  // criamos o socket usando SOCK_DGRAM para UDP
  sd=socket(AF_INET, SOCK_DGRAM, 0);
  if(sd<0) {
    printf("%s: cannot open socket \n",argv[0]);
    exit(1);
  }

  // bind no local
  servAddr.sin_family = AF_INET;
  servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  servAddr.sin_port = htons(LOCAL_SERVER_PORT);
  rc = bind (sd, (struct sockaddr *) &servAddr,sizeof(servAddr));
  if(rc<0) {
    printf("%s: cannot bind port number %d \n",
      argv[0], LOCAL_SERVER_PORT);
    exit(1);
  }

  printf("%s: waiting for data on port UDP %u\n",
      argv[0],LOCAL_SERVER_PORT);

  // loop infinito pra escuta
  while(1) {
    
    // buffer
    memset(msg,0x0,MAX_MSG);


    // separando dados
    cliLen = sizeof(cliAddr);
    n = recvfrom(sd, msg, MAX_MSG, 0,
       (struct sockaddr *) &cliAddr, &cliLen);

    if(n<0) {
      printf("%s: cannot receive data \n",argv[0]);
      continue;
    }
      
    //mostrando dados
    printf("%s: from %s:UDP%u : %s \n",
      argv[0],inet_ntoa(cliAddr.sin_addr),
      ntohs(cliAddr.sin_port),msg);
    
  }

return 0;

}

Agora vamos ao cliente para o mesmo:

#include "sys/types.h"
#include "sys/socket.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#include "netdb.h"
#include "stdlib.h"
#include "stdio.h"
#include "unistd.h"
#include "string.h"
#include "sys/time.h"

#define REMOTE_SERVER_PORT 6666
#define MAX_MSG 100


int main(int argc, char *argv[]) {
  
  int sd, rc, i;
  struct sockaddr_in cliAddr, remoteServAddr;
  struct hostent *h;

  // checamos o argumento
  if(argc<3) {
    printf("usage : %s <server> <data1> ... <dataN> \n", argv[0]);
    exit(1);
  }

  // verificamos o Host
  h = gethostbyname(argv[1]);
  if(h==NULL) {
    printf("%s: unknown host '%s' \n", argv[0], argv[1]);
    exit(1);
  }

  printf("%s: sending data to '%s' (IP : %s) \n", argv[0], h->h_name,
    inet_ntoa(*(struct in_addr *)h->h_addr_list[0]));

  remoteServAddr.sin_family = h->h_addrtype;
  memcpy((char *) &remoteServAddr.sin_addr.s_addr,
    h->h_addr_list[0], h->h_length);
  remoteServAddr.sin_port = htons(REMOTE_SERVER_PORT);

  // criamos nossa socket repare só o SOCK_DGRAM isso por que vamos usar UDP
  sd = socket(AF_INET,SOCK_DGRAM,0);
  if(sd<0) {
    printf("%s: cannot open socket \n",argv[0]);
    exit(1);
  }
  
  // bind na porta
  cliAddr.sin_family = AF_INET;
  cliAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  cliAddr.sin_port = htons(0);
  
  rc = bind(sd, (struct sockaddr *) &cliAddr, sizeof(cliAddr));
  if(rc<0) {
    printf("%s: cannot bind port\n", argv[0]);
    exit(1);
  }


  // mandamos os dados
  for(i=2;i<argc;i++) {
    rc = sendto(sd, argv[i], strlen(argv[i])+1, 0,
      (struct sockaddr *) &remoteServAddr,
      sizeof(remoteServAddr));

    if(rc<0) {
      printf("%s: cannot send data %d \n",argv[0],i-1);
      close(sd);
      exit(1);
    }

  }
  
  return 1;

}

Rode os dois programas, um em cada terminal e veja o funcionamento. Tente entender o que está ocorrendo, não é muito difícil, já que o mesmo está comentado linha por linha...

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

Ponteiros - Saindo de Pesadelos

Buffer Overflow: Entendendo e explorando

Bind: Explorando e evitando falhas

Apache + PHP + MySQL + ftpd no OpenBSD

Usando OpenBSD como desktop

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

Controlando UPLOAD com o CBQ

  
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




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner
Linux banner
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts