Páginas

miércoles, 28 de septiembre de 2011

¿De quien carajo es este socket? III

Cotinuando con nuestra fijacion de mirar sockets en el sistema, vamos a ver como podemos averiguar a quien corresponde un determinado socket y que es lo que contiene, anteriormente lo realizamos por comandos, mirando directamente en los archivos y ahora lo haremos en C.

En primer lugar, un pequeño recuerdo de cuales son nuestra fuentes de informacion en el sistema.

-/proc/net/*:lista de sockets que el sistema esta utilizando, con informacion como el inode.
-/proc/pid/fd/*: aqui encontraremos los descriptores correspondientes a los sockets que tenemos abiertos.

Por no repetirme voy a hacer hincapie en los puntos donde yo tuve mas problemas, que fueron en la parte de como se lee un socket que no he creado y las expresiones regulares.

Bien vamos a ponernos manos a la obra, en primer lugar cogeremos la parte que queremos de /proc/net/* para ello utilizaremos la libreria regex de linux, con esta libreria compilaremos expresiones regulares(regcomp) y las ejecutaremos sobre una determinada cadena(regexec), para comprobar si coincide.

El formato para las expresiones regulares sigue el standard POSIX, si se requiere mas informacion podemos utilizar el manual de grep de linux.

#info grep

La cadena que queremos captar es del modo:

"0: 0100007F:1DD2 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 4398 1 dce04a80 300 0 0 2 -1"

y la parte que nos interesa es:

"0100007F:1DD2 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 4398"

Desde la ip origen hasta el inode, para utilizar regex como hemos mecionado primero se compila la expresion y luego se ejecuta sobre una cadena. nuestra expresion vendria a ser lo siguiente

"[0-9A-F]{8}:[0-9A-F]{4} [0-9A-F]{8}:[0-9A-F]{4} [0-9A-F]{2} [0-9A-F]{8}:[0-9A-F]{8} [0-9A-F]{2}:[0-9A-F]{8} [0-9A-F]{8} .* [0-9]{4,5} "

Si nos fijamos esta expresion regular viene determinada por varias repeticiones del mismo tipo, los corchetes nos indican que en esa posicion puede ir cualquier caracter de los que se indican en su interior, a continuacion tenemos un numero o numeros indicado entre llaves, este nos indica que lo que halla atras suya se debe repetir ese numero de veces.

Lo siguiente una vez tenemos nuestra expresion regular es compilarla para ello se utiliza la funcion regcomp.

int regcomp(regex_t *preg, const char *regex, int cflags);

El primer parametro que nos pide la funcion es un puntero para un area temporal de patrones, el segundo es nuestra expresion regular terminada en NULL, y por ultimo un entero para indicar los flags que queremos activar.

Si regcomp devuelve distinto de cero, la compilacion no ha tenido exito.

La siguiente funcion seria regexec, la cual nos permite ejecutar esa expresion regular sobre una cadena.

int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);

El primer parametro en nuestro caso es el mismo puntero al area temporal que hemos indicado en regcomp, a continuacion una cadena sobre la que queramos ejecutar nuestra expreison regular, nmatch y pmatch sirven para almacenar la informacion respecto de las coincidencias encontradas, el ulimo parametro sirve para activar los flags REG_NOTBOL y REG_NOTEOL, los cuales se utilizan para definir las coincidencias respectos del caracter "nueva linea".

Una vez lanzado regexec devolvera cero en caso de exito y  REG_NOMATCH en caso de fallo, la informacion respecto a la coincidencia se encuentra en el array pmatch, dependiendo del numero de coincidencias tendremos mas o menos elementos en este array, cada elemento del array es del tipo regmatch el cual contiene dos parametros rm_so y rm_eo, los cuales indican la posicion primera y ultima de la subcadena que coincide con la expresion regular.

Los elementos del array que no tengan coincidencia tendran el valor de rm_so puesto a -1.

Ahora os dejo una funcion que recibe el nombre de un fichero y lo parsea, una vez encontrada una coincidencia podemos obtener los datos, analizarlos y utilizarlos para analizar los sockets.


 1 int parser(char *protocol)
  2 {      
  3         //definimos variables
  4         int status=0;
  5         FILE *protocolFile;
  6         char buff[128];
  7         char *out;
  8         char *aux;
  9         regex_t re;
 10        regmatch_t pmatch[1];
 11        char *regexpchar = "[0-9A-F]{8}:[0-9A-F]{4} [0-9A-F]{8}:[0-9A-F]{4} [0-9A-F]{2} [0-9A-F]{8}:[0-9A-F]{8} [0-9A-F]{2}:[0-9A-F]{8} [0-9A-F]{8} .* [0    -9]{4,5} ";
 12        
 13        
 14         //compilamos y comprobamos que no dio error
 15         if(regcomp(&re, regexpchar, REG_EXTENDED) != 0)
 16         {
 17         fprintf(stderr, "regcomp: %s\n", (char *)strerror(errno));
 18         status=-1;
 19         return status;
 20         }
 21        
 22         //abirmos el fichero y comprbamos que no dio error
 23         protocolFile = fopen(protocol,"r");
 24         if(protocolFile == NULL)
 25         {      
 26                 fprintf(stderr, "protocolFile: %s\n", (char *)strerror(errno));
 27                 status=-1;
 28                 return status;
 29         }
 30        
 31         /abrimos bucle para analizar linea por linea
 32         while(fgets(buff, sizeof(buff), protocolFile) != NULL)
 33         {      
 34                 //ejecutamos por cada linez la expresion regular
 35                 if(!regexec(&re, buff, (size_t)1, pmatch, REG_NOTEOL))
 36                 {      
 37                         //comprobamos si coincidio
 38                         if(pmatch[0].rm_so != -1)
 39                         {      
 40                                 out = (char *)malloc(pmatch[0].rm_eo - pmatch[0].rm_so);
 41                                 strncpy(out, buff + pmatch[0].rm_so, pmatch[0].rm_eo - pmatch[0].rm_so);
 42                                 //tratamos cadena y liberamos.
 43                                 free(out);
 44                         }      
 45                 }      
 46         }      
 47        
 48         //liberamos memoria
 49         regfree(&re);
 50         fclose(protocolFile);
 51         free(regexpchar);
 52        
 53         return(status);
 54 }   




Para analizar los sockets vamos a utilizar la libreria pcap de nuestros amigos de tcpdump, la primera funcion que necesitamos es pcap_open_live la cual nos permite poner nuestros dispositvos de red para empezar a capturar paquetes, una vez activados para capturar, compilaremos un filtro en el cual incluira las ips y puertos que hemos conseguido con el parser con al funcion pcap_compile, con nuestro filtro compilado lo fijamos con pcap_setfilter, por ultimo iremos capturando paquete a paquete con pcap_next en un bucle.

Vamos a analizar las funciones que necesitamos:

pcap_t *pcap_open_live(const char *device, int snaplen, int promisc, int to_ms, char *errbuf);






El primer parametro es el nombre del dispositvo a utilizar, si utilizamos el valor NULL escucharemso por todas las interfaces de nuextra red. el segundo parametro es el maximo tamaño de bytes para capturar por paquete, en nuestro caso utilizaremos BUFSIZ de stdio.h la cual nos indica el tamaño del buffer del sistema, Promisc indica si la interface esta o no en modo promiscuo, to_ms indica el timeout en milisegundos, por ultimo un buffer para los errores de la funcion.

El valor devuelto es un manejador para poder utilizarlo como parametro en las siguientes funciones.





int pcap_compile(pcap_t *p, struct bpf_program *fp, const char *str, int optimize, bpf_u_int32 netmask);

El primer parametro es el manejador que hemos obtenido con pcap_open_live, fp es un puntero donde almacenaremos nuestro filtro compilado, str es nuestro filtro, optimize es para indicar si optimizamos el filtro o no, por ultimo la netmask de la red que vamos a escanear, se devuelvo 0 en caso de exito y otro en caso de error.

Para obtener informacion sobre como formar filtros podemos consultar:

#man pcap-filter

int pcap_setfilter(pcap_t *p, struct bpf_program *fp);

pcap_setfilter, tiene los mismos parametros que los dos primeros de pcap_compile.

const u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h);






El primer parametros es un puntero a nuestro manejador, el segundo es un puntero a una estructura donde guardaremos el momento en el que fue capturado, el tamaño del paquete y el tamaño de la porcion si estaba fragmentado.

El valor devuelto es el paquete capturado, en el encontraremos las cabeceras ethernet, ip y tcp. estas cabeceras las vamos a representar por las siguientes estructuras.


  1 /* Ethernet header */
  2 struct sniff_ethernet
  3 {      
  4         u_char ether_dhost[ETHER_ADDR_LEN]; /* destination host address */
  5         u_char ether_shost[ETHER_ADDR_LEN]; /* source host address */
  6         u_short ether_type; /* IP? ARP? RARP? etc */
  7  };    
  8        
  9  /* IP header */
 10  struct sniff_ip
 11 {      
 12         u_char ip_vhl; /* version << 4 | header length >> 2 */
 13         u_char ip_tos; /* type of service */
 14         u_short ip_len; /* total length */
 15         u_short ip_id; /* identification */
 16         u_short ip_off; /* fragment offset field */
 17         #define IP_RF 0x8000 /* reserved fragment flag */
 18         #define IP_DF 0x4000 /* dont fragment flag */
 19         #define IP_MF 0x2000 /* more fragments flag */
 20         #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
 21         u_char ip_ttl; /* time to live */
 22         u_char ip_p; /* protocol */
 23         u_short ip_sum; /* checksum */
 24         struct in_addr ip_src,ip_dst; /* source and dest address */
 25 };
 26        
 27 /* TCP header */
 28  struct sniff_tcp
 29 {      
 30         u_short th_sport; /* source port */
 31         u_short th_dport; /* destination port */
 32         tcp_seq th_seq; /* sequence number */
 33         tcp_seq th_ack; /* acknowledgement number */
 34         u_char th_offx2; /* data offset, rsvd */
 35        
 36         u_char th_flags;
 37         #define TH_FIN 0x01
 38         #define TH_SYN 0x02
 39         #define TH_RST 0x04
 40         #define TH_PUSH 0x08
 41         #define TH_ACK 0x10
 42         #define TH_URG 0x20
 43         #define TH_ECE 0x40
 44         #define TH_CWR 0x80
 45         #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
 46         u_short th_win; /* window */
 47         u_short th_sum; /* checksum */
 48         u_short th_urp; /* urgent pointer */
 49  };   

Hay un punto a tener en cuenta, para obtener el tamaño de las cabeceras ip y tcp utilizamos las siguientes macros.



#define IP_HL(ip_hdr) (((ip_hdr)->ip_vhl) & 0x0f)
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)


Una posible funcion donde enviamos un filtro y el nombre dle dispositivo a escuchar seria.




     1    int pcapFunction(char *filter_exp, char *dev)
     2    {
     3         char *errbuf[PCAP_ERRBUF_SIZE];
     4         pcap_t *handle;
     5         bpf_u_int32 net;
     6         bpf_u_int32 mask;
     7         struct bpf_program fp;
     8         struct pcap_pkthdr header;
     9         const u_char *packet;
    10         struct sniff_ethernet *ethernet;
    11         struct sniff_ip *ip_hdr;
    12         struct sniff_tcp *tcp_hdr;
    13       
    14         int size_tcp;
    15         int size_ip;
    16         int count = 0;
    17       
    18         handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
    19       
    20         if (handle == NULL)
    21         {
    22             fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
    23             return(2);
    24         }
    25  
    26        if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1)
    27         {
    28             fprintf(stderr, "Can't set net and mask for device %s: %s\n",dev, errbuf);
    29             net = 0;
    30             mask = 0;
    31         }
    32       
    33         if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1)
    34         {
    35             fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
    36             return(2);
    37         }
    38       
    39         if(pcap_setfilter(handle, &fp) == -1)
    40         {
    41             fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
    42             return(2);
    43         }
    44       
    45         while(count < 10)
    46         {
    47             count++;
    48             packet = pcap_next(handle, &header);
    49  
    50             if (packet != NULL)
    51             {
    52                 ethernet = (struct sniff_ethernet*)(packet);
    53       
    54                 ip_hdr = (struct sniff_ip*)(packet + SIZE_ETHERNET);
    55                 size_ip = IP_HL(ip_hdr)*4;
    56                 printf("size_ip: %d\n", size_ip);
    57       
    58                 tcp_hdr = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
    59                 size_tcp = TH_OFF(tcp_hdr)*4;
    60                 printf("size_tcp: %d\n", size_tcp);
    61             }
    62         }
    63  
    64         pcap_freecode(&fp);
    65         pcap_close(handle);
    66       
    67         return(0);
    68    }



Y solo nos quedaria buscar en /proc/pid/fd el inode conseguido en el parser, pero eso so lo dejo a vosotros :)

La mayor parte de la informacion para hacer este post la he conseguido de:

http://www.tcpdump.org/pcap.html
http://www.wikipedia.org/

Un saludo y gracias por el interes.
 

jueves, 22 de septiembre de 2011

Si no hay trabajo....generemoslo.

Llevo un tiempo pensando en esto y cada vez lo veo mas claro, primero os expongo mi situación, soy Administrador de sistemas en paro, llevo así 5 meses, he intentado busca algo de lo mio, pero siempre he acabado con una negativa o con pasotismo después de la entrevista, sin recibir ninguna respuesta.

Esta claro que ahora mismo el trabajo no es que abunde, cuando entro en infojobs, tecnoempleo, etc me encuentro con menos ofertas y ocho mil personas apuntadas, ante esta perspectiva tenia dos opciones seguir buscando que una empresa lanzara una oferta y me cogieran o generar yo ese trabajo. En teoría eso de ser tu propio jefe pinta bastante bien, todos sabemos los problemas de los autónomos, del sueldo variable, etc. Pero si miras la perspectiva en la que nos encontramos, se supone que somos la generación mejor preparada, pues demostremos lo, conozco gente en paro que en su ámbito son personas muy validas, y aun así no encuentran trabajo o no se les reconoce todo lo que a mi entender se debería.

Señores generamos trabajo, creemos, innovemos, hagamos algo por nuestro futuro, yo estoy harto de esperar y creo que mis conocimientos unidos a los de otros podrían generar un buen vinculo laboral, que se gana poco dinero ....bueno pero ningún comienzo fue fácil, la verdad, creo que nos merecemos la oportunidad de mostrar al mundo nuestra capacidad, yo tengo mil ideas y nos las he podido llevar a cabo porque me faltaba un comercial, un diseñador, un comunity manager, etc.

¿Que quiero decir con todo esto? pues que nos reunamos, nos conozcamos y seguro que encontramos gente con la que colaborar y crear trabajo, puede que esto ya se haga y yo no me halla enterado, pero sino es así. voto por que aquí en Sevilla se cree una reunión para del desarrollo laboral, no nos hace falta nadie solo un hashtag en el twitter, un sitio y a conocerse. que venga todo el mundo, de cualquier ámbito, nutramosnos de otras áreas y digamos al mundo que estamos aquí.

¿como? he pensado en el hashtag #yolocreo (nunca fui muy bueno con esto de la inventiva).
¿cuando? cuanto antes mejor, el viernes 30 por la tarde, a las 17:00.
¿donde? si alguien tiene un local mejor, sino en el central en la alameda.

un saludo de un tío con ganas de trabajar y ser creativo.

lunes, 22 de agosto de 2011

¿de quien carajo es este socket? II

Continuando con lo explicado la semana pasada vamos a ver donde podemos encontrar la información que conseguimos por comandos, pero esta vez lo haremos de el fabuloso FS "/proc".

"/proc" realmente es un pseudo-file system (tipo procfs), este no consume espacio de almacenamiento y ocupa una cantidad de memoria baja, en él el kernel nos vuelca información sobre los procesos, hardware, memoria, almacenamiento, vamos información sobre el sistema en si.

Para un estudio mas profundo aconsejo mirar un "man proc" donde viene explicado el contenido de sus directorios y ficheros.

Yendo la grano, para encontrar información sobre los sockets, podemos mirar en el siguiente directorio "/proc/net/", ahí dependiendo del tipo de socket del que queramos información miraremos en el archivo con dicho nombre, en nuestro caso vamos a mirar sockets tcp (tcp6 para ipv6).

Dentro de este archivo nos encontramos con algo como esto.

#cat /proc/net/tcp
sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                    
   0: 0301A8C0:994C BF9255D1:0050 01 00000000:00000000 00:00000000 00000000  1000        0 120913 1 00000000 30 4 28 5 3   


Vale, una vez tenemos este armatoste de datos en hexadecimal little-endian, vamos a ver las columnas que tienen los datos que nos hacen falta para lanzar nuestro tcpdump, como ya hicimos en el articulo anterior. 

local_addres -> aqui tenemos la ip:puerto origen.
rem_address -> ip:puerto destino
inode -> inode del socket (este dato viene en decimal).

Ya sabemos los datos ahora solo nos falta pasarlos a formato decimal, mas que nada para enterarnos nosotros de que pone.

Al estar en little-endian se lee de derecha a izquierda, por ejemplo para la ip y el puerto tenemos lo siguiente según la salida.

ip:port ------> 0301A8C0:994C

Sabemos que la ip es de la forma x1.x2.x3.x4, la parte de la ip consta de 8 digitos hexadecimales por lo que cada dos digitos corresponden a una de las x del formato ip, y al estar en little-endian x1 es la pareja de digitos mas a la derecha, vamos a verlo en nuestra salida.

Hexadecimal              Decimal               
03.01.A8.C0      ---->   3.1.168.192

De tal modo si lo pasamos a big-endian, cambiando el orden de los digitos de izquierda a derecha, obtenemos nuestra dirección ip.

192.168.1.3

El puerto seria igual, pero ya que solo hay un digito no habría que darle la vuelta. 

Hexadecimal              Decimal               
994C               ---->    39244

Una vez tenemos todos los datos en decimal, podemos obtener información del socket con tcpdump igual que hicimos en el articulo anterior. Lo único que nos queda es saber a que proceso pertenece.

Entre los directorios que podemos encontrar en /proc vamos a fijar nuestra atención en aquellos que tienen por nombres números. Cada vez que se ejecuta un proceso, durante el tiempo que este corriendo se le asigna una de estas carpetas cuyo nombre coincide con su pid. Dentro de cada una de estas carpetas hay un directorio llamado fd, pues bien aquí se encuentran lo descriptores de ficheros de los archivos que tiene abiertos, y como nuestro socket necesita un descriptor de fichero pues aquí lo encontraremos.

Bien, ya sabemos la carpeta donde buscar, ahora nos hace falta saber como saber que un determinado fd es un socket y que inode tiene ese socket, un listado del directorio con -l nos sacara de dudas, escogeremos un pid al azar, el uno por ejemplo.

#ls -li
total 0
167690 lrwx------ 1 root root 64 2011-08-22 21:02 0 -> /dev/null
167691 lrwx------ 1 root root 64 2011-08-22 21:02 1 -> /dev/null
167700 lrwx------ 1 root root 64 2011-08-22 21:02 10 -> socket:[7298]



Antes de proseguir, apuntar un par de cosas sobre el archivo, primero vamos a ver como esta formado su nombre. 

fd->tipo:[inode]
10->socket:[7298]

Como ya os estaréis preguntando, si la primera columna que sale es el inode, ¿como que no es el mismo?  


167700 lrwx------ 1 root root 64 2011-08-22 21:02 10 -> socket:[7298]

La respuesta es que el inode que nos muestra "ls -i" es el del descriptor de fichero y el que sale entre paréntesis es del socket.

Pues por fin solo nos queda buscar /proc el directorio donde esté uno de estos archivos y tendremos el pid del proceso dueño del socket, una posible solución podría ser la siguiente, aunque es un poco enrevesada, si tenéis alguna manera mejor, soy todo oídos.

#find /proc -inum `ls -Rli /proc/[0-9]{1,}/fd|egrep 'socket:\[120913\]'|awk '{print $1}'` 2> /dev/null
/proc/3514/fd/13

Ahí lo tenemos pid 3514 con el fd 13, en mi caso si miro en /proc/3514/cmdline veo que el pid pertenece a firefox.

#cat /proc/3514/cmdline
/usr/lib/firefox-6.0/firefox-bin

Esto es todo por hoy, la próxima semana pondré como hacerlo por C.

Un saludo.

lunes, 15 de agosto de 2011

¿de quien carajo es este socket?

En su día estuve bastantes horas hasta que descubrí como se podía obtener información de un determinado socket en una determinada aplicación para Linux, por lo que dejo aquí mi experiencia para quien pueda ser de utilidad o para quien pueda corregirme en mis errores.

Lo primero es saber con que herramientas podemos trabajar para obtener esa información, lo primero si queremos saber que sockets hay abiertos en nuestro sistema de una manera rápida, yo optaría por

#lsof -i

Lo cual nos generaría una salida parecida a esta:

COMMAND    PID      USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
firefox-b 3743 usuario   65u  IPv4 144930      0t0  TCP nuestropc:50449->209.222.128.176:www (ESTABLISHED)

Veamos que nos ha dicho este comando y que hace este comando.

Por definición lsof nos muestra los archivos abiertos que hay actualmente en nuestro sistema, entendiendo por archivo copiando literalmente del man de lsof "a regular file, a directory, a block special file, a character special file, an executing text reference, a library, a stream or a network file  (Internet  socket,  NFS  file  or  UNIX  domain socket.)"

De toda esta lista solo nos interesa los últimos tipos, por lo que le decimos a lsof la opción -i la cual nos da los sockets con datos ip que le indiquemos, en nuestro caso no le aportamos nada, para que nos de todas las conexiones.

La salida nos ofrece la siguiente información:

COMMAND    

firefox-b
Aplicación propietaria del socket.

PID      
3743
pid de la aplicación propietaria. 

USER   
usuario
usuario que lanzo la aplicación.

FD
65u
es el numero del descriptor de fichero, seguido de los permisos.

TYPE
IPv4
tipo de servicio.

DEVICE
144930
el numero del dispositivo asociado al socket.

SIZE/OFF
0t0
tamaño u offset, en nuestro caso "0t" nos indica offset.

NODE 
TCP
protocolo del socket.

NAME
nuestropc:50449->209.222.128.176:www (ESTABLISHED)
información del socket: source:port->dest:port(Estado).

Ya sabemos que sockets tenemos abiertos, si queremos saber información sobre el socket y su contenido, tenemos a nuestro gran amigo tcpdump, el cual nos dirá toda la información que queramos sobre el. una posible ejecucion con los datos obtenidos seria.

#tcpdump -vvv -A -i eth1 src host nuestropc and src port 50449 and dst host 209.22.128.176 and dst port www

Con este comando obtenemos los paquetes de los sockets, recomiendo pasar la salida a un archivo ya que esto manda un chorrazo por pantalla bastante dificil de seguir.

En el lanzamiento del comando podemos diferenciar dos zonas, la primera de opciones las cuales nos indican:

-vvv: esto es para obtener el maximo de informacion posible, tambien se puede obtener menos quitandole uves, hasta el minimo omientiendolas.

-A: nos ofrece la informacion en formato ascii.

-i eth1: sirve para indicarle por que tarjeta debemos escuchar.

La segunda parte, de parametros es para indicarle las especificaciones que debe tener el socket, la sintaxis y opciones disponibles las podemos encontrar ejecutando "man pcap-filter".

Bueno ya podemos elegir que socket escuchar y asi saber que esta comunicando nuestro pc al mundo.

En la proxima entrada hablare sobre "/proc/net/tcp", "/proc/net/udp" y "/proc/PID/fd", desde donde tambien podremos obtener informacion para su posterior captacion con tcpdump.

Espero vuestras aportaciones a mi primer post :).