Echando código: ¿Como monitorear a NFS usando Java? (hablemos ONC RPC, I)

Manhattan
Muchas aplicaciones dependen de NFS para funcionar correctamente, como bloques uno encima de el otro. ¿Como asegurarse de que este servicio está trabajando como se debe?

Si no sabe que es NFS, entonces lease primero este tutorial y después vuelva aquí. También para este articulo yo asumo que usted ya ha escrito programas en Java con anterioridad y que sabe algunos conceptos básicos de redes.

Es una pregunta interesante, ya que NFS utiliza RPC como protocolo principal (más abajo están UDP o TCP pero eso es otra cosa); Así que quizas los más fácil es simular una transacción sintetica (como por ejemplo para monitorear una base de datos podemos hablar JDBC y preguntar por la metada directamente). Se me ocurre que pedir un listado de archivos en el directorio exportado puede ser una prueba decente (limitando el número de archivos a cierto número, por si el directorio es grande).

Usted se puede estar preguntando lo siguiente: ¿No es más eficiente preguntar estas cosas por SNMP (quizas extendiendo la configuración de el agente para que llame a los comandos nativos nfstat o showmount por ejemplo)? Quizas sí, después de todo RPC es un protoclo más pesado que SNMP. Pero por otro lado si usted no puede instalar demonios adicionales en la máquina o su organización no soporta SNMP, entonces ¿que alternativa le queda?

Después de mucho buscar, me consegui con una librería GPL llamada “Remote Tea“. Ya hay varias implementaciones (entre ellas un demonio de NFS) así que quizas esto es lo que estaba buscando.

Lo primero que hacemos es preparar a un servidor NFS de pruebas. Si usted utiliza Linux quizas pudiera hacer algo como esto (necesita ser root, para el ejemplo asumo que su red interna es 192.168.0.0/24 y que está compartiendo /usr/local/src.):

[root@localhost ~]# cat /etc/exports
/usr/local/src 127.0.0.1/255.0.0.0(ro) 192.168.0.0/255.255.255.0(ro)
[root@localhost ~]# /etc/init.d/nfs restart
Shutting down NFS mountd: [ OK ]
Shutting down NFS daemon: [ OK ]
Shutting down NFS quotas: [ OK ]
Shutting down NFS services: [ OK ]
Starting NFS services: [ OK ]
Starting NFS quotas: [ OK ]
Starting NFS daemon: [ OK ]
Starting NFS mountd: [ OK ]
[root@localhost ~]# showmount -e localhost
Export list for localhost:
/usr/local/src 192.168.0.0/255.255.255.0
[root@localhost ~]#

Y para estar completamente seguros (¿los demonios se registraron con RPC?):

[root@localhost ~]# rpcinfo -p localhost
program vers proto port
100000 2 tcp 111 portmapper
100000 2 udp 111 portmapper
100024 1 udp 32829 status
100024 1 tcp 32771 status
100011 1 udp 912 rquotad
100011 2 udp 912 rquotad
100011 1 tcp 915 rquotad
100011 2 tcp 915 rquotad
100003 2 udp 2049 nfs
100003 3 udp 2049 nfs
100003 4 udp 2049 nfs
100003 2 tcp 2049 nfs
100003 3 tcp 2049 nfs
100003 4 tcp 2049 nfs
100021 1 udp 32839 nlockmgr
100021 3 udp 32839 nlockmgr
100021 4 udp 32839 nlockmgr
100021 1 tcp 32777 nlockmgr
100021 3 tcp 32777 nlockmgr
100021 4 tcp 32777 nlockmgr
100005 1 udp 924 mountd
100005 1 tcp 927 mountd
100005 2 udp 924 mountd
100005 2 tcp 927 mountd
100005 3 udp 924 mountd
100005 3 tcp 927 mountd

Note que rpcinfo nos da información muy importante acerca de el demonio que queremos contactar. En este caso es el número de el programa, la versión, el protocolo y el nombre. Anotelo en algún lado, ya que esto será requerido en cuanto nos intentemos conectar.

Para nuestro experimiento, vamos a asegurarnos que nos podemos conectar a nuestra propia máquina Linux sin restricciones de seguridad. Para ello el archivo ‘/etc/hosts.allow’ debe decir:

ALL:localhost.localdomain,127.0.0.1

Y el archivo ‘/etc/hosts.deny’:

ALL:ALL

Hay una limitación de seguridad con nuestro cliente. Como no lo vamos a correr como root sino como un usuario no privilegiado, el cliente no va a poder abrir puertos menores a 1024. Mountd no le va a permitir a usted conectarse desde un puerto inseguro (mayor que 1024):

Jul 20 22:55:15 localhost rpc.mountd: refused mount request from localhost.localdomain for /usr/local/src (/usr/local/src): illegal port 32791

La cura es fácil (/etc/exports, puede revisar los mensajes de autenticación exitosos con ‘tail -f /var/log/messages‘):

/usr/local/src 127.0.0.1/255.0.0.0(insecure,ro) 192.168.0.0/255.255.255.0(insecure,ro)

Una vez que el demonio de NFS está arriba y que vemos que funciona entonces podemos empezar a jugar con el cliente de monitoreo. Lo primero que tiene que hacer es desempacar el archivo zip que se encuentra en Source Forge, y ponga los archivos JAR en el ClassPath.

La pregunta es, para alguien que no sabe nada de ONC RPC (yo por ejemplo), ¿que tan dificil puede ser hacer un programa que haga ping, basandose sólo en los RFC?. Primero que nada vamos a hacer un “hola mundo“, el cual sólo va a tratar de conectarse al demonio apropiado de RPC (para ello vea las lineas que resalté en rojo en el paso anterior). El código es el siguiente (basado en los métodos descritos en el JavaDoc):

   1:package com.blogspot.elangelnegro.nfs;
2:
3:import java.net.InetAddress;
4:
5:import java.io.IOException;
6:
7:import org.acplt.oncrpc.OncRpcClient;
8:import org.acplt.oncrpc.OncRpcProgramNotRegisteredException;
9:import org.acplt.oncrpc.OncRpcException;
10:import org.acplt.oncrpc.OncRpcProtocols;
11:
12:public final class Ping {
13:
14: /**
15: * Error exit code
16: */
17: public static final int ERROR_EXIT_CODE=192;
18:
19: /**
20: * The RPC program ID of NFSD
21: */
22: public static final int RPC_PROGRAM_NFSD=100003;
23:
24: /**
25: * The RPC program version for NFSD
26: */
27: public static final int RPC_PROGRAM_NFSD_VERSION=4;
28:
29: public static void main(String [] args) throws Exception {
30: OncRpcClient client = null;
31: if ( ! ( (args != null) && (args[0] != null)) ) {
32: throw new IllegalArgumentException("Please specify the host where the NFS daemon is running!");
33: }
34: try {
35: client = OncRpcClient.newOncRpcClient(
36: InetAddress.getByName(args[0]),
37: RPC_PROGRAM_NFSD,
38: RPC_PROGRAM_NFSD_VERSION,
39: OncRpcProtocols.ONCRPC_TCP);
40: client.call(0, XdrVoid.XDR_VOID, XdrVoid.XDR_VOID);
41: } catch ( OncRpcProgramNotRegisteredException exp) {
42: System.out.println("ONC/RPC program server not found");
43: throw exp;
44: } catch (OncRpcException exp) {
45: System.out.println("Could not contact portmapper:");
46: exp.printStackTrace(System.out);
47: throw exp;
48: } catch (IOException exp) {
49: System.out.println("Could not contact portmapper:");
50: exp.printStackTrace(System.out);
51: throw exp;
52: } finally {
53: if (client != null) {
54: try {
55: client.close();
56: } catch (Exception ignore) {
57: // Do nothing
58: }
59: }
60: }
61: System.out.println("NFS daemon is registered with RPC");
62: }
63:
64:}

Compilamos el programa, lo corremos y si todo está bien deberiamos ver lo siguiente:

java com.blogspot.elangelnegro.nfs.Ping localhost
NFS daemon is registered with RPC

Pero, ¿Y se puede hacer algo más interesante, como leer que archivos hay en el directorio que está siendo exportado usando NFS?. No hay almuerzo grátis así que hacemos una pausa y nos leemos los RFC relacionados (RFC1831 y RFC1094).

Haciendo un resumen ultra violento, nos encontramos que:

  1. Primero debemos hablar con el protocolo mount el cual nos va a dar el punto de partida para poder conectarnos a NFS. Vea el apendice A del RFC 1094 para más detalles.
  2. Una vez obtenido los valores con Mount, es entonces que usamos NFS.

No se asuste, las analogías con como usted utiliza mount son identicas; Mount se encarga de lidiar con problemas de permisología, autenticación, etc y luego es con NFS que usted puede empezar a trabajar con sus archivos, pero una vez que el directorio está montado.

Si estamos utilizando un servidor bajo Linux, quizas lo más fácil es utilizar el compilador de archivos X el cual nos dará las clases de Java necesarias (les muestro un pequeño wrapper, usted haga los cambios necesarios):

#!/bin/bash
# Check the IBM RedBook: http://www.redbooks.ibm.com/redpapers/pdfs/redp3882.pdf
# Author: josevnz@users.sourceforge.net
# Blog: El Angel Negro – http://elangelnegro.blogspot.com
#
CLASSPATH=/home/josevnz/jars/jrpcgen.jar:/home/josevnz/jars/oncrpc.jar:/home/josevnz/jars/portmap.jar:.
DESTDIR=/home/josevnz/java/NFSPing/src/com/blogspot/elangelnegro/nfs/client/
mkdir -p $DESTDIR && rm -f $DESTDIR/* && cd /usr/include/rpcsvc && for stub in nfs_prot.x mount.x ; do
/usr/java/jdk1.5.0_03/bin/java -jar /home/josevnz/jars/jrpcgen.jar -p com.blogspot.elangelnegro.nfs.client -withcallinfo -d $DESTDIR $stub
done

Esto va a generar un montón de clases en Java, las cuales van a ser utilizadas por nuestra aplicación. Busque por aquellas que dicen “client*.java” ya que con ellas llamaremos a nuestro servidor RPC. Lo otro que notará en el código es que cada uno de los métodos remotos descritos en el RFC tiene su contraparte en las clases clientes de Java. Por ejemplo, esto es lo que tengo en mi directorio después de correr el script.

[josevnz@localhost ~]$ cd java/NFSPing/src/com/blogspot/elangelnegro/nfs/client/[josevnz@localhost
client]$ ls
attrstat.java groupnode.java nfsstat.java
createargs.java groups.java nfstime.java
dirlist.java linkargs.java readargs.java
diropargs.java mountbody.java readdirargs.java
diropokres.java mountClient.java readdirres.java
diropres.java mount.java readlinkres.java
dirpath.java mountlist.java readokres.java
entry.java mountServerStub.java readres.java
exportnode.java name.java renameargs.java
exports.java nfscookie.java sattrargs.java
fattr.java nfs_fh.java sattr.java
fhandle.java nfspath.java statfsokres.java
fhstatus.java nfs_protClient.java statfsres.java
filename.java nfs_prot.java symlinkargs.java
ftype.java nfs_protServerStub.java writeargs.java
[josevnz@localhost client]$

Bueno, espero que esta pequeño articulo le haya resultado útil y entretenido. Conseguí un tutorial muy bueno de ONC RPC y la gente de IBM muestra más de como usar Remote Tea con sus servidores.

En una segunda entrega les mostraré el código cliente de NFSPing, así como un enlace para que se baje el código.

Etiquetas en Technorrati:

La historia de Rafel, El Hacker Venezolano: ¿Cayo todo en el olvido?

Queens Tunnel
¿El final de el tunel para este Hacker? (este tunel lo tomas para ir de Queens a NYC)

No lo creo. La historia continua.

Este articulo de el Universal Digital me lo mando el amigo Jorge Camargo (el pana siempre me manda articulos interesantes); Allí describen como ya Rafael lleva 3 meses preso, mientras preparan el caso en su contra. Ya antes les había comentado acerca de este caso, lo que me parece curioso es que aquí en los Estados Unidos la prensa más comercial aún no le ha dado publicidad.

Y es que Rafael parece ser una celebridad; Por otro lado si cometió un delito de verdad, entonces no va a importar mucho si había tratado de enmendar sus actos.

¿Alguien tiene información más actualizada al respecto?

El caso de Luis Tascón: ¿En que quedó todo eso?

Luis Tascó

Hace como un mes atrás leí sobre el caso de Luis Tascón y sobre su imprudencia (hay otros epitetos que quiero evitar) al publicar números de cedula de identidad en Internet, los cuales le permitian a cualquiera verificar si una persona había firmado en el referendum o no. Vinieron los arrepentimientos, las “mea culpa” pero el daño ya estaba hecho, de repente eramos “ellos contra nosotros“. Vino la corrupción, gente vendiendo la oportunidad de ser “borrado” de la lista por un monto.

Hoy en día la aplicación ya no existe, pero la lista ya fué diseminada.

Curiosamente Tascón fué uno de los primeros articulos que escribí para este Blog, violando mi regla de no escribir nada de politica. En aquel momento me quejaba de la falta de seguridad e irresponsabilidad al publicar esa información en Internet. Sólo estaba viendo parte de el problema, nunca me imagine las ramificaciones que esto iba a tomar a la larga.

Discriminación, Venezolano contra Venezolano. No entendemos que así no vamos a poder salir de abajo, el pedazo de tierra y la cultura que nos une es la misma. Pero eso no es suficiente.

¿Porqué me entró ese ataque de nostalgía? Después de todo ya nadie habla más de el caso Tascón, el daño ya está hecho y en teoría todos estamos dispuestos a olvidar…

Revizando en mi correo me conseguí este joya (mucho antes de que Tascón se arrepintiera, por la fuerza), en el cual se ofrece una tregua con condiciones:

——- Original Message ——–

Subject: Re: Perfil
Date: Mon, 23 Aug 2004 08:46:00 -0500 (CDT)
From: Henry Useche
To: iturri@ula.ve
Es válido, Edgar, échale pierna. que envien
curriculum a useche@ula.ve
Chao y gracias

--- Edgar Iturriaga
escribió:
> En esta cuenta email, lo acabo de leer.
>
> Te llamé para proponerte que se hiciera un llamado
> público. Pienso que si firmó o no firmó, no debería
> importar; siempre y cuando sea un profesional
> dispuesto a formarse y que acepte la constitución
> bolivariana como norma de convivencia. Claro, son
> ustedes los que decidiran, pero creo que ya debemos
> comenzar la revolución en la revolución y no podemos
> estar cobrándole toda la vida a aquellos que
> firmaron.
>
> ¿Podemos hacer pública esta necesidad de la misión
> sucre? a ver cuantos se presentan. Por la via del
> boca a boca, no he tenido éxito.
>
> Recibe un abrazo y espero me respondas por ambas
> cuentas email: esta y la de la ULA.
>
> Un abrazo,
>
> Edgar
>
> Henry Useche wrote:
>

Increible las cosas que uno puede guardar en su correo electrónico.

Sólamente el trabajo y la dedicación van a sacar a el pais de abajo; Por lo general siempre termino mis “articulos” con una conclusión, pero realmente no se que decir al respecto, excepto que espero que esto no se repita nunca más.

Opinión de libro: Unix Backup & Recover, de W.Curtis Preston.

Ya antes les habia comentado acerca de los libros de O’Reilly; Son concisos, directos al grano, actuales y sobre todo muy relevantes en cada uno de lso tópicos cubiertos.

Voy a contarles acerca de una experiencia que tuve con esto de los respaldos:

Un dia se nos llena el disco duro en la compañia X. El sistema de monitoreo nos alerta de que una de las particiones está al %100 (con todo y quotas) y nos manda un correo. Después de investigar un poco, nos armamos una lista detallada de quienes y como son los principales causantes de la crisis. Como buenos administradores sugerimos varias formas no traumaticas de solucionar el problema, entre ellas hacer respaldos a cinta para luego eliminar los archivos que no son utilizados.

El lider de desarollo (un tirano poco inteligente, el cual sólo sabe gritar pero no delegar) decide que la mejor manera de resolver el problema es eliminar todo el código de producción, y que cada quien arregle el problema como pueda. El tomó una decisión “ejecutiva” (si, el mismo personaje ha estado en terapia con profesionales varias veces por problemas de conducta, pero ese cuento es para otra historía).

Al día siguiente nos conseguimos con no menos de 15 correos, en los que los desarrolladores nos ruegan que recuperemos los archivos borrados, ya que el el código (el cual nunca fué colocado en una herramienta parecida a CVS) es utilizado para correr cálculos financieros para luego correr reportes.

La gran cagada.

Por suerte nosotros utilizabamos Amanda; fué cuestión de intercambiar dos cintas, extraer todo a un directorio y la crisis fué solucionada en dos horas. Pero Amanda funcionó porque nosotros ya antes habíamos probado el sistema de respaldos y rutinariamente revizamamos las cintas. Además teníamos varias semanas de respaldos.

En pocas palabras, estabamos preparados (esta vez)…

Como puede ver no se necesita una falla en un disco para ocasionar una tragedía; Sólo hace falta una desarrolladora escribiendo ‘chown -R juleitsi:yula /’ o un desarrollador neurotico con poder para acabar con la confianza de los clientes, todo por interrupción del servicio (si, de nuevo, son casos tomados de la vida real). En pocas palabras errar es de humanos y seguramente es usted quien terminará acomodando el lio…

Este libro es uno de mis favoritos; Trata sobre como hacer respaldos. Ahh, si respaldos, gran cosa… hasta que la responsabilidad de hacerlos le cae a usted encima y usted se da cuenta que que la última línea de defensa entre una catastrofe en un compañia es usted.

Pero lo mejor de hacer respaldos no es sólo como hacerlos sino como recuperar la información que usted guardó; Otro aspecto al cual casi no se le presta atención es como validar que esos respaldos de verdad funcionan. Este libro le va a decir como.

Esta increiblemente bien escrito; Habla de tópicos desde como respaldar con Amanda, Dump, Tar, respaldo de bases de datos como Oracle, Sybase hasta respaldo de repositorios de software como CVS o Clearcase.

Otra cosas curiosa es que el libro está lleno de anecdotas (siempre las hay) y ciertas historías de terror, de las cuales espero que usted nunca sea el protagonista.

Este es uno de esos libros que se pagan sólos, y debería tenerlo en su biblioteca de consulta, porque nunca se sabe.

¿Y usted, se come sus vegetales, se cepilla los dientes y hace sus respaldos? Si, las 3 cosas son una ladilla, pero en algún momento se hacen valiosas…

Echando código: Bjarne Stroustrup nos dice que hay que estudiar más…

El papá de C++ nos dice que un plomero pasa 8 años estudiando, sólo para acomodar un baño; En comparación, ¿cuanto tiempo debería gastar un desarrollador de software para hacer el software de aun avión lleno de pasajeros?

A mi siempre me dió risa como después de el periodo “.com” un montón de carajos auto llamados “computistas” salieron despedidos en masa, porque sólo sabian hacer paginitas HTML y macros en Visual Basic; No quiero implicar que la herramienta los definió sino más bien su falta de conocimiento en otras áreas.

Ahora con paises como la India y China produciendo excelentes desarrolladores, más baratos incluso que los Venezolanos, ¿qué es lo que queda por hacer?

Ah estudiar, y más nada. Los mangos bajitos se acabaron y la hora de echarle bolas.

¿Y usted, que opina? El hecho de incrementar la cantidad de horas de práctica, ¿puede hacer que salgan mejores programadores de las Universidades?

Feliz cumpleaños Java: 10 años ya han pasado

Si, y aunque les llovió a Sun durante su celebración, no deja de ser un acontecimiento . Es increible en como sólo 10 años el lenguaje es una herramienta utilizada a todos los niveles…

¿Quien iba a pensar que un lenguaje que fué conocido en el principio sólo por que podía hacer Applets ahora está hasta en el celular de última generación?

Macho Man Sandwich: ¡Yo tengo uno en mi honor!

Si, resulta que Joe (el dueño de esta deli) me homenajeo hace tiempo y me dedico un Sandwich llamado ´The Macho Man´. La combinación de ingredientes es brutal y esto estuvo pegado en su tienda por un buen tiempo hasta que me regalo el afiche.

Macho Man Sandwich

Lo curioso es que aparezco como un torero, 20 veces más delgado en la foto. Pero, ¡hey, no todos los días te dedican un Sandwich en tu honor!.

Jenna Marie´s Deli & Catering

Así que sí algun día visitan Stamford, 500 Summer Street, los invito a que pidan un ‘Macho Man‘, por sólo $5.25 🙂

Trucos Unix: ¿Como mandar anexos más grandes de 20MB por Yahoo?

Es un lio y no se puede, ya que el el sistema tiene un limite de seguridad para 20MB. Mi problema era que queria enviar unas fotos que tomé este sabado y comprimidas ocupaban más de 27MB.

La solución es trivial, hacer dos archivos tar.gz comprimidos para luego mandar un par de correos; La excusa es como hacer los archivos tar sin mucho esfuerzo:

[josevnz@localhost fotos]$ tar -czvf fotos1.tar.gz DSC003{17,18,19,20,21,22,23,24,25,26,27}.JPG

[josevnz@localhost fotos]$ tar -czvf fotos2.tar.gz DSC003{28,29,30,31,32,33,34,35,36}.JPG
DSC00328.JPG
DSC00329.JPG
DSC00330.JPG
DSC00331.JPG
DSC00332.JPG
DSC00333.JPG
DSC00334.JPG
DSC00335.JPG
DSC00336.JPG
[josevnz@localhost fotos]$ ls -lh
total 53M
-rw-rw-r– 1 josevnz josevnz 1.3M Apr 9 11:01 DSC00317.JPG
-rw-rw-r– 1 josevnz josevnz 1.4M Apr 9 11:02 DSC00318.JPG
-rw-rw-r– 1 josevnz josevnz 1.4M Apr 9 11:02 DSC00319.JPG
-rw-rw-r– 1 josevnz josevnz 1.4M Apr 9 11:03 DSC00320.JPG
-rw-rw-r– 1 josevnz josevnz 1.5M Apr 9 11:04 DSC00321.JPG
-rw-rw-r– 1 josevnz josevnz 1.4M Apr 9 11:05 DSC00322.JPG
-rw-rw-r– 1 josevnz josevnz 1.5M Apr 9 11:06 DSC00323.JPG
-rw-rw-r– 1 josevnz josevnz 1.2M Apr 9 11:10 DSC00324.JPG
-rw-rw-r– 1 josevnz josevnz 1.3M Apr 9 12:35 DSC00325.JPG
-rw-rw-r– 1 josevnz josevnz 1.3M Apr 9 12:35 DSC00326.JPG
-rw-rw-r– 1 josevnz josevnz 1.2M Apr 9 12:35 DSC00327.JPG
-rw-rw-r– 1 josevnz josevnz 1.2M Apr 9 12:35 DSC00328.JPG
-rw-rw-r– 1 josevnz josevnz 1.5M Apr 9 13:04 DSC00329.JPG
-rw-rw-r– 1 josevnz josevnz 1.5M Apr 9 13:04 DSC00330.JPG
-rw-rw-r– 1 josevnz josevnz 1.4M Apr 9 13:04 DSC00331.JPG
-rw-rw-r– 1 josevnz josevnz 1.4M Apr 9 13:04 DSC00332.JPG
-rw-rw-r– 1 josevnz josevnz 1.4M Apr 9 13:05 DSC00333.JPG
-rw-rw-r– 1 josevnz josevnz 1.3M Apr 9 13:05 DSC00334.JPG
-rw-rw-r– 1 josevnz josevnz 1.4M Apr 9 13:05 DSC00335.JPG
-rw-rw-r– 1 josevnz josevnz 1.4M Apr 9 13:05 DSC00336.JPG
-rw-rw-r– 1 josevnz josevnz 15M Apr 10 11:03 fotos1.tar.gz
-rw-rw-r– 1 josevnz josevnz 13M Apr 10 11:03 fotos2.tar.gz
[josevnz@localhost fotos]$

Un truco más para su colección.

(Por cierto, para abrir las fotos solo necesita utilizar tar -xzvf archivo.tar.gz. Si es un usuario de Windows, bajese Winzip de Internet y abralo con el programa, el efecto es el mismo).

Capturan a Hacker Venezolano: Rafaél Núñez (RaFa)

El pana Jorge Camargo me indicó una noticia que salió publicada en el Diario El Universal el día de hoy, acerca de un “Hacker Venezolano” el cual fué encarcelado.

Un pequeño extracto de el articulo:

Cuando los sistemas fueron apagados y encendidos nuevamente un mensaje en la página electrónica del centro leía: ”Besa mi trasero porque el tuyo es mío!”, y un enlace llevaba a una página de ”hackers” llamada ”World of Hell”.

La pesquisa llevó a los agentes hasta varios ”hackers” que formaban parte del grupo y determinaron que el responsable por el ataque fue uno que se hacía llamar ”RaFa”, luego identificado como Rafael Nuñez, un ciudadano venezolano.

Buscando un poco en Google, me encontré que el chamito es una celebridad en los circulos de hacking. El artículo de el Universal no menciona si aparte de hackear la página hizo algo más, pero en otros lados dicen que se robo los planos de un transbordador espacial de la NASA (versión muy diferente a la de el Universal).

Parece ser que Rafael no es un “Black Hat” sino un “White Hat“, lo cual me hace sospechar de que es lo que realmente está ocurriendo tras su encarcelamiento, ademas de que las versiones de los periodicos en línea no coinciden (la noticia de la NASA parece ser vieja, del ‘ 08/12/2002‘).

La compañia para la cual trabaja se llama Scientech.

¿Alguien sabe algo más sobre el caso de RaFa? (les dejo una entrevista de el carajito con otros Hackers)

Echando código: Respaldando las fotografías de Flickr usando Java y XML-RPC

Ya en otra oportunidad les habia mostrado como ver la lista de fotos en Flickr utilizando Java y XML-RPC; El principio de bajarse las imagenes es muy sencillo:

  1. Obtener el NSID de flickr, dada la dirección de correo
  2. Buscar la información de todas las imagenes asociadas a este NSID y construir un URL
  3. Por cada imagen, bajarsela de Flickr usando el URL de el paso anterior
  4. Repetir hasta que no hayan más imagenes.

La primera versión hace el trabajo:

[josevnz@localhost FlickrBackup]$ time java -jar /home/josevnz/java/FlickrBackup/dist/FlickrBackup-1.0.jar disposablehero3000-flickr3@yahoo.com password /home/josevnz/tmp/fotos
Connecting as: disposablehero3000-flickr3@yahoo.com
Pictures found: 53

real 1m52.368s
user 0m4.103s
sys 0m0.642s

Me toma casi dos minutos bajarme 53 fotos, asi que ¿Como lo puedo hacer más rápido?; Aparte de tener un buffer más grande para las imagenes (escritura / lectura) se me ocurre que puedo crear más ‘threads’, asumiendo que no me importa gastar todo mi ancho de banda bajandome imagenes, además de crear un montón de procesos en mi computadora. Cambiando solamente la forma en como guardo los datos (buffer más grande):

Connecting as: disposablehero3000-flickr3@yahoo.com
Pictures found: 53

real 1m42.083s
user 0m7.394s
sys 0m0.696s
[josevnz@localhost FlickrBackup]$

No se ganó mucho. Pero quizas pudiera utilizar una técnica comunmente utilizada en los navegadores, la cual es utilizar varias hebras para traerse los recursos a través de lared, en este caso mis fotos. Para utilizar ‘threads’ debo hacer un cambio no tan trivial en la aplicación; Esta deberá controlar cuantas hebras son creadas, y deberá esperar a que todas ellas terminen antes de matar a la hebra principal. En resumen la estrategia es la siguiente:

  1. Cree no más de ‘n’ hebras de ejecución simultaneamente.
  2. Cada hebra se baja una foto en particular y le reporta a la clase controladora como le fué en el proceso.
  3. Al final la clase controladora indica como le fué a cada una de las hebras.

Cualquiera que le diga que utilizar ‘Threads’ es fácil le está mintiendo. Si bien vienen incluidas con el lenguaje, lo cual hace su uso más fácil, no subestime sus potenciales problemas de mantenimiento, además de que no todos los programas se benefician de esta técnica (aqui estamos asumiendo que bajarse una imagen de la red es mucho más lento que generar otra hebra de ejecución para hacer lo mismo).

Primero el código de la clase que se baja las imagenes:

   1:package com.blogspot.elangelnegro.flickr;
2:
3:import java.net.URL;
4:
5:import java.io.IOException;
6:import java.io.ByteArrayInputStream;
7:import java.io.File;
8:import java.io.FileOutputStream;
9:import java.io.BufferedOutputStream;
10:import java.io.InputStream;
11:import java.net.MalformedURLException;
12:
13:import java.util.concurrent.CountDownLatch;
14:import java.util.concurrent.BrokenBarrierException;
15:
16:/**
17: * This class downloads a image from a given URL.
18: * For more information, please check the following URLS:
19: * <ul>
20: * <li><a href="http://java.sun.com/j2se/1.5.0/docs/api/">Java API</a>
21: * <li><a href="http://java.sun.com/j2se/1.5.0/docs/guide/2d/spec/j2d-bookTOC.html">Programmer's Guide to the JavaTM 2D API</a>
22: * </ul>
23: * License: GPL
24: * Blog: http://elangelnegro.blogspot.com
25: * @author Jose Vicente Nunez Zuleta
26: * @version 0.1 - 04/03/2005
27: */
28:public final class Download implements Runnable {
29:
30: private String server;
31: private String id;
32: private String secret;
33: private String title;
34: private File dir;
35: private URL url;
36: private boolean downloadStatus;
37: private CountDownLatch startSignal;
38: private CountDownLatch endSignal;
39:
40: /*
41: * Buffer size for network reading
42: */
43: private static final int BUFFER_SIZE = 819200;
44:
45: private static final NullPointerException NULLEXCEPTION = new NullPointerException();
46:
47: /**
48: * Class constructor
49: * @param server server name where the picture is stored
50: * @param secret secret that defines the image
51: * @param title Title given by the user to the image
52: * @param dir Local directory where the image will be stored
53: * @param barrier Barrier object used to notify the controler than this thread finished attempting downloading the resource
54: * @throws MalformedURLException
55: * @throws NullPointerException If any required parameter is missing. All of them are
56: */
57: public Download(String server, String id, String secret, String title, File dir, CountDownLatch startSignal, CountDownLatch endSignal) throws MalformedURLException {
58: if (server == null) {
59: throw NULLEXCEPTION;
60: }
61: this.server = server;
62: if (id == null) {
63: throw NULLEXCEPTION;
64: }
65: this.id = id;
66: if (secret == null) {
67: throw NULLEXCEPTION;
68: }
69: this.secret = secret;
70: if (title == null) {
71: throw NULLEXCEPTION;
72: }
73: this.title = title;
74: if (dir == null) {
75: throw NULLEXCEPTION;
76: }
77: this.dir = dir;
78: if (startSignal == null) {
79: throw NULLEXCEPTION;
80: }
81: this.startSignal = startSignal;
82: if (endSignal == null) {
83: throw NULLEXCEPTION;
84: }
85: this.endSignal = endSignal;
86: // Construct the actual URL
87: url = new URL("http://photos" + server + ".flickr.com/" + id + "_" + secret + "_o.jpg");
88: downloadStatus = false;
89: }
90:
91: /**
92: * Tell if this class was able to download the resource.
93: * It will return false if the method getPhoto has not been called
94: * @see #getPhoto
95: * @returns boolean
96: */
97: public boolean getStatus() {
98: return downloadStatus;
99: }
100:
101: /**
102: * Download the given picture into the local filesystem
103: * @throws IOException Reading / writting the image data
104: */
105: private void getPhoto() throws IOException {
106: /*
107: * Grab all the picture metadata information. It will be required if we want to upload the picture back
108: * to Flickr.
109: * Check: http://www.flickr.com/services/api/flickr.photos.getInfo.html
110: */
111: BufferedOutputStream image = null;
112: InputStream data = null;
113: byte [] buffer = null;
114: try {
115: // Prepare a file for writting
116: image = new BufferedOutputStream(
117: new FileOutputStream(
118: dir.getCanonicalPath() +
119: System.getProperty("file.separator") +
120: id +
121: "_" +
122: secret +
123: "_o.jpg"
124: )
125: );
126: data = url.openStream();
127: int bytes = -1;
128: buffer = new byte[BUFFER_SIZE];
129: // Save the image
130: while( (bytes = data.read(buffer, 0, buffer.length)) != -1) {
131: image.write(buffer, 0, bytes);
132: }
133: if (image != null) {
134: image.flush();
135: }
136: downloadStatus = true;
137: } catch (IOException ioexp) {
138: throw ioexp;
139: } finally {
140: buffer = null;
141: try {
142: if (image != null) {
143: image.close();
144: }
145: } catch (IOException ioexp) {
146: ioexp.printStackTrace();
147: }
148:
149: try {
150: if (data != null) {
151: data.close();
152: }
153: } catch (IOException ioexp) {
154: ioexp.printStackTrace();
155: }
156: }
157: }
158:
159: /**
160: * This method is called by the Thread manager
161: */
162: public void run () {
163: try {
164: // Wait to be awaken...
165: startSignal.await();
166: // Wrap the call to download the photo
167: getPhoto();
168: endSignal.countDown();
169: } catch (InterruptedException intExp) {
170: intExp.printStackTrace();
171: } catch (Exception exp) {
172: exp.printStackTrace();
173: } finally {
174: // Don't do anything here
175: }
176: }
177:}

Aqui utilizamos una barrera (en el caso de Java 1.5.0, un Latch), la cual nos permite notificarle a las demás hebras en Java que ya terminamos de ‘tratar’ de bajar la imagen. Ahora el código de la clase que se encarga de obtener la información de las imagenes y de coordinar la ejecución de las hebras:

   1:package com.blogspot.elangelnegro.flickr;
2:
3:import java.util.ResourceBundle;
4:import java.util.Properties;
5:import java.util.Vector;
6:import java.util.Hashtable;
7:
8:import javax.xml.parsers.DocumentBuilder;
9:import javax.xml.parsers.DocumentBuilderFactory;
10:import javax.xml.parsers.FactoryConfigurationError;
11:import javax.xml.parsers.ParserConfigurationException;
12:
13:import org.xml.sax.SAXException;
14:import org.xml.sax.SAXParseException;
15:
16:import org.w3c.dom.Document;
17:import org.w3c.dom.DOMException;
18:import org.w3c.dom.NodeList;
19:import org.w3c.dom.Node;
20:
21:import java.net.URL;
22:
23:import java.io.IOException;
24:import java.io.ByteArrayInputStream;
25:import java.io.File;
26:import java.io.FileOutputStream;
27:import java.io.BufferedOutputStream;
28:import java.io.InputStream;
29:
30:import org.apache.xmlrpc.XmlRpcClient;
31:import org.apache.xmlrpc.XmlRpcException;
32:import org.apache.xmlrpc.XmlRpc;
33:
34:import java.util.concurrent.CountDownLatch;
35:import java.util.concurrent.BrokenBarrierException;
36:import java.util.concurrent.TimeUnit;
37:
38:/**
39: * I wrote this application because Flickr doesn't have an automatic way to do
40: * backups of the original images. For that reason I decided to make a quick
41: * backup utility that uses the Flickr API to get the list of photos
42: *
43: * For more information, please check the following URLS:
44: * <ul>
45: * <li> <a href="http://www.flickr.com/services/api/misc.urls.html">Flickr URL format</a>
46: * <li> <a href="http://www.flickr.com/services/api/">Description of the API</a>
47: * <li> <a href="http://www.flickr.com/forums/help/5304/">The problem I had to solve :)</a>
48: * <li> <a href="http://ws.apache.org/xmlrpc/client.html">Apache XML-RPC</a>
49: * <li> <a href="http://prdownloads.sourceforge.net/elangelnegro/DownloadPublicPictures.plx?download">Original Perl version I wrote</a>
50: * </ul>
51: * License: GPL
52: * Blog: http://elangelnegro.blogspot.com
53: * @author Jose Vicente Nunez Zuleta
54: * @version 0.1 - 04/01/2005
55: */
56:public final class FlickrBackup {
57:
58: private static final ResourceBundle BUNDLE =
59: ResourceBundle.getBundle(FlickrBackup.class.getName());
60:
61: private static final Properties CONFIG = System.getProperties();
62:
63: /**
64: * Maximun number of pictures per page
65: */
66: public static final int MAX_PICTURES_PER_PAGE = 15;
67:
68: /**
69: * Hide the constructor
70: *
71: */
72: private FlickrBackup() {
73: // Empty
74: }
75:
76: /**
77: * Command line invocation
78: * @param args Command line args
79: * <ul>
80: * <li> args[0] Is the Flickr user
81: * <li> args[1] is the password for that account
82: * <li> args[2] is the directory where we will save all the photos
83: * </ul>
84: * @throws Exception if there is any error while downloading the pictures
85: */
86: public static void main(String [] args) throws Exception {
87: if (! ( (args != null) && (args.length == 3) )) {
88: throw new IllegalArgumentException(
89: BUNDLE.getString("com.blogspot.elangelnegro.flickr.FlickrBackup.main.messages.allparam")
90: );
91: }
92: File dir = new File(args[2]);
93: if ( dir.exists() && ! dir.isDirectory() ) {
94: throw new IllegalArgumentException(
95: BUNDLE.getString("com.blogspot.elangelnegro.flickr.FlickrBackup.main.messages.dirNotFound")
96: );
97: }
98: Vector params = new Vector();
99: Hashtable struct = new Hashtable();
100: DocumentBuilderFactory factory =
101: DocumentBuilderFactory.newInstance();
102: DocumentBuilder builder =
103: factory.newDocumentBuilder();
104: Document document = null;
105: XmlRpcClient xmlrpc = null;
106: try {
107: xmlrpc = new XmlRpcClient(
108: BUNDLE.getString("com.blogspot.elangelnegro.flickr.FlickrBackup.xmlrpc.url")
109: );
110:
111: /* Get the Flickr numeric user ID
112: * http://www.flickr.com/services/api/flickr.people.findByUsername.html
113: */
114: struct.put(
115: "api_key",
116: BUNDLE.getString("com.blogspot.elangelnegro.flickr.FlickrBackup.xmlrpc.key")
117: );
118: struct.put(
119: "find_email",
120: args[0]
121: );
122: params.add(struct);
123: String result =
124: (String) xmlrpc.execute (
125: "flickr.people.findByEmail",
126: params
127: );
128: document = builder.parse(
129: new ByteArrayInputStream(
130: result.getBytes()));
131: NodeList nodes = document.getElementsByTagName("user");
132: String userId =
133: nodes.item(0).getAttributes().getNamedItem("nsid").getNodeValue();
134: System.out.println(
135: BUNDLE.getString("com.blogspot.elangelnegro.flickr.FlickrBackup.main.messages.connecting") +
136: ": " +
137: args[0]
138: );
139: struct.clear();
140: params.clear();
141: int currentPage = 1;
142: int totalCount = 0;
143: int totalPages = 1;
144: String maxPages = String.valueOf(MAX_PICTURES_PER_PAGE);
145: /*
146: * Get the total number of photos and pages.
147: * This will control how many photos are downloaded
148: * at the same time.
149: * The only way to know that is to search
150: */
151: struct.put(
152: "api_key",
153: BUNDLE.getString("com.blogspot.elangelnegro.flickr.FlickrBackup.xmlrpc.key")
154: );
155: struct.put("user_id", userId);
156: struct.put("per_page", maxPages);
157: struct.put("page", String.valueOf(currentPage));
158: params.add(struct);
159: result = (String) xmlrpc.execute (
160: "flickr.photos.search",
161: params
162: );
163: struct.clear();
164: params.clear();
165: /*
166: * Get the photo and page count.
167: */
168: document = builder.parse(
169: new ByteArrayInputStream(
170: result.getBytes()));
171: nodes = document.getElementsByTagName("photos");
172: totalCount =
173: Integer.parseInt(nodes.item(0).getAttributes().getNamedItem("total").getNodeValue());
174: totalPages =
175: Integer.parseInt(nodes.item(0).getAttributes().getNamedItem("pages").getNodeValue());
176: System.out.println(
177: BUNDLE.getString("com.blogspot.elangelnegro.flickr.FlickrBackup.main.messages.found") +
178: ": " +
179: totalCount +
180: ", " +
181: totalPages
182: );
183: for (currentPage = 1; currentPage <= totalPages; currentPage++) {
184: /*
185: * Get now the photo list. Flickr has a limit of
186: * how many photos can show on a list (500) so get
187: * the first page, find how many photos are and then iterate
188: * all the pages. This number is an upper limit.
189: * For that we have to repeat the search on a loop.
190: */
191: struct.put(
192: "api_key",
193: BUNDLE.getString("com.blogspot.elangelnegro.flickr.FlickrBackup.xmlrpc.key")
194: );
195: struct.put("user_id", userId);
196: struct.put("per_page", maxPages);
197: struct.put("page", String.valueOf(currentPage));
198: params.add(struct);
199: result =
200: (String) xmlrpc.execute (
201: "flickr.photos.search",
202: params
203: );
204: struct.clear();
205: params.clear();
206: /*
207: * Get the photo list. Using this information, construct the phot URL as indicated here:
208: * http://www.flickr.com/services/api/misc.urls.html
209: */
210: ByteArrayInputStream xmlStream = new ByteArrayInputStream(result.getBytes());
211: document = builder.parse(xmlStream);
212: xmlStream.close();
213: nodes = document.getElementsByTagName("photo");
214: System.out.println(
215: "\t" +
216: BUNDLE.getString("com.blogspot.elangelnegro.flickr.FlickrBackup.main.messages.currPage") +
217: ": " +
218: currentPage +
219: ": " +
220: nodes.getLength()
221: );
222: // Prepare the latch
223: CountDownLatch startSignal = new CountDownLatch(1);
224: CountDownLatch doneSignal = new CountDownLatch(nodes.getLength());
225: for (int i = 0; i < nodes.getLength(); i++) {
226: String server = (String) nodes.item(i).getAttributes().getNamedItem("server").getNodeValue();
227: String secret = (String) nodes.item(i).getAttributes().getNamedItem("secret").getNodeValue();
228: String title = (String) nodes.item(i).getAttributes().getNamedItem("title").getNodeValue();
229: String id = (String) nodes.item(i).getAttributes().getNamedItem("id").getNodeValue();
230: // Download the picture
231: Thread thread = new Thread(
232: new Download(server, id, secret, title, dir, startSignal, doneSignal),
233: server + "-" + id + "-" + secret
234: );
235: thread.start();
236: } // End for
237: // Start all the threads
238: startSignal.countDown();
239: // We could do something else here, but we wont :)
240:
241: // Wait for all the other threads to finish
242: doneSignal.await(60*5, TimeUnit.SECONDS);
243: } // End launching threads - while
244: } catch (XmlRpcException xmlexp) {
245: throw xmlexp;
246: } catch (SAXException sxe) {
247: Exception exp = sxe;
248: if (sxe.getException() != null) {
249: exp = sxe.getException();
250: }
251: exp.printStackTrace();
252: } catch (IOException ioexp) {
253: throw ioexp;
254: } finally {
255: struct.clear();
256: params.clear();
257: }
258: }
259:}

Después de unos cambios, este es el tiempo de ejecución:


Connecting as: disposablehero3000-flickr3@yahoo.com
Pictures found: 53

real 0m32.087s
user 0m1.969s
sys 0m0.788s
[josevnz@localhost FlickrBackup]$

!Mucho más rápida!

La aplicación aún no está completa, sólo me concentré esta vez en mejorar su ejecución:

  • La versión final guarda tambien la metadata de cada foto además de la imagen, por lo que en teoría deberia ser fácil escribir una aplicación que pueda restaurar las imagenes de vuelta en Flickr.
  • La aplicación reportará si pudo bajar o no cada una de las imagenes.
  • La aplicación utilizará una interfaz gráfica (Swing?) lo cual hará más fácil su uso.
  • Averiguar correctamente la extensión de la imagen original. Ahorita asumo que todas son JPG lo cual es falso (el API de Flickr no soporta eso actualmente. Aunque hay una forma de saber.).

Les dejo un par de tutoriales sobre Threads (1, 2), y por supuesto con el enlace a el código. Pienso acomodarlo ahora para usar Swing de manera que sea más amigable para que pueda hacer sus respaldos.