Mostrando entradas con la etiqueta Redes. Mostrar todas las entradas
Mostrando entradas con la etiqueta Redes. Mostrar todas las entradas

miércoles, 25 de enero de 2012

Sockets TCP con Bash

Es muy habitual, cuando se escriben scripts en Bash, recurrir a herramientas como "netcat" para realizar comunicaciones de red. Sin embargo, aunque no es muy conocido, Bash soporta sockets.

Veamos como conectarnos, por ejemplo, a google.
Abrimos el socket y le asociamos el descriptor 3. Recordemos que 0, 1 y 2 se usan habitualmente para la entrada y salida estándard y para la salida de error.

$ exec 3<>/dev/tcp/www.google.com/80

A continuación, enviamos un mensaje por el socket:

$ echo "GET /" >&3

Y finalmente, leemos la respuesta del servidor:

$ cat <&3

HTTP/1.0 302 Found
Location: http://www.google.es/
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Set-Cookie: PREF=ID=a38d7ed8770f70cb:FF=0:TM=1327418982:LM=1327418982:S=0tgHEV7GxJaC6Wl7; expires=Thu, 23-Jan-2014 15:29:42 GMT; path=/; domain=.google.com
Date: Tue, 24 Jan 2012 15:29:42 GMT
Server: gws
Content-Length: 218
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
... 


miércoles, 22 de abril de 2009

Evitar timeouts en conexiones ssh

 
echo "ServerAliveInterval 60" >> ~/.ssh/config

Y se acabaron los timeouts por inactividad.

Gracias Jordi!

viernes, 6 de febrero de 2009

Cómo agilizar conexionex SSH

Dejo aquí una pequeña receta que permite agilizar las conexiones ssh, de manera que solo pedirá la contraseña la primera vez. A partir de ese momento las conexiones usarán el mismo canal multiplexado.

Solo hay que añadir en ~/.ssh/config lo siguiente:

Host *
ControlMaster auto
ControlPath ~/.ssh/master-%r@%h:%p

sábado, 22 de noviembre de 2008

Multicast sender/listener

Dejo un ejemplo de como hacer multicast en C/Linux.


sender.c - el envío de mensajes



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>


#define PORT 9999
#define GROUP "225.0.0.37"

int main(int argc, char *argv[])
{
struct sockaddr_in addr;
int fd;
char *message="ieps!";

if((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0)
{
perror("socket()");
exit(EXIT_FAILURE);
}

memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr(GROUP);
addr.sin_port=htons(PORT);

while(1)
{
if(sendto(fd, message, strlen(message),
0, (struct sockaddr *) &addr,
sizeof(addr)) < 0)
{
perror("sendto()");
exit(EXIT_FAILURE);
}

sleep(1);
}

return 0;
}



listener.c - recepción de mensajes

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>


#define PORT 9999
#define GROUP "225.0.0.37"

int main(int argc, char *argv[])
{
struct sockaddr_in addr;
int fd, nbytes;
unsigned int addrlen;
struct ip_mreq mreq;
char msgbuf[256];
u_int yes=1;

if((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0)
{
perror("socket");
exit(1);
}

if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
&yes, sizeof(yes)) < 0)
{
perror("setsockopt() SO_REUSEADDR");
exit(1);
}

memset(&addr, 0, sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(PORT);

if(bind(fd,(struct sockaddr *) &addr,
sizeof(addr)) < 0)
{
perror("bind");
exit(1);
}

mreq.imr_multiaddr.s_addr=inet_addr(GROUP);
mreq.imr_interface.s_addr=htonl(INADDR_ANY);

if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq)) < 0)
{
perror("setsockopt");
exit(1);
}


while(1)
{
addrlen=sizeof(addr);
if ((nbytes=recvfrom(fd, msgbuf,
sizeof(msgbuf), 0, (struct sockaddr *)
&addr, &addrlen)) < 0)
{
perror("recvfrom");
exit(1);
}

puts(msgbuf);
}

return 0;
}

sábado, 31 de mayo de 2008

Sockets en shellscript con xinetd

Existe la posibilidad de crear aplicaciones sencillas "tipo servidor" con scripts de shell. Vamos a ver un ejemplo en el que se configurará el daemon xinetd para abrir un socket y poner un programa a la escucha. En nuestro caso el programa en cuestión será un script, aunque podría utilizarse cualquier otro lenguaje de programación.

Configurando Xinetd:
Generalmente disponcremos de un archivo de configuración de xinetd similar al siguiente:


$ cat /etc/xinetd.conf
#
# Simple configuration file for xinetd
#
# Some defaults, and include /etc/xinetd.d/

defaults
{
instances               = 60
log_type                = SYSLOG authpriv
log_on_success          = HOST PID
log_on_failure          = HOST
cps                     = 25 30
}
includedir /etc/xinetd.d

Este indica la inclusión de los scripts situados en /etc/xinet.d/.


ls -lh /etc/xinetd.d/
total 100K
-rw-r--r--    1 root     root          563 nov 19 17:53 chargen
-rw-r--r--    1 root     root          580 nov 19 17:53 chargen-udp
-rw-r--r--    1 root     root          419 nov 19 17:53 daytime
-rw-r--r--    1 root     root          438 nov 19 17:53 daytime-udp
-rw-r--r--    1 root     root          341 nov 19 17:53 echo
-rw-r--r--    1 root     root          360 nov 19 17:53 echo-udp
-rw-r--r--    1 root     root          318 jun 23  2002 finger
-rw-r--r--    1 root     root          370 sep  1  2002 imap
-rw-r--r--    1 root     root          365 sep  1  2002 imaps
-rw-r--r--    1 root     root          453 sep  1  2002 ipop2
-rw-r--r--    1 root     root          359 sep  1  2002 ipop3
-rw-r--r--    1 root     root          275 jul  5  2002 ntalk
-rw-r--r--    1 root     root          335 sep  1  2002 pop3s
-rw-r--r--    1 root     root          361 jun 24  2002 rexec
-rw-r--r--    1 root     root          378 jun 24  2002 rlogin
-rw-r--r--    1 root     root          431 jun 24  2002 rsh
-rw-r--r--    1 root     root          317 jun 25  2002 rsync
-rw-r--r--    1 root     root          312 nov 19 17:53 servers
-rw-r--r--    1 root     root          314 nov 19 17:53 services
-rw-r--r--    1 root     root          392 ago 11  2002 sgi_fam
-rw-r--r--    1 root     root          263 jul  5  2002 talk
-rw-r--r--    1 root     root          305 jul 23  2002 telnet
-rw-r--r--    1 root     root          497 nov 19 17:53 time
-rw-r--r--    1 root     root          518 nov 19 17:53 time-udp
-rw-r--r--    1 root     root          276 ago 16  2002 vsftpd


Editando cualquiera de estos archivos puede verse la estructura que siguen los scripts. Para nuestro propósito debemos crear uno de estos scritps, por ejemplo el siguiente:


service nuevoservicio
{
port            = 6666
socket_type     = stream
wait            = no
user            = nobody
server          = /usr/local/bin/NuestroServidor.sh
log_on_success  += USERID
log_on_failure  += USERID
disable         = no
}


El puerto escogido para la escucha debe estar especificado en el archivo /etc/services, en nuestro caso, el puerto 6666, no está especificado, por lo tanto añadimos la siguiente línea a /etc/services:


nuevoservicio 6666/tcp   # Nuestro servicio de prueba

Sencillo, ¿no?.

Ahora solo necesitamos crear el script "NuestroServidor.sh" y colocarlo en /usr/local/bin.

Podemos probar con el clásico:


#!/bin/bash

echo "Hello World!"

Para que el servidor funcione correctamente debemos asegurarnos de que dispone de permisos de ejecución para todos los usuarios.

finalmente reiniciamos xinetd:


$ /etc/rc.d/init.d/xinetd restart
Parando xinetd:                                            [  OK  ]
Iniciando xinetd:                                          [  OK  ]


Podemos probar con:


$ telnet localhost 6666
Trying 127.0.0.1...
Connected to hackerbox (127.0.0.1).
Escape character is '^]'.
Hello World!
Connection closed by foreign host.


El programa llamado por xinetd podría haberse desarrollado en cualquier otro lenguaje de programación: C, Perl ... Lo interesante de esta forma de hacerlo es que permite la creación de servicios simples en un tiempo récord. Aunque no se debe olvidar que en estos sistemas la seguridad no es uno de sus fuertes. Con cuidado!

domingo, 25 de mayo de 2008

Enrutamiento básico en Linux

Actualmente existen gran cantidad de herramientas basadas en GNU/Linux, *BSD y similares que permiten montar un router de una forma más o menos sencilla. Algunas de ellas incluso tienen bonitas interfaces gráficas que nos facilitan la tarea. Si nos movemos al terreno comercial, la oferta crece.

Sin embargo en este artículo no vamos a tratar sobre estas herramientas si no sobre los cimientos que las sustentan. Vamos a ver como usar una máquina Linux para las tareas básicas de enrutamiento.

Lo primero que necesitaremos es configurar las interfaces de red. Para esto utilizaremos la herramienta "ifconfig" disponible en cualquier SO Unix. A continuación tendremos que establecer rutas; utilizaremos "route". Y finalmente, con el daemon "routed" configuraremos el protocolo de enrutamiento RIP.


La herramienta ifconfig
Para configurar las interfaces del sistema utilizaremos la herramienta "ifconfig". Esta herramienta viene con cualquier SO de la familia Unix, y suele estar ubicada en "/sbin". Los parámetros para configurar una interfaz mediante "ifconfig" son los siguientes:

/sbin/ifconfig [interface] [IP] netmask [máscara] broadcast [broadcast]

Veamos un ejemplo, configuremos la interface eth0 con la IP 192.168.1.2:

/sbin/ifconfig eth0 192.168.1.2 netmask 255.255.255.0 broadcast 192.168.1.255

La herramienta route
De la misma forma que utilizamos "ifconfig" para configurar las interfaces, disponemos de "route", para configurar rutas. La herramienta "route", ubicada en "/sbin" recibe los siguientes parámetros:

/sbin/route -[net|host] [IP] netmask [gw [IP]] dev [interface]

Una forma típica de llamara a "route" es la utilizada para configurar un gateway por defecto. Veamos la forma de hacerlo:

/sbin/route add -net 0.0.0.0 netmask 0.0.0.0 gw 192.168.1.1

Hemos añadimo una ruta de manera que los paquetes destinados a cualquier red (0.0.0.0) se dirijan al gateway 192.168.1.1. Hay otra forma de hacerlo más utilizada:

/sbin/route add default gw 192.168.1.1

Entendiendo como "default": "-net 0.0.0.0 netmask 0.0.0.0".

Hemos configurado un gateway por defecto. Ahora configuremos una ruta normal. Por ejemplo, enrutemos todos los paquetes destinados a 192.168.78.0 al gateway 192.168.1.1:

/sbin/route add -net 192.168.78.0 netmask 255.255.255.0 gw 192.168.1.1

El daemon routed
A diferencia de lo que pasaba con "ifconfig" y "route", el daemon "routed" no viene instalado por defecto en el SO Linux. Pero no es difídil de conseguir e instalar.

Una vez instalado se ubica en "/sbin" o en "/usr/sbin". Ejecutarlo es fácil:

/usr/sbin/routed

Pero, ¿para que sirver routed?

El daemon "routed" es la implementación del protocolo RIP (Routing Information Protocol). La finalidad del protocolo RIP consiste en comunicar la estructura de la red a los routers vecinos. Esto soluciona el problema del mantenimiento de rutas en redes grandes, donde sería del todo imposible añadir y borrar rutas en función del estado de la red. Script de ejemplo
Según lo que se ha visto hasta ahora, configurar el enrutamiento básico en Linux es de lo más sencillo. Primero configuramos las interfaces, a continuación habilitamos un ruta por defecto y finalmente arrancamos el demonio "routed". Veamos un Script de ejemplo:

#!/sbin/bash

# Habilitamos la redirección de paquetes
echo 1 > /proc/sys/net/ipv4/ip_forward

# Configuramos las interfaces
/sbin/ifconfig eth0 192.168.1.2 netmask 255.255.255.0 broadcast 192.168.1.255
/sbin/ifconfig eth1 192.168.2.1 netmask 255.255.255.0 broadcast 192.168.2.255
/sbin/ifconfig eth2 192.168.3.1 netmask 255.255.255.0 broadcast 192.168.3.255

# Gateway por defecto
/sbin/route add default gw 192.168.1.1

# Arrancamos routed
/usr/sbin/routed


miércoles, 31 de octubre de 2007

Introducción a las redes Tor

Tor es un cojunto de herramientas que pretende conseguir el anonimato online. Usa una red de máquinas o nodos a través de los cuales enruta el tráfico de red, tal y como se muestra en el esquema siguiente:



Como se ve en el dibujo una conexión a través de la red Tor usa tres nodos intermediarios entre el origen y el destino. De esta manera la máquina destino que recibe la conexión solo tendrá acceso a la IP del último nodo.

Tor ofrece una interfaz SOCKS a las aplicaciones, por lo que cualquier aplicación preparada para usar SOCKS podrá utilizar la red Tor sin problemas.
Sin embargo, no es necesario que una aplicación disponga de soporte para SOCKS, dado que Tor distribuye un script llamado "torify" que permite que cualquier aplicación use la red tor.

Este script emplea la herramienta tsocks para permitir que una aplicación use SOCKS de forma transparente.


Para que un usuario pueda conectarse a la red Tor necesita obtener un listado de nodos de la red. Este proceso, como indica el dibujo, se realiza sin cifrar.



A continuación se empieza a crear el circuito por el que viajaran los datos en la red Tor. Cada servidor conoce únicamente al servidor que le proporciona los datos y al servidor al que se los envía. por lo que ninguna máquina conoce el recorrido completo. Además, en cada tramo, los servidores afectados negocian claves diferentes, impidiendo que las conexiones sean rastreadas.



Una vez el circuito ha sido establecido se utilizará durante unos diez minutos. Posteriormente se creará un circuito nuevo.







La forma de instalar Tor depende en gran medida del sistema operativo utilizado, por lo que para instalarlo lo mejor es partir de la documentación oficial.

Una vez instalado su uso es sencillo. Por ejemplo, podríamos torificar la aplicación netcat con el comando siguiente:
$ torify nc www.google.com 80

Observemos el funcionamiento de Tor. Si no usamos el comando "torify" vemos que estamos realizando una conexión directa a google.
$ nc www.google.com 80

# En otro terminal
$  netstat  | grep ESTABLISHED
tcp        0      0 192.168.1.69:34362          nf-in-f99.google.com:http   ESTABLISHED


Sin embargo, torificando netcat los resultados son diferentes.
$ torify nc www.google.com 80

# En otro terminal
$ netstat  | grep ESTABLISHED
tcp        0      0 192.168.1.69:34352          tor.outra.net:9090          ESTABLISHED
tcp        0      0 192.168.1.69:34351          cyberphunk.eu:9001          ESTABLISHED


Queda claro que no saltamos directamente al servidor de google sino que entramos en la red Tor. Veamos ahora con que dirección IP llegamos a nuestro destino. Necesitaremos una web que nos diga la dirección IP (http://www.vermiip.es/) y otra que nos diga la localización geográfica (http://www.ip2location.com/free.asp),

Esto nos permitirá obtener la IP con el comando:
$ torify lynx --dump http://www.vermiip.com 2>/dev/null | grep "Tu ip" | cut -c 18-30
85.214.63.25

Y su localización geográfica:
$ torify lynx --dump http://www.ip2location.com/free.asp?ipaddresses=85.214.63.25 \
2>/dev/null | grep "85.214.63.25\|mapit" |  sed -e 's/\[.*\]//'
85.214.63.25 DE GERMANY
BERLIN BERLIN STRATO RECHENZENTRUM BERLIN


De esta manera podemos construir un sencillo script que nos de el listado de nodos de salida de Tor.
$ cat  print_tor_nodes.sh
#!/bin/bash

while true; do

IP=`torify lynx --dump http://www.vermiip.com 2>/dev/null | grep "Tu ip" | cut -c 18-30`;

DATA=`torify lynx --dump http://www.ip2location.com/free.asp?ipaddresses="$IP" \
2>/dev/null | grep "$IP\|mapit" |  sed -e 's/\[.*\]//'`

echo $DATA
sleep 300

done


Del que extraemos el siguiente listado, que nos permite ver los nodos que nos dan acceso a Internet en último lugar.
208.64.29.34 US UNITED STATES NEW YORK NEW YORK R & D TECHNOLOGIES LLC
217.20.117.1 DE GERMANY BERLIN BERLIN NETDIREKT E. K
128.197.11.3 US UNITED STATES MASSACHUSETTS BOSTON BOSTON UNIVERSITY
88.191.29.92 FR FRANCE ILE-DE-FRANCE PARIS DEDIBOX SAS
88.191.25.27 FR FRANCE ILE-DE-FRANCE PARIS DEDIBOX SAS
89.149.207.1 DE GERMANY BERLIN BERLIN NETDIREKT E.K
87.234.124.1 DE GERMANY BERLIN BERLIN QSC AG DYNAMIC IP ADDRESSES
88.191.29.92 FR FRANCE ILE-DE-FRANCE PARIS DEDIBOX SAS
87.234.124.1 DE GERMANY BERLIN BERLIN QSC AG DYNAMIC IP ADDRESSES
88.191.29.92 FR FRANCE ILE-DE-FRANCE PARIS DEDIBOX SAS
128.197.11.3 US UNITED STATES MASSACHUSETTS BOSTON BOSTON UNIVERSITY
75.72.79.46 US UNITED STATES
203.26.16.68 AU AUSTRALIA NEW SOUTH WALES WEST WYALONG TPG INTERNET PTY LTD
88.191.11.18 FR FRANCE ILE-DE-FRANCE PARIS DEDIBOX SAS
88.198.50.14 DE GERMANY - - HETZNER-RZ-NBG-NET
83.243.80.77 DE GERMANY BERLIN BERLIN SERVERCREW LTD. PI
88.191.29.92 FR FRANCE ILE-DE-FRANCE PARIS DEDIBOX SAS
209.160.32.1 US UNITED STATES DISTRICT OF COLUMBIA WASHINGTON HOPONE INTERNET CORPORATION
60.167.39.24 CN CHINA BEIJING BEIJING CHINANET ANHUI PROVINCE NETWORK
83.217.66.50 BE BELGIUM OOST-VLAANDEREN DENDERMONDE NMV-CUST-DIGITALROOT
128.2.141.33 US UNITED STATES PENNSYLVANIA PITTSBURGH CARNEGIE MELLON UNIVERSITY83.171.182.9 DE GERMANY BAYERN NüRNBERG MNET TELEKOMMUNIKATION GMBH
85.214.63.25 DE GERMANY BERLIN BERLIN STRATO RECHENZENTRUM BERLIN
74.208.46.15 US UNITED STATES NEW YORK NEW YORK 1&1 INTERNET INC
122.145.8.19 JP JAPAN - - FREEBIT CO. LTD
71.226.252.2 US UNITED STATES PENNSYLVANIA WEST CHESTER COMCAST CABLE COMMUNICATIONS INC
83.233.181.9 SE SWEDEN - - PROVIDER LOCAL REGISTRY
166.70.207.2 US UNITED STATES UTAH SALT LAKE CITY XMISSION
195.71.90.10 DE GERMANY - - PROVIDER LOCAL REGISTRY
...



Respecto al script anterior, realiza un sleep de cinco minutos antes de reintentar, lo que hace el procese extremadamente lento. Si se reinicia Tor antes de cada petición el circuito se crea de nuevo y se obtiene un nodo diferente. De esta manera puede suprimirse el sleep del script.



En el momento de escribir este artículo la red Tor es relativamente pequeña, lo que disminuye notablemente el grado de anonimato. Aunque, sin duda, tiene un futuro prometedor.


Referencias:
- Tor Project: http://www.torproject.org


domingo, 6 de mayo de 2007

Interfaz de red en Linux

En el siguiente ejemplo se muestra como acceder a la información de las interfaces de Linux en C:

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

#include <sys/socket.h>
#include <sys/time.h>
#include <malloc.h>
#include <ctype.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <netinet/tcp.h>
#include <net/if.h>
#include <sys/ioctl.h>

#define ETH_P_ARP  0x0806

/* Funcion de utiliadad para pasar una ip en formato u_int32_t a un char* */
char *inetaddr ( u_int32_t ip )
{
struct in_addr in;
in.s_addr = ip;
return inet_ntoa(in);
}

int main ()
{
struct ifreq if_data;
int sockd;
u_int8_t  local_mac[6];
u_int32_t local_ip;
u_int32_t local_netmask;
u_int32_t local_broadcast;
u_int32_t ip;

/* Son necesarios privilegios de root */
if (getuid () != 0)
{
perror ("You must be root. \n");
exit (0);
}

/* Crea el socket */
if ((sockd = socket (AF_INET, SOCK_PACKET, htons (ETH_P_ARP))) < 0)
{
perror("socket");
exit (0);
}

/* Interfaz eth0 */
strcpy (if_data.ifr_name, "eth0");

/* Obtiene la MAC address */
if (ioctl (sockd, SIOCGIFHWADDR, &if_data) < 0)
{
perror ("ioctl(): SIOCGIFHWADDR \n");
exit(EXIT_FAILURE);
}
memcpy (local_mac, if_data.ifr_hwaddr.sa_data, 6);
printf ("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
local_mac[0], local_mac[1], local_mac[2],
local_mac[3], local_mac[4], local_mac[5]);

/* Obtiene la IP address */
if (ioctl (sockd, SIOCGIFADDR, &if_data) < 0)
{
perror ("ioctl(); SIOCGIFADDR \n");
exit(EXIT_FAILURE);
}
memcpy ((void *) &ip, (void *) &if_data.ifr_addr.sa_data + 2, 4);
local_ip = ntohl (ip);
printf ("IP: %s\n", inetaddr(ip));

/* Obtiene la mascara de red */
if (ioctl (sockd, SIOCGIFNETMASK, &if_data) < 0)
{
perror ("ioctl(): SIOCGIFNETMASK \n");
exit(EXIT_FAILURE);
}
memcpy ((void *) &ip, (void *) &if_data.ifr_netmask.sa_data + 2, 4);
local_netmask = ntohl (ip);
printf ("NETMASK: %s\n", inetaddr(ip));

/* Obtiene la direccion de broadcast */
if (ioctl (sockd, SIOCGIFBRDADDR, &if_data) < 0)
{
perror ("ioctl(): SIOCGIFBRDADDR \n");
exit(EXIT_FAILURE);
}
memcpy ((void *) &ip, (void *) &if_data.ifr_broadaddr.sa_data + 2, 4);
local_broadcast = ntohl (ip);
printf ("BROADCAST: %s\n", inetaddr(ip));

return (0);
}



Sockets TCP y UDP

A continuación pego dos ejemplos de como usar sockets TCP y UDP.


Cliente TCP:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>

/*
* Ejemplo de Sockets cliente TCP
*/
int main () 
{

/* Datos de conexion */
char *host = "localhost";
int port = 9999;

/* Cadena a enviar */
char *data = "Cadena de prueba";

/* Obtenemos el host */
struct hostent *host_name;
if ((host_name = gethostbyname(host))==0) 
{
perror ("gethostbyname()");
exit (EXIT_FAILURE);
}

/* Configura el socket */
struct sockaddr_in pin; 
bzero (&pin, sizeof(pin));
pin.sin_family =  AF_INET;
pin.sin_addr.s_addr = htonl(INADDR_ANY);
pin.sin_addr.s_addr = ((struct in_addr *)(host_name->h_addr))->s_addr;
pin.sin_port = htons (port);


/* Crea un socket TCP */
int socket_descriptor = socket (AF_INET, SOCK_STREAM, 0);
if (socket_descriptor == -1) 
{ 
perror ("socket()");
exit (EXIT_FAILURE);
}


/* Conecta con el servidor */
if (connect(socket_descriptor, (void *)&pin, sizeof(pin))==-1) 
{
perror ("connect()");
exit (EXIT_FAILURE);
}

/* Envia los datos al servidor */
if (send(socket_descriptor, data, strlen(data), 0) == -1) 
{
perror ("send()");
exit (EXIT_FAILURE);
}

/* Lee la respuesta */
static char buffer [2048];
if (recv(socket_descriptor, buffer, sizeof(buffer), 0) == -1) {

perror ("recv()");
exit (EXIT_FAILURE);
}

/* Datos recibidos */
printf ("%s\n", buffer);
}







Servidor TCP:


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

#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>

#define BUFFER_SIZE 1024

/*
* Ejemplo Socket servidor TCP
*/
int main() 
{

/* Descriptor del socket */
int socket_descriptor;

/* Puerto al que escuchara el servidor */
int port = 9999;


/* Configuracion del socket */
struct sockaddr_in sin; 

/* Crea un socket TCP */
socket_descriptor = socket (AF_INET, SOCK_STREAM, 0);
if (socket_descriptor == -1) 
{
perror("socket()");
exit(EXIT_FAILURE);
}

/* Configura el socket */
bzero (&sin, sizeof(sin));
sin.sin_family =  AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons (port);

/* Une el socket al puerto X */
if (bind(socket_descriptor,(struct sockaddr *)&sin,sizeof(sin)) == -1) 
{ 
perror("bind()");  
exit(EXIT_FAILURE);
}

/* Configura la cola del socket */
if (listen(socket_descriptor, 20) == -1) 
{
perror("listen()");  
exit(EXIT_FAILURE);
}


/* Para obtener la configuracion del socket */
struct sockaddr_in pin;
int address_size;

/* Descriptor del socket temporal */
int temp_socket_descriptor;

/* Buffer de recepcion  */
char buffer[BUFFER_SIZE];

/* Bucle principal de recepcion y envio de paquetes */
while (1) 
{
/* Limpieza del buffer */
memset (buffer, '\0', BUFFER_SIZE);

/* Aceptamos la conexion */
temp_socket_descriptor = 
accept(socket_descriptor,(struct sockaddr*)&pin,&address_size);

if (temp_socket_descriptor == -1) 
{ 
perror("accept()");  
exit(EXIT_FAILURE);
}

/* Recibimos datos */
if (recv(temp_socket_descriptor,buffer,sizeof(buffer),0)==-1) 
{ 
perror("recv()");  
exit(EXIT_FAILURE);
}

printf ("Recibido: %s\n",buffer); 

/* Enviamos datos */
if (send(temp_socket_descriptor, "Recibido\n", 9,0) == -1) 
{ 
perror("send()");  
exit(EXIT_FAILURE);
}

/* Cierre del descriptor de archivo */
close(temp_socket_descriptor);
}
}




Paquetes UDP:

#include <stdio.h>

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <netinet/ip.h>
#include <netinet/udp.h>


int main(void) 
{
/* socket */
int sock;

/* Tamaño del paquete UDP */
unsigned int buffer_size = sizeof(struct iphdr) + sizeof(struct udphdr);

/* Buffer de tamaño suficiente para un paquete UDP */
unsigned char buffer[buffer_size];
memset (buffer, 0, buffer_size);

/* Cabecera IP */
struct iphdr *ip = (struct iphdr *)buffer;

/* Cabecera UDP */
struct udphdr *udp = (struct udphdr *)(buffer + sizeof(struct iphdr));

/* Crea el socket */
if ((sock = socket(AF_INET,SOCK_RAW,IPPROTO_UDP)) == -1) 
{ 
perror("socket()"); 
exit(EXIT_FAILURE); 
}

/* Establece las opciones del socket */
int o = 1;
if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&o,sizeof(o)) == -1) 
{ 
perror("setsockopt()"); 
exit(EXIT_FAILURE); 
}

/* Rellena la cabecera IP */
ip->version = 4;
ip->ihl = 5;
ip->id = htonl(random());
ip->saddr = inet_addr("1.2.3.4");
ip->daddr = inet_addr("1.2.3.4");
ip->ttl = 255;
ip->protocol = IPPROTO_UDP;
ip->tot_len = buffer_size;
ip->check = 0;

/* Rellena la cabecera UDP */
udp->source = htons(1234);
udp->dest = htons(1234);
udp->len = buffer_size;
udp->check = 0; /* falta calcular checksum  */


struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = udp->source;
addr.sin_addr.s_addr = ip->saddr;

/* Envio del paquete */
if ((sendto(sock, buffer, buffer_size, 0, (struct sockaddr*)&addr,
sizeof(struct sockaddr_in))) == -1) 
{
perror("send()");
exit(1);
}

return 0;
}


Raw Sockets

Todos los lenguajes de programación disponen de una manera más o menos sencilla de realizar conexiones entre un cliente y un servidor. Como mínimo todos ofrecen la posibilidad de utilizar sockets TCP y UDP. Algunos incluso ICMP. Pero cuando deseamos disponer de un acceso total al sistema de red, no hay otro remedio que acceder a la API de red con un lenguaje como C. Este acceso permite cosas como enviar paquetes ICMP, ARP, o cualquier otro totalmente personalizado, modificando cualquier flag o dato de la cabecera. Incluso, si lo deseamos, podemos crear nuestro propio protocolo.

Como primer ejemplo enviaremos un paquete ARP. Este es el formato de las cabeceras ARP y Ethernet:

ETHERNET HEADER:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Ethernet destination address (first 32 bits)           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Ethernet dest (last 16 bits) |Ethernet source (first 16 bits)|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Ethernet source address (last 32 bits)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            Type code          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         IP header, then TCP header, then your data            |
|                                                               |
...
|                                                               |
|                       end of your data                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Ethernet Checksum                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


ARP HEADER:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            Hardware           |         Protocol              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Hw Addr len  |Proto Addr len |         Operation             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                   Source hardware address                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Source hardware address    |      Source IP address        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Source IP address          | Destination hardware address  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                  Destination hardware address                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     Destination IP address                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


A continuación un ejemplo de envío de paquete ARP:

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

#include <errno.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <net/if_arp.h>
#include <arpa/inet.h>


/* Cabecera ARP */
struct arp_hdr
{
unsigned short int hardware;
unsigned short int protocol;
char hw_addr_len;
char proto_addr_len;
unsigned short operation;
char src_addr[6];
char src_ip[4];
char dst_addr[6];
char dst_ip[4];
};


int main ()
{
/* socket */
int sock;

/* Tama~o del buffer capaz de contener un paquete ARP */
unsigned int buffer_size =
sizeof(struct arp_hdr) + sizeof(struct ether_header);

/* Buffer que contendra el paquete ARP */
unsigned char buffer[buffer_size];
memset(buffer,0,buffer_size);

/* Cabecera ethernet */
struct ether_header *eth = (struct ether_header *)buffer;

/* Cabecera ARP */
struct arp_hdr *arp =
(struct arp_hdr *)(buffer + sizeof(struct ether_header));

/* Direcciones MAC */
char src_mac[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
char dst_mac[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};

/* Direcciones IP */
char src_ip[] = {0x01, 0x02, 0x03, 0x04};
char dst_ip[] = {0x01, 0x02, 0x03, 0x04};

/* Dispositivo  */
char dev[5];
strncpy(dev, "eth0", 5);

/* Creacion del socket */
if ((sock = socket(AF_INET,SOCK_PACKET,htons(ETH_P_ARP)))==-1)
{
perror("socket()");
exit(EXIT_FAILURE);
}

/* Rellena la cabecera ethernet */
memcpy(eth->ether_dhost,dst_mac,ETHER_ADDR_LEN);
memcpy(eth->ether_shost,src_mac,ETHER_ADDR_LEN);
eth->ether_type = htons(ETHERTYPE_ARP);

/* Rellena la cabecera ARP */
arp->hardware = htons(ARPHRD_ETHER);
arp->protocol = htons(ETH_P_IP);
arp->hw_addr_len = 6; 
arp->proto_addr_len = 4;
arp->operation = htons(ARPOP_REPLY);
memcpy(arp->src_addr, src_mac,6);
memcpy(arp->src_ip, src_ip, 4);
memcpy(arp->dst_addr, dst_mac, 6);
memcpy(arp->dst_ip, dst_ip, 4);

/* Dispositivo utilizado "eth0" */
struct sockaddr addr;
strncpy(addr.sa_data, dev, sizeof(addr.sa_data));

/* Envio del paquete ARP */
if ((sendto(sock, buffer, buffer_size, 0,
&addr, sizeof(struct sockaddr)))==-1)
{
perror("sendto()");
exit(EXIT_FAILURE);
}

return 0;
}

De forma similar a la anterior podemos enviar un paquete ICMP. Estas son las
cabeceras utilizadas:

IP HEADER:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Identification         |Flags|    Fragment Offset      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |        Header Checksum        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Source Address                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      Destination Address                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                TCP header, then your data ......              |
|                                                               |

ICMP HEADER:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     Type      |    Code   |               Checksum            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


Y el ejemplo:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>


int main(void)
{
/* socket */
int sock;

/* Longitud de un paquete ICMP */
unsigned int buffer_size = sizeof(struct iphdr) + sizeof(struct icmphdr);

/* Paquete capaz con capacidad para un paquete ICMP */
unsigned char buffer[buffer_size];
memset(buffer, 0, buffer_size);

/* Cabecera IP */
struct iphdr *ip = (struct iphdr *)buffer;

/* Cabecera ICMP */
struct icmphdr *icmp = (struct icmphdr *)(buffer + sizeof(struct iphdr));


/* Creacion del socket */
if ((sock = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) == -1)
{
perror("socket()");
exit(EXIT_FAILURE);
}

/* Establece las opciones del socket */
int o = 1;
if( setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&o,sizeof(o)) == -1 )
{
perror("setsockopt()");
exit(EXIT_FAILURE);
}

/* Rellena la cabecera IP */
ip->version = 4;
ip->ihl = 5;
ip->id = htonl(random());
ip->saddr = inet_addr("1.2.3.4");
ip->daddr = inet_addr("1.2.3.4");
ip->ttl = 255;
ip->protocol = IPPROTO_ICMP;
ip->tot_len = buffer_size;
ip->check = 0;

/* Rellena la cabecera ICMP */
icmp->type = 0;
icmp->code = ICMP_ECHO;
icmp->checksum = 0;

/* Rellena la estructura sockaddr_in */
struct sockaddr_in addr;
addr.sin_family = AF_INET;

/* Envio del paquete */
if ((sendto(sock, buffer, buffer_size, 0, (struct sockaddr*)&addr,
sizeof(struct sockaddr_in))) == -1 )
{
perror("send()");
exit(EXIT_FAILURE);
}

return 0;
}



En algunos casos puede ser interesante enviar paquetes TCP de uno en uno.
Por ejemplo en escaneos. Las cabeceras utilizadas en este caso son:

IP HEADER:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Identification         |Flags|    Fragment Offset      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |        Header Checksum        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Source Address                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      Destination Address                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                TCP header, then your data ......              |
|                                                               |

TCP HEADER:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Source Port           |      Destination Port         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     Acknowledgment Number                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data  |           |U|A|P|R|S|F|                               |
| Offset| Reserved  |R|C|S|S|Y|I|             Window            |
|       |           |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|             Checksum          |          Urgent Pointer       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                 your data ... next 500 octets                 |
|                            ......                             |


Un ejemplo es el siguiente.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>


int main(void)
{
/* socket */
int sock;

/* Tama~o del paquete TCP */
unsigned int buffer_size = sizeof(struct iphdr) + sizeof(struct tcphdr);

/* Buffer de tama~o suficiente para un paquete TCP */
unsigned char buffer[buffer_size];
memset (buffer,0,buffer_size);

/* Cabecera IP */
struct iphdr *ip = (struct iphdr *)buffer;

/* Cabecera TCP */
struct tcphdr *tcp = (struct tcphdr *)(buffer + sizeof(struct iphdr));

/* Crea el socket */
if ((sock = socket(AF_INET,SOCK_RAW,IPPROTO_TCP)) == -1)
{
perror("socket()");
exit(EXIT_FAILURE);
}

/* Establece las opciones del socket */
int o = 1;
if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&o,sizeof(o)) == -1)
{
perror("setsockopt()");
exit(EXIT_FAILURE);
}

/* Rellena la cabecera IP */
ip->version = 4;
ip->ihl = 5;
ip->id = htonl(random());
ip->saddr = inet_addr("1.2.3.4");
ip->daddr = inet_addr("1.2.3.4");
ip->ttl = 255;
ip->protocol = IPPROTO_TCP;
ip->tot_len = buffer_size;
ip->check = 0;  /* falta calcular checksum  */

/* Rellena la cabecera TCP */
tcp->source = htons(1234);
tcp->dest = htons(1234);
tcp->seq = htonl(999999999);
tcp->ack_seq = htonl(999999999);
tcp->ack = 1;
tcp->syn = 1;
tcp->window = htons(2048);
tcp->check = 0; /* falta calcular checksum  */


struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = tcp->source;
addr.sin_addr.s_addr = ip->saddr;

/* Envio del paquete */
if ((sendto(sock, buffer, buffer_size, 0, (struct sockaddr*)&addr,
sizeof(struct sockaddr_in))) == -1)
{
perror("send");
exit(1);
}

return 0;
}


Finalmente un ejemplo de UDP para completar los protocolos más utilzados. Utilizaremos las siguientes cabeceras:

IP HEADER:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Identification         |Flags|    Fragment Offset      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |        Header Checksum        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Source Address                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      Destination Address                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                TCP header, then your data ......              |
|                                                               |

UDP HEADER:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Source Port           |      Destination Port         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         length                |      Checksum                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |


Y nuestro último ejemplo:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>


int main(void)
{
/* socket */
int sock;

/* Tama~o del paquete UDP */
unsigned int buffer_size = sizeof(struct iphdr) + sizeof(struct udphdr);

/* Buffer de tama~o suficiente para un paquete UDP */
unsigned char buffer[buffer_size];
memset (buffer, 0, buffer_size);

/* Cabecera IP */
struct iphdr *ip = (struct iphdr *)buffer;

/* Cabecera UDP */
struct udphdr *udp = (struct udphdr *)(buffer + sizeof(struct iphdr));

/* Crea el socket */
if ((sock = socket(AF_INET,SOCK_RAW,IPPROTO_UDP)) == -1)
{
perror("socket()");
exit(EXIT_FAILURE);
}

/* Establece las opciones del socket */
int o = 1;
if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&o,sizeof(o)) == -1)
{
perror("setsockopt()");
exit(EXIT_FAILURE);
}

/* Rellena la cabecera IP */
ip->version = 4;
ip->ihl = 5;
ip->id = htonl(random());
ip->saddr = inet_addr("1.2.3.4");
ip->daddr = inet_addr("1.2.3.4");
ip->ttl = 255;
ip->protocol = IPPROTO_UDP;
ip->tot_len = buffer_size;
ip->check = 0;

/* Rellena la cabecera UDP */
udp->source = htons(1234);
udp->dest = htons(1234);
udp->len = buffer_size;
udp->check = 0; /* falta calcular checksum  */


struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = udp->source;
addr.sin_addr.s_addr = ip->saddr;

/* Envio del paquete */
if ((sendto(sock, buffer, buffer_size, 0, (struct sockaddr*)&addr,
sizeof(struct sockaddr_in))) == -1)
{
perror("send()");
exit(1);
}

return 0;
}

GNU Netcat

Netcat es una herramienta de Unix utilizada para leer y escribir a través de conexiones de red mediante los protocolos TCP y UDP. GNU Netcat dispone de tres funcionalidades principales: modo conexión, modo escucha y modo tunel. A lo largo de este post se describen los tres modos.


Netcat fue desarrollado inicialmente por Hobbit, aunque actualmente se ha convertido en un proyecto open source basado en licencia GPL que reside en
netcat.sourceforge.net.



La instalación de Netcat es la típica de las herramientas Unix:


$ tar xvfz netcat-0.7.0.tar.gz
$ cd netcat-0.7.0
$ ./configure
$ make
$ make install


Una vez instalado, el parámetro -V, muestra la versión de netcat:


$ nc -V
netcat (The GNU Netcat) 0.7.0
Copyright (C) 2002 - 2003  Giovanni Giacobbi

This program comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of this program under the terms of
the GNU General Public License.
For more information about these matters, see the file named COPYING.

Original idea and design by Avian Research ,
Written by Giovanni Giacobbi .


Con el parámetro -h obtenemos un listado con las opciones disponibles:


$ nc -h
GNU netcat 0.7.0, a rewrite of the famous networking tool.
Basic usages:
connect to somewhere:  netcat [options] hostname port [port] ...
listen for inbound:    netcat -l -p port [options] [hostname] [port] ...
tunnel to somewhere:   netcat -L hostname:port -p port [options]

Mandatory arguments to long options are mandatory for short options too.
Options:
-c, --close                close connection on EOF from stdin
-e, --exec=PROGRAM         program to exec after connect
-g, --gateway=LIST         source-routing hop point[s], up to 8
-G, --pointer=NUM          source-routing pointer: 4, 8, 12, ...
-h, --help                 display this help and exit
-i, --interval=SECS        delay interval for lines sent, ports scanned
-l, --listen               listen mode, for inbound connects
-L, --tunnel=ADDRESS:PORT  forward local port to remote address
-n, --dont-resolve         numeric-only IP addresses, no DNS
-o, --output=FILE          output hexdump traffic to FILE (implies -x)
-p, --local-port=NUM       local port number
-r, --randomize            randomize local and remote ports
-s, --source=ADDRESS       local source address (ip or hostname)
-t, --tcp                  TCP mode (default)
-T, --telnet               answer using TELNET negotiation
-u, --udp                  UDP mode
-v, --verbose              verbose (use twice to be more verbose)
-V, --version              output version information and exit
-x, --hexdump              hexdump incoming and outgoing traffic
-w, --wait=SECS            timeout for connects and final net reads
-z, --zero                 zero-I/O mode (used for scanning)

Remote port number can also be specified as range.  Example: '1-1024'

Netcat en modo conexión
El modo conexión de netcat es el más sencillo de los existentes. Se comporta de una forma similar a telnet, aunque es una herramienta mucho más avanzada. Por ejemplo, para realizar una conexión TCP (conexión por defecto) pasaremos como parámetro el nombre del host y el puerto:

$ nc localhost 22
SSH-1.99-OpenSSH_3.6.1p2


si deseamos que netcat se comporte como telnet añadiremos la opción -T:


nc -T localhost 23
Fedora Core release 1 (Yarrow)
Kernel 2.4.22-1.2135.nptl on an i686
login:


Añadiendo la opción -x, netcat ofrece la salida en hexadecimal:

$ nc -x localhost 22
SSH-1.99-OpenSSH_3.6.1p2
Received 25 bytes from the socket
00000000  53 53 48 2D  31 2E 39 39  2D 4F 70 65  6E 53 53 48  SSH-1.99-OpenSSH
00000010  5F 33 2E 36  2E 31 70 32  0A                        _3.6.1p2.


Otra opción que puede resultar interesante es -v (verbose) o -v -v que oferce cierta información sobre la conexión:
$ nc -v localhost 22
localhost [127.0.0.1] 22 (ssh) open
SSH-1.99-OpenSSH_3.6.1p2

$ nc -v -v localhost 22
Notice: Real hostname for localhost [127.0.0.1] is NEPTUNO
localhost [127.0.0.1] 22 (ssh) open
SSH-1.99-OpenSSH_3.6.1p2


Netcat trabaja con dos protocolos: TCP y UDP. El parámetro utilizado para las conexiones TCP es -t, aunque no es necesario, dado que esta es la opción por defecto. Para realizar conexiones UDP, puede utilizar el parámetro -u. Por ejemplo, enviar datos a un host + puerto UDP es tan sencillo como:

$ echo "datos enviados" | nc -u localhost 1234


Por ejemplo, realize la siguiente prueba: Abra tres terminales y escriba en el primero:

nc -u -l localhost -p 1234

(Este comando mantine a netcat a la escucha en un puerto udp, se estudiará con detalle en el siguiente apartado)



En el segundo:

$ /usr/sbin/tcpdump -s 0 -xX udp -i lo
tcpdump: listening on lo


y en el tercero:

echo "datos enviados" | nc -u localhost 1234



Observaremos que la salida de tcpdump con los datos UDP capturados es la
siguiente:

21:14:46.787266 NEPTUNO.32885 > NEPTUNO.1234: udp 15 (DF)
0x0000   4500 002b b17e 4000 4011 8b41 7f00 0001        E..+.~@.@..A....
0x0010   7f00 0001 8075 04d2 0017 79d5 6461 746f        .....u....y.dato
0x0020   7320 656e 7669 6164 6f73 0a                    s.enviados.


Netcat en modo escucha
El modo de escucha de netcat es un complemento interesante. Este nos permite abrir sockets en la máquina local, a los que posteriormente poderemos conectarnos. En el apartado anterior (modo conexión) se ha realizado un ejemplo sencillo del modo de escucha.



Se utiliza la opción -l para indicarle a netcat que deseamos ejecutar la herramienta en modo escucha (listen), indicamos con la opción -p, el puerto en el que deseamos abrir el socket y añadimos -u si deseamos utilizar el protocolo UDP. Recordemos que por defecto netcat utiliza TCP:
nc -u -l localhost -p 1234


Con netstat podemos ver los puertos abiertos de nuestro sistema y el programa asociado a esos puertos:

$ netstat -lunp | grep nc
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
udp        0      0 0.0.0.0:1234            0.0.0.0:* 3624/nc



Una opción que ha sido utilizada durante mucho tiempo como puerta trasera simple, es la opción -e de netcat. Esta opción permite asociar un programa a la conexión establecida.

Por ejemplo, con el siguiente comando dejamos una shell escuchando en el puerto 1234.


$ nc -l  localhost -p 1234 -e /bin/bash



Podemos conectarnos a ella con netcat y utilizarla de un modo similar a la utilidad Telnet:

$ nc localhost 1234
uname -o
GNU/Linux
date
jue dic 18 21:50:07 CET 2003


Netcat en modo tunel
GNU Netcat puede utilizarse para crear túneles de una forma sencilla. Veamos un ejemplo en el que enlazamos el puerto de telnet (23) de una máquina remota al puerto 1234 de la máquina local.

En un terminal escribimos:
$ nc -L remoto:23 -p 1234

En otro terminal nos conectamos al tunel:
$ telnet localhost 1234
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Fedora Core release 1 (Yarrow)
Kernel 2.4.22-1.2135.nptl on an i686
login:



De esta forma hemos conectado el puerto remoto 23 de la máquina "remoto" al puerto local 1234.

En modo tunel puede resultar interessante la opción -x, que realizará un volcado de toda la información que pase por el tunel.


$ nc -L localhost:23 -p 1234 -x
Sent 12 bytes to the socket
00000000  FF FD 18 FF  FD 20 FF FD  23 FF FD 27               ..... ..#..'
Received 12 bytes from the socket
00000000  FF FB 18 FF  FB 20 FF FB  23 FF FB 27               ..... ..#..'
Sent 24 bytes to the socket
00000000  FF FA 20 01  FF F0 FF FA  23 01 FF F0  FF FA 27 01  .. .....#.....'.
00000010  FF F0 FF FA  18 01 FF F0                            ........
Received 107 bytes from the socket
00000000  FF FA 20 00  33 38 34 30  30 2C 33 38  34 30 30 FF  .. .38400,38400.
00000010  F0 FF FA 23  00 4E 45 50  54 55 4E 4F  3A 30 2E 30  ...#.NEPTUNO:0.0
00000020  FF F0 FF FA  27 00 03 58  41 55 54 48  4F 52 49 54  ....'..XAUTHORIT
00000030  59 01 2F 68  6F 6D 65 2F  64 6C 65 72  63 68 2F 2E  Y./home/dlerch/.
00000040  58 61 75 74  68 6F 72 69  74 79 00 44  49 53 50 4C  Xauthority.DISPL
00000050  41 59 01 4E  45 50 54 55  4E 4F 3A 30  2E 30 FF F0  AY.NEPTUNO:0.0..
00000060  FF FA 18 00  58 54 45 52  4D FF F0                  ....XTERM..
Sent 15 bytes to the socket
00000000  FF FB 03 FF  FD 01 FF FD  1F FF FB 05  FF FD 21     ..............!
Received 24 bytes from the socket
00000000  FF FD 03 FF  FC 01 FF FB  1F FF FA 1F  00 50 00 28  .............P.(
00000010  FF F0 FF FD  05 FF FB 21                            .......!
Sent 73 bytes to the socket
00000000  FF FB 01 46  65 64 6F 72  61 20 43 6F  72 65 20 72  ...Fedora Core r
00000010  65 6C 65 61  73 65 20 31  20 28 59 61  72 72 6F 77  elease 1 (Yarrow
00000020  29 0D 0A 4B  65 72 6E 65  6C 20 32 2E  34 2E 32 32  )..Kernel 2.4.22
00000030  2D 31 2E 32  31 33 35 2E  6E 70 74 6C  20 6F 6E 20  -1.2135.nptl on
00000040  61 6E 20 69  36 38 36 0D  0A                        an i686..
Sent 7 bytes to the socket
00000000  6C 6F 67 69  6E 3A 20                               login:
Received 3 bytes from the socket
00000000  FF FD 01                                            ...


Uso de Netcat como scanner
Las funciones de escaner de netcat son muy simples. Puede utilizarse para detectar los servicios de una máquina, pero no dispone de las funciones de escaneo avanzadas de que disponen escáneres como nmap.


El parámetro utilizado para el escaneo es -z. A continuación se especifica la máquina y el rango de puertos a escanear.
nc -v -z localhost 20-24
localhost [127.0.0.1] 22 (ssh) open
localhost [127.0.0.1] 23 (telnet) open