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

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


Portabilidade



Estes programas mostrados até agora não rodam no Windows, mas por quê disso? Windows usa um fork de socket chamado "winsock", com umas frescuras para Windows, muitos colegas fizeram artigos só falando de winsock detalhadamente, mas aqui vou mostrar apenas para deixar seu código portável tanto para Windows como para Unix like.

Para começar você pode deixar uma condição no seu código como exemplo:

#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
#  include "windows.h"
// aqui versão do winsock 2.0...
#  include "winsock2.h"
#else
#  include "sys/socket.h"
#endif

Um paradigma que podemos seguir quando usamos winsock seria usar a função "WSAStartup" para iniciar nossa "winsock", e temos como exemplo:

WSADATA wsa;
// definido versão 2.0 da winsock comum em todos os windows só não sei no
// windows 7 mais deve funcionar.
WSAStartup(MAKEWORD(2,0),&wsa);
//e para declarar o uso de uma socket
SOCKET sock;

Uma ideia fascinante para melhorar a tarefa, uma função void:

#ifdef _WIN32
static void init_winsock()
{
WORD   wVersion = MAKEWORD(2,2);
WSADATA   wsaData;
DWORD   err;

   if ( (err = (DWORD)WSAStartup(wVersion, &wsaData)) != 0 )
   {
      fprintf(stderr, "ERROR: got err#%d from Winsock init\n", err);
      exit(EXIT_FAILURE);
   }

}
#endif

// podemos chamar então desta forma
#ifdef _WIN32
   init_winsock();
#endif

Você vai usar winsock como se fosse socket do padrão Unix, porém usando estas condições. Bom lembrar, se for portar um exploit remoto, terá que refazer tal exploit para o sistema operacional a ser portado porque endereços de memória mudam agora para scanners, spiders, programas corporativos... seria uma boa!

Exemplo final um código portável que tem como função fazer "whois".

/* fwhois.c by Ang3ldust
* Networking code taken and modified from net.c in the finger source code
*
* Added :port notation to the hostname
*/

#include "stdio.h"
#include "string.h"
#include "ctype.h"
#include "stdlib.h"
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
#  include "windows.h"
#  include "winsock2.h"
#  include "io.h"
#else
#  include "sys/types.h"
#  include "sys/socket.h"
#  include "netinet/in.h"
#  include "netdb.h"
#  define __cdecl        
#endif
#ifndef _WIN32
#  define SOCKET         int
#  define closesocket(fd)   close(fd)
#endif

static unsigned int portno = 43;      
static char         whobuf[256];

static void netfinger(char *name);

int __cdecl main(int argc, char **argv)
{
   if ( argc != 2 )
   {
      printf("usage: fwhois user[@<whois.server> [:portno] ]\n");
      exit(1);
   }

   strcpy(whobuf, argv[1]);

   if ( strchr(whobuf, '@') == 0 )
      strcat(whobuf, "@whois.internic.net");

   netfinger(whobuf);

   exit(EXIT_SUCCESS);
}

#ifdef _WIN32
static void init_winsock()
{
WORD   wVersion = MAKEWORD(2,2);
WSADATA   wsaData;
DWORD   err;

   if ( (err = (DWORD)WSAStartup(wVersion, &wsaData)) != 0 )
   {
      fprintf(stderr, "ERROR: got err#%d from Winsock init\n", err);
      exit(EXIT_FAILURE);
   }

}
#endif

static void netfinger(char *name)
{
   int      nr;
   char *pport;
   char  iobuf[256];
   register int lastc = 0;
   struct in_addr defaddr;
   struct hostent *hp, def;
   struct sockaddr_in sin;
   SOCKET s;
   char *alist[1], *host;

   if ((host = strrchr(name, '@')) == 0 )
   {
      fprintf(stderr, "ERROR: don't know what host to query\n");
      return;
   }

   *host++ = '\0';  

   if ( (pport = strchr(host, ':')) != 0 )
   {
      *pport++ = '\0';
      portno = atoi(pport);
   }

#ifdef _WIN32
   init_winsock();
#endif

   if (!(hp = gethostbyname(host)))
   {
      defaddr.s_addr = inet_addr(host);
      if (defaddr.s_addr == -1)
      {
         fprintf(stderr, "fwhois: unknown host: %s\n",host);
         return;
      }
      def.h_name = host;
      def.h_addr_list = alist;
      def.h_addr = (char *)&defaddr;
      def.h_length = sizeof(struct in_addr);
      def.h_addrtype = AF_INET;
      def.h_aliases = 0;
      hp = &def;
   }


   if ( portno == 0 )
   {
   struct servent *sp;

      if ( (sp = getservbyname("whois", "tcp")) == 0 )
      {
         fprintf(stderr, "fwhois: tcp/whois: unknown service\n");
         return;
      }

      portno = ntohs(sp->s_port);
   }

   sin.sin_family = hp->h_addrtype;

   memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);

   sin.sin_port = htons((short)portno);

#ifdef _WIN32
   if ((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) == INVALID_SOCKET )
#else
   if ((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
#endif
   {
      perror("fwhois: socket");
      return;
   }


   printf("[%s]\n", hp->h_name);

   if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
   {
      perror("fwhois: connect");
      closesocket(s);
      return;
   }

   nr = sprintf(iobuf, "%s\r\n", name);

   send(s, iobuf, nr, 0);

   while ( (nr = recv(s, iobuf, sizeof iobuf, 0)) > 0 )
   {
      char *maxread = iobuf + nr;
      char *p       = iobuf;

      while ( p < maxread )
      {
         int   c = *p++ & 0x7F;

         if (c == '\r')
         {
            lastc = '\r';
            c = '\n';
         }
         else
         {
            if (!isprint(c) && !isspace(c))
               c |= 0x40;
            if (lastc != '\r' || c != '\n')
               lastc = c;  
            else
            {
               lastc = '\n';
               continue;
            }
         }
         putchar(c);
      }
   }

   if (lastc != '\n')
      putchar('\n');
   fflush(stdout);
}

Para testar:

./programa endereço_web

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

Usando o NetBSD como desktop

Bind: Explorando e evitando falhas

Apache + PHP + MySQL + ftpd no OpenBSD

Módulos de Web no Perl

Leitura recomendada

Controlando UPLOAD com o CBQ

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

Monitorando o consumo de banda com Bwbar

O Modelo de Referência OSI

Estudo de ponteiros para GNU/Linux

  
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

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts