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.