[XKCD] Windows 7

Posted in Jokes, Uncategorized | Tagged | Leave a comment

Serialización con XML

En ciertas situaciones una forma más prolija de serializar un objeto consiste en convertir su estado en una definicion XML y guardarlo o enviarlo donde haga falta. Esto nos permite poder ver el estado del objeto sobre el stream persistido incluso modificarlo con un editor de texto, cosa impensable sobre la salida de una serializacion estandar. Para tal fin podriamos complicarnos usando XStream o alguno de los tantos OXM (Object XML Mapper) que hay dando vueltas por la red. Calma! Antes de bajar los 20MB de librerias que nos pide el Apache Digester podemos echarle un vistazo a dos clases incluidas en J2SE: XMLEncoder y XMLDecoder.

Estas clases sirven exactamente para nuestros propositos: Transformar objetos en definiciones XML, y definiciones XML en objetos, sin perder nada en el medio. Sin configuraciones ni descriptores extraños estas dos clase son tan faciles de usar como un ObjectInputStream y objectOutputStream.

Objeto -> XML

 Perro p = new Perro();
      FileOutputStream f = new FileOutputStream("perro.xml");
  XMLEncoder xe = new XMLEncoder(f);
      xe.writeObject(p);
      xe.close();

XML -> Objeto

  FileInputStream f = new FileInputStream("perro.xml");
  XMLDecoder xd = new XMLDecoder(f);
      Perro p = (Perro)xd.readObject();

Esta demas decir que el XML contiene todo el grafo del objeto (el perro con sus pulgas, collar, dueño y cualquier otra dependencia) haciendose cargo de forma impecable de las referencias circulares.

Los drawbacks de este metodo si lo comparamos con la serializacion tradicional es el mismo que se le atribuye a RMI vs Webservices: El XML es mas lento y pesado que su par binario. A favor de este método podemos decir que el objeto serializado es un xml que puede editarse en cualquier lado y que se elimina el problema del cambio de version de las clases. Ya no hay que preocuparse por conflictos de versionado con el serialVersionUID porque la unica informacion que se guarda en el xml sobre la clase de las instancias es el nombre.

Posted in Java | Tagged | 1 Comment

Contadores Atómicos

Cuanto tiempo sin escribir… en fin. Siempre me llamo la atención que en Java muchas de las operaciones sobre primitivos no son atómicas. El statement c++; por ejemplo no es atómico y puede traernos muchos dolores de cabeza en entornos de multithreading. Vale recordar, consideramos una instrucción como “atómica” si no puede ser interrumpida para darle paso a otro thread. En el siguiente ejemplo se presentan el caso de dos threads que consumen concurrentemente un contador. Si ejecutan el código podrán ver que no se obtiene el resultado esperado.

public class Main {

public static void main(String[] args) {
Contador c = new Contador();
new Corredor(c).start();
new Corredor(c).start();
}
}

class Corredor extends Thread {

private Contador contador;

@Override
public void run() {

for (int i = 0; i < 20; i++) {
try {
System.out.println(contador.incrementAndGet());
Thread.sleep(500);
} catch(Exception e) {
}
}

}

public Corredor(Contador contador) {
this.contador = contador;
}
}

class Contador {

private int value = 0;

public int incrementAndGet() {
return ++value;
}
}

En la pantalla terminamos con algo parecido a

1 2 2 3 3 4 4 5 6 6 7 7 8 8 9 9 10 10 11 ……

De alguna forma estamos forzando un poco esa situacion poniendo a dormir el Thread luego de llamar al metodo incrementAndGet() pero es una situacion que perfectamente se puede dar cuando hay muchos threads en ejecución. Podriamos corregir esta situación synchronizando el metodo incrementAndGet() pero produciría una sobrecarga que quizá no estemos dispuestos a pagar.

java.util.concurrent.atomic.AtomicInteger es la clase que vamos a usar para solucionar este problema. AtomicInteger se comporta como un Integer comun y corriente pero es mutable a traves de metodos atómicos. Lo mejor es que los metodos no son sincronizados de forma tradicional sino por otras oscuras tecnicas que en su momento trataré. Esta clase posee metodos tales como addAndGet(int delta) que incrementa en un delta de forma atomica, decrementAndGet(), getAndAdd(), getAndIncrement() y muchos otros que hacen más o menos lo que indican los nombres pero siempre siempre de forma atómica.

Más información en http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/atomic/AtomicInteger.html

Posted in Uncategorized | Leave a comment

Manejando XML con XStream

Existen muchas librerias que permiten manejar archivos XML dentro de Java. Tenemos desde Apache Commons Digester hasta StAX incorporado en la JRE6 pasando por incontables alternativas con menos fama. Si a estas alturas hay algun desprevenido que no sabe que es XML le recomiendo este articulo. Si bien hay muchas alternativas algunas son algo complicadas y otras presentan una sobreingenieria extrema para algo que parece tan trivial como el mapeo de un documento xml a un grafo de objetos.

XStream es una de esas librerias que hacen fáciles las cosas simples. Con XStream no hay que armar grandes descriptores para hacer funcionar el mapeo sino que todo apela a la vieja y querida API Reflection, además XStream mantiene al minimo las dependencias con otros proyectos (exactamente lo opuesto que ocurre con Digester que requiere casi todo el apache commons para funcionar). La forma de usar XStream es muy sencilla. Veremos como hacer para pasar de Objetos a XML y de XML a Objetos. Supongamos que tenemos una clase Persona y una Telefono.

public class Persona {
private String nombre;
private long id;
private String fechaNacimiento;
private float sueldo;
private Telefono[] telefonos = new Telefono[10];

//setters y getters.....

}

public class Telefono {
private String codigoPais;
private String codigoArea;
private String numero;
private String interno;
//getters y setters....

}

Más POJO imposible, ni siquiera hay que hacer a la clase Serializable o algo así. Para hacer mas claros estos ejemplos y no bombardear la pantalla con código inutil ;) voy a apoyarme en una clase Generator a partir de la cual obtengo un objeto Persona o un String con la definicion XML. La definicion XML estará en un archivo, o en internet o en cualquier lado, como sea, la operatoria es la misma.

Ejemplo XML -> Objeto

import com.thoughtworks.xstream.XStream;

public class TestRead {
public static void main(String[] args) {
XStream xs = new XStream();
xs.alias("persona",Persona.class);
xs.alias("telefono",Telefono.class);
Persona p = (Persona)xs.fromXML(Generator.getXML());
//listo! en 3 lineas hace la conversion de XML a un Objeto de clase Persona.
System.out.println(p.getId());
System.out.println(p.getNombre());
System.out.println(p.getSueldo());
for(Telefono t:p.getTelefonos())
System.out.println(t.getNumero());
}
}

Ejemplo Objeto -> XML

public class TestWrite {
public static void main(String[] args) {

Persona p = Generator.getPersona();
XStream xs = new XStream();
xs.alias("persona", Persona.class);
xs.alias("telefono",Telefono.class);
String xml = xs.toXML(p);

// toXML() devuelve un string con la definicion xml para el objeto p. Todo en 3 lineas!
System.out.println(xml);
}
}
Posted in Uncategorized | 2 Comments

Atando con Alambre

En mi país se usa la expresión “atar con alambre” cuando hablamos de una reparación que se hace de forma descuidada. Se lo ata con alambre y queda como queda. Esta expresión poco feliz ha sido usada para darle connotaciones negativas a una actividad tan noble como es arreglar cosas con un poco de alambre. Les propongo empezar a pensar diferente, animarlos a que aten las cosas con alambre con esta situacion hipotetica.

El Domingo nos levantamos y vamos al baño, diario bajo el brazo, a enterarnos de los acontecimientos del mundo. Llegado el momento de hacer desaparecer el hecho nos damos cuenta con que el botón del inodoro esta roto y no funciona. Nos convertimos en plomeros dominicales y, destornillador mediante, nos encontramos con que al mecanismo le falta un tornillito que sostiene todo el conjunto.

Las alternativas en este caso son atar el conjunto con alambre y que quede firme, pero con todos los prejuicios que implica “atarlo con alambre”, ó bien agarrar un buen pegamento epoxy y dejar el mecanismo firme como rulo de estatua. Obviamente optamos por el pegamento y seguimos la jornada tranquilos de que hicimos bien el trabajo.

A los pocos dias encontramos el bendito tornillo faltante tirado atras del inodoro, emocionados por la empresa por venir, nos disponemos a ponerlo en su lugar y dejar el mecanismo en su estado original. Demasiado tarde, el pegamento es imposible de sacar.

El problema con el pegamento, respecto al alambre, es que crea un acoplamiento muy grande entre las partes involucradas. El alambre, si es bien utilizado, puede ser tan fuerte como el pegamento pero tiene la flexibilidad de poder ser sacado facilmente cuando sea necesario facilitando el mantenimiento. Estos principios, asi como en la plomeria, se aplican al desarrollo de software.

Una práctica muy difundida en la actualidad es la denominada “Inyeccion de Dependencias” que propone que las dependencias entre clases no sean unidas con pegamento, sino atadas con alambre favoreciendo el viejo y querido “code to interfaces”.

Para hacerlo fácil. Miren la diferencia entre estas dos clases.

public class Pegamento {
    private Dependencia dep = new Dependencia();
}

public class Alambre {
    private DependenciaInterface dep;
    public Alambre(DependenciaInterface d) {
       dep = d;
    }
}

La clase Pegamento tiene una asociacion con un objeto de clase Dependencia y lo mismo ocurre con el Alambre. Este ultimo sin embargo no esta fuertemente acoplado con su dependencia. En primer lugar porque la dependencia no es una clase concreta sino una interfaz que puede ser implementada de muchas formas distintas, y en segundo lugar porque la dependencia no esta “hardcodeada” dentro de la clase, sino que es “inyectada” por medio del constructor.

En todo esto aparece un personaje denominado “contenedor de inyección de dependencia” que es el encargado justamente de ver que dependencia llega como parametro al constructor de la clase. Como esto se suele determinar declarativamente usando un archivo de configuracion XML la aplicacion se va “atando con alambre” sobre la marcha dandonos una flexibilidad enorme.

El contenedor más popular es Spring Framework que poco a poco se transformo en un stack de proyectos que proveen funcionalidades similares a J2EE pero con un menor impacto en nuestros desarrollos. PicoContainer es otro contenedor, mas liviano que Spring, que utiliza la inyeccion por setters para lograr su objetivo. Se vienen unos cuantos articulos sobre Spring asi que Stay Tuned!

Posted in Frameworks, Patterns, Spring, Uncategorized | Tagged , | 1 Comment

[XFiles] Cambiando el password del MySql

Cambiando passwords olvidados en MySql.

1) iniciar mysqld_safe --skip-grant-tables
2) conectarse con mysql -user=root
3) mysql> update user set Password=PASSWORD('nuevo-password');
mysql> flush privileges;
mysql> exit;
4) killall mysqld_safe

Entrar usando el nuevo-password.

Posted in Bofh | Tagged | Leave a comment

La inmutabilidad de los Objetos

En el mundo de la OOP existe un tipo de Objeto denominado “Inmutable” que consiste en un objeto en el que su estado, una vez definido, no cambia durante todo su tiempo de vida.

Java incorpora unos cuantas clases cuyas instancias tienen la propiedad de ser inmutables. Las clases que envuelven los tipos primitivos (Byte, Short, Character, Integer, Long, Float, Double) son inmutables. La clase String es inmutable. ¿Por qué son inmutables?¿Para complicarnos la existencia? No realmente…

Asi como en el experimento del Gato de Schrödinger creamos una clase Gato con un atributo estado de tipo String. El estado del gato nos dice si el gato esta “VIVO” ó “MUERTO”

public class Gato {
    private String estado;
    public void setEstado(String e) {
    this.estado = e;
    }
    public String getEstado() {
    return estado;
    }
}

vamos a suponer que la clase String tuviera un metodo setValue() que me permita cambiar la cadena de texto almacenada en una instancia particular de String. Es decir, supongamos que los objetos String no son inmutables y podemos cambiar el texto que contiene.


public class TestSchrödinger {
public static void main(String[] args) {
Gato gato = new Gato();
String e = "vivo";
gato.setEstado(e);
//El gato esta vivo :)
e.setValue("muerto");
//A nuestro String mutable le cambiamos el texto que encapsula.
System.out.println(e);
//Si esto es cierto entonces deberia salir impreso en la pantalla "muerto"
System.out.println(gato.getEstado());
//¿El gato esta vivo o esta muerto?

El problema de la mutabilidad consiste en que los parametros en Java siempre son pasados por valor, inclusos las referencias a objeto pero no el objeto al que apunta. Si un String, por ejemplo, podria cambiar el valor que contiene entonces aparecerian inconsistencia en el estado de los objetos que en algun momento incorporaron ese String a su estado.

Si no les quedo muy claro tal vez este video ayude un poco ;)

Posted in Java, Patterns | Leave a comment

TCP Port Knocking!

Port Knocking (PK) es una forma bastante ingeniosa y cuestionablemente segura de establecer una comunicación entre dos hosts mediante puertos cerrados. Tambien puede decirse que es una forma de “seguridad por oscuridad” pero yo particularmente no comparto esa visión.

Imaginen esta situación: Queremos acceder a un lugar que tiene 65536 puertas en su fachada. Golpeamos una puerta para nos abran y poder entrar pero no recibimos respuesta. Lo mismo ocurre con cualquier puerta que golpée. Del lado de adentro hay un conserje con instrucciones precisas que le indican que:

“Vas a abrir la puerta número 22 unicamente si alguien golpea primero las puertas 12865, 53271, 14780, 6912, 37312 y 43976″

El conserje se queda todo el dia prestando atención a que puertas golpea la gente que quiere ingresar y en que secuencia lo hace. Solo abre la puerta 22 si alguién previamente toca en la 12865, 53271, 14780,…etc. Simplemente ignora a todas las personas que no toquen en estas puertas en la secuencia correcta.

La técnica de PortKnocking consiste exactamente en el ejemplo anterior pero en lugar de edificio tenemos un host, en lugar de puertas tenemos puertos tcp y en lugar de conserje hay un daemon escuchando los SYN enviados para iniciar una coneccion tcp encargado de manejar un firewall. Cuando estos SYN llegan a los puertos indicados, que el cliente deberia conocer de antemano, el daemon se encarga de bajar el firewall que previamente bloqueaba todas las entradas.

Este método no reemplaza ningún otro mecanismo de autentificación pero puede servir para confundir un poco más a los posibles atacantes. La implementación no podria ser más simple. Basta con un programa que vigile los logs del firewall y en el momento que detecte la combinación correcta de tcp SYNs crea una regla para permitir el paso a un determinado puerto desde la IP que origino la secuencia de SYNs. Del lado del cliente alcanza con un script que, armado de un programa tipo nc, automatice la secuencia de intentos de conexiones al host destino. Tiempo atras, en mis epocas de BOFH, habia hecho una implementacion en PERL muy minimalista que espero poder encontrar y compartirla acá.

Existen muchas implementaciones dando vueltas por internet, algunas más avanzadas que otras, pero todas mantienen la misma filosofía. Más información en www.portknocking.org

Posted in Bofh, Seguridad | Tagged | 1 Comment

Rentas, Patentes y RegExp

Introducción

Esto es una historia real que me ocurrio hace unos instantes. No sabia si postearlo acá pero como es mi blog hago lo que quiero. Hace un rato queria ver cuanto debía del impuesto de patente de mi auto. Por motivos que no es prudente comentar acá hacia tiempo que no me llegaban las boletas y no reclamaba asi que quise ver si internet me daba una solución. Vaya si me dio una solución… Por prudencia no voy a poner la dirección donde pueden encontrar las páginas que destriparé en los párrafos siguientes, confio en que sabrán googlear ;)

Prolegómenos

Sin problemas encontré que en la página web de rentas de la ciudad autónoma de buenos aires es posible consultar el estado de la deuda con tan solo poner el número de patente. Sin embargo es necesario ingresar un “digito verificador” por motivos que, estimo, tienen que ver con salvaguardar la información del deudor. Dicho digito se encuentra impreso en la boleta pero como comente mas arriba, hace tiempo que no la tengo.

La página presentaba dos cuadros de texto donde introducir la patente y el digito verificador (se llama digito verificador a pesar de estar formado por Dos digitos). Puse la patente de mi auto y tire algunos digitos al azar. Naturalmente, no acerte con el digito verificador pero me llamó poderosamente la atención de que la validación se hacia instantaneamente y un hermoso alert() se encargaba de avisarnos de nuestro error. Algo olía a Javascript.

En pleno siglo XXI cuesta creer que alguien use javascript para realizar este tipo de validación, sin embargo las tecnicas de ofuscación de codigo a veces confunden a ciertas personas. Digo las confunde porque a veces se olvidan de ofuscar su código… asi que, firebug mediante, pude hacerme con el código de validación que comparto en este link.

Años atras, cuando el tiempo libre era algo que me sobraba, curioseando por ahí habia podido comprobar en incontables veces lo patético que son los emprendimientos informaticos del estado, sobretodo en lo referente a seguridad, desde el servicio meteorológico de las fuerzas aereas hasta el ministerio de educacion. Más cerca en el tiempo habia sufrido con las aplicaciones de rentas y los tramites via web de la afip. Sabía de las limitaciones del estado. Pero no sabia que se habian vuelto a superar…

Tratamiento

Todo empieza con una curiosa función titulada ValidarDigitoPatente() la cual, increiblemente, valida nuestra patente y el digito verificador. Efectivamente la lógica que hace la validación esta del lado del cliente y el codigo no esta ofuscado… bueno… no a la manera tradicional porque se las ingeniaron para que cueste entenderlo :)

Lo primero que hace esta función es validar que la patente ingresada sea valida (para los hermanos latinoamericanos en argentina las patentes, ó matriculas, de los autos tienen 3 letras seguidas de 3 digitos. ABC 123, DFG 167 y UIO 654 son ejemplos de patentes validas. Antiguamente se usaba una codificación formada por una letra y 7 numeros). El codigo que se encarga de hacer esto, contengan las risas, es el siguiente:

if (EsLetra(n.charAt(0))) {
if (n.length == 8 &&
isDigit(n.charAt(1)) &&
isDigit(n.charAt(2)) &&
isDigit(n.charAt(3)) &&
isDigit(n.charAt(4)) &&
isDigit(n.charAt(5)) &&
isDigit(n.charAt(6)) && isDigit(n.charAt(7))) {

Javascript posee una de las implementaciones mas lindas de expresiones regulares que existen. Los estatales argentinos, sin embargo, no saben usarlas. Esa maraña de código para chequear la validez de una patente se puede reducir a:

if(n.match('[a-zA-z]{3}[0-9]{3}')) {

Esta bien, no seria taaaaan grave que no dominen el arte oscuro de las expresiones regulares. Sucede que hay patentes de otro tipo de vehiculos, e incluso las patentes que se usaban antiguamente, que tienen otro formato. ¿Como hacen para evaluar los otros formatos? Facil, seguimos enmarañando el código.

if (isDigit(n.charAt(0))) {
if (n.length > 1 && isDigit(n.charAt(1))) {
if (n.length > 2 && isDigit(n.charAt(2))) {
if (n.length == 6 &&
EsLetra(n.charAt(3)) &&
EsLetra(n.charAt(4)) && EsLetra(n.charAt(5))) {

Más alla de esto lo que nos importa es poder averiguar el bendigo digito, que son dos, verificador. Para eso se valen de una enigmatica funcion Codigo(). Esta función recibe una letra y devuelve un número de dos cifras aparentemente asignados al azar, no soy un criptoanalista ni mire muy detenidamente la serie, pero esos numeros me parecen familiares. Bueno, con un array asociativo devolvemos como value dos digitos a partir de una letra que usamos como clave y listo. No necesariamente…

function Codigo(letra) {
if (letra == "a" || letra == "A") {
return "14";
} else if (letra == "b" || letra == "B") {
return "01";
} else if (letra == "c" || letra == "C") {
return "00";
//asi hasta el infinito y mas allá...

Detalles… detalles… ¿Pero como funciona la funcion validadora? Para seguir con la tradicion de los digitos del CUIT vamos a hacer un par de cuentitas con los valores que devuelve esta funcion y a otra cosa. En el caso de la patente de mi auto, con tres letras y tres digitos, el codigo verificador se evalua de esta forma:

//la patente esta en n
var a = new String(Codigo(n.charAt(0)) + Codigo(n.charAt(1)) + Codigo(n.charAt(2)) + n.charAt(3) + n.charAt(4) + n.charAt(5));
//quedaria a como un string de 9 digitos ya que cada letra se cambia por dos numeros y los ultimos 3 numeros quedan igual.
document.frmLogin.chapa_patente_copia.value = n;
//se copia la patente original (WTF?)
var digitos_impares = new String(a.charAt(0) * 1 + a.charAt(2) * 1 + a.charAt(4) * 1 + a.charAt(6) * 1 + a.charAt(8) * 1);
//aca empieza lo bueno: se castean los digitos en las posiciones pares de a y se suman entre ellos.
var digitos_pares = new String(a.charAt(1) * 1 + a.charAt(3) * 1 + a.charAt(5) * 1 + a.charAt(7) * 1);
//se hace lo mismo pero para las posiciones impares del string a
if (digitos_impares.length == 2) {
digitos_impares = new String(digitos_impares.charAt(0) * 1 + digitos_impares.charAt(1) * 1);
}
if (digitos_pares.length == 2) {
digitos_pares = new String(digitos_pares.charAt(0) * 1 + digitos_pares.charAt(1) * 1);
}
if (digitos_impares.length == 2) {
//aca es donde el agua nos empieza a tapar...
digitos_impares = new String(digitos_impares.charAt(0) * 1 + digitos_impares.charAt(1) * 1);
}
if (digitos_pares.length == 2) {
digitos_pares = new String(digitos_pares.charAt(0) * 1 + digitos_pares.charAt(1) * 1);
}
//aca es donde nos tapo el agua.
var digito_verificador = new String(digitos_impares + digitos_pares);

Con el agua al cuello puede darse la ¿casualidad? de que los benditos digitos_pares una vez sumados den una cifra de dos digitos, como 39, por lo que volvemos a sumarlos para obtener uno solo… aunque 3+9 da 11… deberiamos repetir el procedimiento y llegariamos como resultado a 2. No comprobe como se comportan los digitos que devuelve la funcion Codigo() para ver si podria darse el caso de llegar a situaciones que requieran volver a sumar los digitos del resultado hasta alcanzar un valor final de un solo digito. Los personajes de rentas aparentemente ya agotaron estas arduas cuestiones metafisicas y parece que con dos sumas siempre se llega a un resultado de un digito ¿maravilloso verdad? Para hacer esto no usaremos recursion, ni closures, ni un miserable ciclo while. Hacemos copy&paste del codigo y le prendemos una vela a los santos agradecidos por haber inventado el hardcoding.

Conclusión

Uno se encuentra con cosas raras en las operaciones informáticas de la administración pública. No puedo evitar reflexionar sobre estas cuestiones cada vez que tengo que hacer una declaracion jurada via internet en la pagina de la afip, o llenar un formulario de arba. Creo que la información de las personas es uno de los bienes más sensibles y deben ser manejados de la mejor forma por nuestras autoridades.

Lo importante resulto ser que el “digito verificador” de mi patente es el 43, uno más que el sentido de la vida, el universo y todo lo demás. Finalmente si alguien quiere saber cuanto debo de patentes esta es la respuesta que obtuve en el sitio de rentas:

ERROR DE COMUNICACION: INTENTE MAS TARDE.

Para todos los que llegan buscando como sacar su numerito de la suerte les dejo algo para que lo usen con fines beneficos solamente. Escriban la patente en el cuadro presionen en el boton, crucen los dedos y quiza se hagan con el numerito

Posted in Seguridad | Tagged | 9 Comments

[XFiles] Los 7 pasos a subversion

Bajo el nombre de [XFiles] van a aparecer las recetas perdidas que solia anotarme en mis tiempos de BOFH. Inaugurando la seccion un archivo que encontre en el /root de un server olvidado: Subversion en casi 8 pasos.

svn sin tunneling ni cosas raras en CentOS:
1) # yum install subversion
2) crear un directorio donde guardar los repos (ie: /home/svn/repositories)
3) crear un usuario/grupo svn para ejecutar el server.
4) iniciar el servidor svn con svnserve --daemon --root=/home/svn/repositories
5) crear un repositorio con:
#cd /home/svn/repositories
#sudo svn svnadmin create miRepo
6) editar el archivo /home/svn/repositories/miRepo/conf/svnserve.conf agregando
[general]
password-db = usuarios
realm = miRealm
auth-access=write
7) editar el archivo /home/svn/repositories/miRepo/conf/usuarios agregando
[users]
miusuario = mipassword
miusuario2 = miotropassword
8 ) enjoy!
Posted in Bofh | Tagged | Leave a comment
35665 pages viewed, 42 today
17305 visits, 10 today
FireStats icon Powered by FireStats