Consultar virustotal.com desde la lí­nea de comandos

Tuesday, 20. January 2009 17:16

Estoy haciendo unos scripts para mantener limpios (en la medida de lo posible) nuestros servidores windows. Buscando la mejor manera de hacerlo me dije… Y si en vez de instalar un antivirus, instalo 38. Alguien podría decir que es una locura a la par que un imposible. Pero llega Hispasec con su Virustotal.com y nos pone de forma totálmente gratuita 38 antivirus.

Y aquí es donde nos aprovechamos un poco de ellos. Tengo que decir que antes de publicar el código, consulté con su soporte si había alguna limitación de uso. La única limitación es el número de consultas por ip así que si alguna vez no os funciona, ya sabéis por qué es :P

El código es bastante simple. Virustotal.com tiene una base de datos de hashes ya procesados y es posible consultar un determinado hash. El código no sube el fichero a virustotal, solo consulta esta base de datos por lo que os podéis encontrar que piezas de malware no son detectadas como tales:

#!/usr/bin/perl

use strict;
use LWP;
use Digest::MD5;
use Getopt::Long;

my $md5 = Digest::MD5->new;
my $virhash;
my $file;
my $hash;

GetOptions  (	"file=s" => \$file,
				"hash=s" => \$hash
			);

if($file && $hash) {
	usage();
	exit 100;
}

if(!$file && !$hash) {
	usage();
	exit 100;
}

if(-f $file) {
	open FILE,$file;
	binmode(FILE);
	$virhash = $md5->addfile(*FILE)->hexdigest;
	print "[HASH] Using MD5 hash $virhash\n";
}
elsif ($hash) {
	$virhash = $hash;
	print "[HASH] Using hash $virhash\n";
}

my $ua = LWP::UserAgent->new;
push @{$ua->requests_redirectable }, 'POST';

my $resp = $ua->post('http://www.virustotal.com/vt/en/consultamd5',[ "hash" =>  $virhash , "x" => 138 , "y" => 24 ]);

if($resp->is_success) {
	my $data = $resp->content;

	$data =~ m,(
),m; my $virdata = $1; $virdata =~ m,>(\d+)/(\d+) ,; if($1 > 0) { print "Virus Found ($1/$2)\n"; exit 1; } else { print "Virus Not Found\n"; exit 0; } } else { print $resp->status_line; exit 100; } sub usage () { print "$0 (--file fichero | --hash hash)\n"; }

Si puedo y me dejan, dentro de unos días publicaré el código algo más avanzado. Pero para los que tengan iniciativa, diré que Win32::Process::Info es un gran comienzo para sacar la lista de procesos y la ruta del ejecutable asociado.

Hasta pronto!

Thema: Malware | Kommentare (0)

Explain e índices de dos campos

Monday, 17. November 2008 19:04

Hoy preguntaban por el canal #MySQL de freenode si era necesario usar un índice en una tabla que usaba para buscar a qué grupos pertenecía un usuario. Vamos, algo como esto:


mysql> desc t1\G
*************************** 1. row ***************************
Field: id
Type: int(11)
Null: NO
Key:
Default: NULL
Extra:
*************************** 2. row ***************************
Field: id2
Type: int(11)
Null: NO
Key:
Default: NULL
Extra:

No tiene índices intencionádamente. Así es como estaba la tabla del sujeto en cuestión. Por lo general, nunca es mala idea añadir algún índice salvo que andes mal de espacio y no solventes en gran medida el tiempo de búsqueda de un registro, cosa poco habitual. Vamos a recrear la situación.

Lo primero es crear las tablas y alimentarlas. Vamos a nuestra consola (mi opción preferida):


mysql> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`id` int(11) NOT NULL,
`id2` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1

Ahora toca añadir datos a la tabla. He usado http://www.generatedata.com/#generator, una aplicación web para generar valores de prueba. Tiene una limitación de 200 registros, aunque cabe la posibilidad de bajarse la aplicación para montárselo uno mismo o dar un donativo. Yo me lo bajé para generar 5000 registros. Vamos a ver lo que tarda en devolverme los 5000 registros:


mysql> select /*SQL_NO_CACHE*/ id,id2 from t1;

...

5000 rows in set (0.01 sec)

Vale, ahora veremos lo que tarda en devolver los registros de un determinado id:


mysql> select /*SQL_NO_CACHE*/ id,id2 from t1 where id=37;

...

53 rows in set (0.00 sec)

Perfecto. Para que fuera aún más real, tendríamos que meter muchos más registros. Dejo a cada cual que haga sus pruebas. De todas formas, no nos hacen falta los datos para saber cómo actúa Mysql. Y aquí viene en nuestra ayuda el comando EXPLAIN (http://dev.mysql.com/doc/refman/5.0/es/explain.html). Con él conocemos el plan de ejecución del optimizador de MySQL. Vemos un ejemplo:


mysql> explain select id,id2 from t1 where id = 37\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 38389
Extra: Using where

En este artículo no voy a explicar cada uno de los campos, ya haré otro sobre el tema, pero básicamente esto nos dice que la select no tiene ningún tipo de optimización porque:

– No hay índices posibles
– Por lo tanto, no se aplica ninguno
– El número de registros aproximados a ver son 38389, vamos, todos. Esto es lo que se llama un full scan de la tabla.

Por lo tanto, está claro que esta tabla es optimizable. Creamos un índice con el campo id, que es el que usamos en la búsqueda:


mysql> alter table t1 add index idx_id (id);
Query OK, 38389 rows affected (0.20 sec)
Records: 38389 Duplicates: 0 Warnings: 0

Y volvemos a hacer la query con el EXPLAIN


mysql> explain select id,id2 from t1 where id = 37\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: ref
possible_keys: idx_id
key: idx_id
key_len: 4
ref: const
rows: 50
Extra:
1 row in set (0.00 sec)

La cosa cambia, ya hay índices posibles e índices aplicados, el número de registros ha cambiado y el campo Extra está vacío. Pero vamos a ver una curiosidad:


mysql> select count(id) from t1 where id = 37\G
*************************** 1. row ***************************
count(id): 53
1 row in set (0.01 sec)

En valor de rows es 50 sin embargo la query devuelve 53 registros. Ya comentaba antes que ese valor es aproximativo. Pero aún se puede optimizar más:


mysql> alter table t1 add index idx_id_id2 (id,id2);

Esto crea otro índice agrupando los dos campos de la tabla. Por lo que si ahora usamos el analizador:


mysql> explain select id,id2 from t1 where id = 37\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: ref
possible_keys: idx_id,idx_id_id2
key: idx_id_id2
key_len: 4
ref: const
rows: 50
Extra: Using index
1 row in set (0.00 sec)

El índice usado ha pasado de ser idx_id a idx_id_id2 y en Extra ahora aparece “Using index”. Lo que hemos conseguido es que MySQL no tenga que ir a buscar los datos a la tabla, sino que los coja diréctamente del fichero de índices. Al final los ficheros quedan así:


-rw-rw---- 1 mysql mysql 338K 2008-11-17 18:55 t1.MYD
-rw-rw---- 1 mysql mysql 923K 2008-11-17 18:55 t1.MYI

Y después de elimiar el primer índice que creamos:


-rw-rw---- 1 mysql mysql 338K 2008-11-17 18:59 t1.MYD
-rw-rw---- 1 mysql mysql 538K 2008-11-17 18:59 t1.MYI

Como os comentaba, la contrapartida es el uso de espacio en disco pero en aplicaciones con altos volúmenes de datos, la velocidad de búsqueda puede ser más importante que el espacio usado.

Thema: MySql | Kommentare (0)

Restaurar password de root en MySQL

Thursday, 13. November 2008 9:58

Leo vía http://code.openark.org/blog/?p=102 un par de formas de restaurar la contraseña de MySQL. Normalmente siempre se suele usar con la opción –skip-grant-tables y se recomienda usar --init-file con la instrucción que cambie la contraeña.

En debian tenemos otra forma de hacerlo que no requiere reinicio. Por defecto, en la instalación de MySQL se añade el usuario debian-sys-maint para la reparación de las tablas en background en el inicio. La contraseña varía de instalación en instalación y podemos verla en el fichero /etc/mysql/debian.cnf.


[client]
host = localhost
user = debian-sys-maint
password = xxxxxxxxxxxxxxxxxx
socket = /var/run/mysqld/mysqld.sock

Para acceder de forma rápida, podemos escribir en la consola:


mysql -u debian-sys-maint -p$(grep -m 1 "^password" /etc/mysql/debian.cnf | cut -d' ' -f3)

Y ya puestos, meterlo en nuestro .bashrc como un alias:


alias mysql-debian="mysql -u debian-sys-maint -p$(grep -m 1 "^password" /etc/mysql/debian.cnf | cut -d' ' -f3)"

Como contrapartida, el fichero debian.cnf solo puede leerlo el root por lo que no sería mala idea tener una copia o modificar el alias para que se ejecute con sudo.

Thema: Linux, MySql | Kommentare (0)

MySQL: Motores de almacenamiento

Monday, 10. November 2008 18:09

Como parte de mi futura (espero) certificación como DBA de MySQL, el viernes pasado hice una pequeña charla (2 horas) para mis compañeros de trabajo. Esta es la primera de un total de 4, donde voy a dar una pequeña formación sobre administración de MySQL. Aquí os dejo las diapositivas:

Motores de almacenamiento (PDF)
Motores de almacenamiento (ODP)

Por desgracia, la grabación de la charla no puedo publicarla porque en algún momento se habla sobre algún cliente y no es plan.

Nos vemos pronto, por lo menos dentro de dos semanitas.

Saludos

Thema: MySql | Kommentare (0)

Perla: Contar palabras en un texto

Friday, 5. September 2008 10:53

Hace un par de días, estaba programando un filtro para Spamassassin y necesitaba contar el número de palabras que contenía el mail. El caso es que me dije, esto tiene que estar programado y requeteprogramado ya, por lo que me lancé a buscar ese pedazo de código. Lo bueno de esta mente virtual llamada Internet, es la disparidad de ideas que llevan al mismo sitio, así que decidí poner unas cuantas funciones que hacen lo mismo:

Esta es cosecha propia

sub wc {
	my $words = shift;

	$words =~ s/[;|,|\.|\:|\?|\!|\/|\)|\=]/ /g;
	$words =~ s/\s{2,}/ /g;
	return scalar split(/ /,$words);
}

Visto en http://askville.amazon.com/find-code-sample-Perl-word-count-script/AnswerViewer.do?requestId=1037856

#!/usr/bin/perl
use strict;
use warnings;

my %count_of;
while (my $line = <>) { #read from file or STDIN
foreach my $word (split /\s+/, $line) {
$count_of{$word}++;
}
}
print "All words and their counts: \n";
for my $word (sort keys %count_of) {
print "'$word': $count_of{$word}\n";
}

Visto en http://dada.perl.it/shootout/wc.perl.html

use strict;

my($nl, $nw, $nc);

while() {
    $nc += length;
    $nw += scalar(split);
    $nl += 1;
    # print "$nl $nw $nc\n";
}
print "$nl $nw $nc\n";
exit(0);
while (read(STDIN, $_, 4095)) {
    $_ .= ;
    $nl += scalar(split(/\n/));
    $nc += length;
    $nw += scalar(split);
}
print "$nl $nw $nc\n";

– La forma más completa: http://folk.uio.no/einarro/Comp/download.php?file=texcount.pl
– Otro más: http://foundationstone.com.au/HtmlSupport/WebPage/wordcount.html
– Y otro con explicaciones: http://en.literateprograms.org/Word_count_(Perl)

Thema: Perl, Receta | Kommentare (0)

Un par de enlaces sobre programación

Thursday, 4. September 2008 17:12

Hoy quería dedicar este espacio para comentar un par de blogs. El primero es de mig21. El blog está dedicado integramente a tratar asuntos de programación. Os lo recomiendo. El último artículo que ha escrito, trata sobre Chrome, el archicomentado navegador de Google, con su tecnología de tabs multihebras, el uso de webkit como motor de render y V8 para javascript. El artículo lo podéis encontrar en http://yapw.blogspot.com/2008/09/un-vistazo-al-cdigo-de-chrome.html.

El otro blog me ha proporcionado grandes dosis de “joder que tío”. En http://www.codingthewheel.com/ el autor escribe artículos tan interesantes como “How I Built a Working Online Poker Bot”, un portento. El “How-to” está compuesto de 8 partes donde desentraña los misterios de la programación de un bot que juega al poker, sirviéndose de la inyección de dll dentro del propio ejecutable del software en cuestión, consulta de bases de datos de jugadas,…

http://www.codingthewheel.com/archives/how-i-built-a-working-poker-bot
http://www.codingthewheel.com/archives/how-i-built-a-working-online-poker-bot-2
http://www.codingthewheel.com/archives/how-i-built-a-working-online-poker-bot-3
http://www.codingthewheel.com/archives/how-i-built-a-working-online-poker-bot-4
http://www.codingthewheel.com/archives/how-i-built-a-working-online-poker-bot-5
http://www.codingthewheel.com/archives/how-i-built-a-working-online-poker-bot-6
http://www.codingthewheel.com/archives/how-i-built-a-working-online-poker-bot-7
http://www.codingthewheel.com/archives/how-i-built-a-working-online-poker-bot-8

Saludos y hasta pronto ;)

Thema: Personal | Kommentare (0)

Replicar configuración entre dos IIS

Friday, 1. August 2008 8:51

Volvemos con un poco de Perl, esta vez para Windows. Tengo un cliente con un par de frontales web y en su día programé una pequeña utilidad para mantener la configuración de los dos servidores actualizada. Para que funcione es necesario que esté instalado Perl para Windows (Yo uso el de ActiveState) y la utilidad de migración de Internet Information Server que podéis descargar desde el site de Microsoft en esta dirección.

El script utilizada el servicio de componentes para conectarse al servidor remoto y pedirle el listado de sitios del IIS. Os pego aquí el código:

use Win32::OLE;
use Win32::OLE::Enum;

my $host = 'SERVIDORREMOTO';

qx,del "%SYSTEMROOT%\\system32\\inetsrv\\MetaBack\\IIS Migration Tool Backup*",;

my @webs = get_sites();

foreach (@webs) {
	qx,iismt.exe $host w3svc/$_ /overwrite /noninteractive /configonly /siteid replace,;
}

sub get_sites {
	my $IIS = Win32::OLE->GetObject("IIS://$host/W3svc");
	my $enumIIS = Win32::OLE::Enum->new($IIS);
	my @site_id;

	foreach my $site ($enumIIS->All) {

		if($site->{Name} =~ /^\d+$/) {
			push @site_id,$site->{Name};
		}
	}

	return @site_id;
}

Cosas a tener en cuenta:

– El script apenas tiene comprobación de errores (ninguna) así que os dejo a vosotros esa tarea :P
– La utilidad IISMT.EXE tiene que estar en el path o en el propio directorio donde esté el script
– Podéis compilar en un EXE el script para no tener el intérprete de perl en una máquina en producción. Yo uso perl2exe, no es caro ($49) y da muy buenos resultados.

Nos vemos pronto ;)

Thema: IIS, Receta | Kommentare (0)

Limitar descarga de apt-get

Thursday, 3. July 2008 2:27

Mis compañeros se quejan a veces de que no pueden ver videos de youtube porque algún desaprensivo está usando la adsl de la oficina para actualizarse su Debian. Que falta de respeto. Buscando enmendar tal afrenta, encontré la manera de limitar el ancho de banda que usa apt-get para descargarse los paquetes:


# apt-get install trickle
# dpkg-divert --local --rename /usr/lib/apt/methods/http
# echo '#!/bin/sh' > /usr/lib/apt/methods/http
# echo 'RATE=100' >> /usr/lib/apt/methods/http
# echo '/usr/bin/trickle -s -d $RATE /usr/lib/apt/methods/http.distrib' >> /usr/lib/apt/methods/http
# chmod 755 /usr/lib/apt/methods/http

Esto es posible gracias a trickle, una aplicación que nos permite gestionar el ancho de banda tanto de subida como de descarga. La aplicación funciona en espacio de usuario, por lo que no es necesario tener privilegios de root para usarla en otras aplicaciones. Por lo que se puede leer en su man, parece funcionar "suplantando" las funciones de gestión de sockets del sistema.

Referencia:

http://ubuntuforums.org/showthread.php?t=20342

Thema: Debian, Linux, Receta | Kommentare (0)

Originar llamadas en Asterisk

Monday, 8. October 2007 14:15

Somos muy cómodos. Para que vamos a engañarnos. Mi jefe dice que no es cuestión de comodidad, sino de productividad. Para qué vamos a tener que marcar un número de un cliente, si tenemos una preciosa base de datos con ellos. Así que me puse manos a la obra y fruto de investigar un poco, salió una pequeña "perla" para llamar desde nuestros teléfonos al cliente con solo hacer un click…

Para que el invento funcione necesitamos:

  1. Un servidor web
  2. Asterisk
  3. Perl

Para el artículo vamos a asumir que tenemos todo esto instalado en nuestra propia máquina (localhost). Empezamos por la configuración del asterisk. El script se conecta al manager de la centralita para lanzar los comandos (no la consola). Así que, lo primero que hay que hacer, es configurarlo. Nos vamos al fichero /etc/asterisk/manager.conf y lo dejamos así:

[general]
enabled = yes
port = 5038
bindaddr = 127.0.0.1

[originate]
read = call
write = call
permit = 127.0.0.1
secret = originatepass

Con esto hemos abierto la interfaz de administración de Asterisk y hemos creado un usuario originate con contraseña originatepass que tiene derechos para lanzar comandos del nivel call. Para más información sobre este tema, podéis pasaros por la sección Asterisk Manager Api de la excelente web voip-info.org

.La segunda parte del invento consiste básicamente en publicar el script que os pondré a continuación, a través de nuestro servidor web. Yo he usado apache así que os pongo cómo sería y dejo a cada cual buscarse el método que más le convenga:

ScriptAlias /asterisk /etc/asterisk/scripts

AllowOverride None
Options ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from 127.0.0.1

Lo he limitado a la propia máquina aunque podéis modificar la directiva Allow para que solo se pueda acceder desde vuestra red local o quitarla directamente. Es importante que el servidor pueda acceder al directorio /etc/asterisk/scripts y que el script tenga permisos de ejecución, o veremos un bonito error 500.

Por último os dejo en este enlace el script originate.pl y además, os lo publico aquí para que le echéis un ojo directamente:

#!/usr/bin/perl -w
# Originate call from command line or cgi script
# Iñaki Rodriguez (2007)
# License: GPL

use IO::Socket::INET;
use strict;
use CGI;

my ($is_cgi,$cgi);
my ($channel,$local_ext,$data_ext,$destination_ext);
my $buffer;
my $debug = 1;

# Connection Data
my $host        = 'localhost';
my $timeout     = 5;
my $username    = 'originate';
my $secret      = 'originatepass';

if(!$ENV{'REMOTE_ADDR'}) {
        $is_cgi = 0;
        if ($ARGV[0] && $ARGV[1]) {
                $local_ext = int($ARGV[0]); # Origin Number
                $destination_ext = int($ARGV[1]); # Destination Number
        } else {
                print "Usage: ./originate.pl  \n";
                exit 1;
        }
} else {
        $is_cgi = 1;
        $cgi = new CGI;
        print $cgi->header();

        if($cgi->param('orig') && $cgi->param('dest')) {
                $local_ext = int($cgi->param('orig')); # Origin Number
                $destination_ext = int($cgi->param('dest')); # Destinatio Number
        } else {
                exit 1;
        }
}

if(!$local_ext || !$destination_ext) {
        print "Invalid values!!!\n";
        exit 1;
}

# Manager Connection
print "Connecting...\n" if $debug;

my $manager_sock = new IO::Socket::INET (PeerPort       => 5038,
                                         Proto          => 'tcp',
                                         PeerAddr       => $host,
                                         Timeout        => $timeout);

die ("Cannot connect to asterisk manager") if (!$manager_sock);
$manager_sock->recv($buffer,4096);

# Send Login Cmd
if(
$manager_sock->send("Action: Login\r\n".
                    "Username: $username\r\n"."Secret: $secret\r\n".
                    "ActionID: 1\r\n\r\n") == 0)
{
        print "Failed to send login information\n";
        exit(1);
}

$manager_sock->recv($buffer,4096);
print "Auth...\n" if $debug;

if($buffer =~ /^Response: Error/) {
        print "Failed to authenticate using $username and $secret\n";
        exit(1);
} 

# Send Originate Cmd
my $originate = "Action: Originate\r\n";
$originate .= "Channel: Local/$local_ext\\@default\r\n";
#$originate .= "Context: default\r\n";
$originate .= "Exten: $destination_ext\r\n";
$originate .= "Priority: 1\r\n\r\n";

# Enviamos y cerramos
$manager_sock->send($originate);

print "Calling...\n" if $debug;
$manager_sock->recv($buffer,4096);
print $buffer if $debug;
$manager_sock->close();

Antes de acabar, os comento un poco por encima el script. Debajo del comentario #Connection Data tenemos la sección de configuración. Solo tenemos que definir el host, el usuario y la contraseña. En nuestro caso, localhost, originate y originatepass respectívamente.

Cuando invoquemos el script con nuestra url http://localhost/asterisk/originate.pl?orig=600&dest=1004 conectará con el manager para abrir el canal y hacer la llamada. Otra cosa que no os he dicho, este script se puede lanzar desde la propia línea de comandos pasándole como argumentos la extensión y el número:
perl originate.pl 600 1004

Con esto acabamos por hoy. Aunque no será el último artículo sobre Asterisk ;)

Thema: Asterisk | Kommentare (0)

Registrando robots de búsqueda en Apache

Wednesday, 3. October 2007 12:56

Hace unos días, un cliente me comentó su necesidad de loguear las entradas de los robots de búsqueda en un fichero aparte, para después procesarlas. Como me resultó novedoso el tema (en tanto que nunca he hecho algo parecido) me decidí a escribir un post. También han ayudado factores como recordarme que si me pagan el dominio, lo podría ir usando. Melón.

Vamos a ponernos en faena. Apache tiene una directiva para definir variables de entorno en función de una serie de condiciones. Me refiero a la directiva SetEnvIf. Para nuestro ejemplo también nos valdría BrowserMatch pero opté por la primera por ser más genérica. Simplificaré un poco el montaje (mi cliente tenía varios dominios virtuales). En nuestro caso lo vamos a hacer con mi dominio, por ejemplo.

Lo primero que tenemos que hacer es abrir el fichero donde definimos nuestro virtualhost. Sobre la línea que define el CustomLog escribimos:

SetEnvIf User-Agent bot is_a_robot
CustomLog /var/log/apache2/robots_access.log combined env=is_a_robot

Estas dos líneas sirven para definir la variable is_a_robot y activar el log en caso de que esa variable de entorno exista (con el tipo de log combined)

Fácil ¿no? Ya para terminar y rizar el rizo, hice un pequeño script en perl para separar cada bot en un fichero. Os lo pego aquí mismo (y os incluyo un enlace de paso aquí)

#!/usr/bin/perl

#
# Iñaki Rodriguez (2007)
#
# Split robots in separate files (using md5 as filename)
#
# License: GPL

use Digest::MD5 'md5_hex';

my $robot_log = '/var/log/apache2/robots_access.log';
my $output_dir = '/var/log/apache2/robots/';
my %bots;

open BOTLOG, "<$robot_log" or die ("$robot_log: I can't open it");
open INDEX, "<".$output_dir."index.txt";

while() {
    chomp();
    my ($md5,$ua) = split(/\t/);
    $bots{$md5} = $ua;
}
close(INDEX);

while() {

    chomp();
    m/^(.+?) (.+?) (.+?) (\[.+?\]) (".+?") (\d\d\d) (.+?) (".+?") (".*")$/;
    my $md5 = md5_hex($9);
    my $tmpf = $output_dir.$md5.".log";
    open LOG,">> $tmpf" or die ("$tmpf: I can't write it");
    print "Adding entry to $tmpf ($9)\n" if ($ARGV[0] ne '-q');
    $bots{$md5} = $9 if (!$bots{$md5});
    print LOG $_."\n";
    close(LOG);
}

close(BOTLOG);

open INDEX, ">".$output_dir."index.txt" or die($output_dir."index.txt: I can't create it");
foreach $md5 (keys %bots) {
    print INDEX $md5."\t".$bots{$md5}."\n";
}
close(INDEX);

Solo comentar un par de cosas del script. La variable $output_dir especifica el directorio donde va a guardar los logs por separado. En este mismo directorio se creará un fichero index.txt que guarda la correspondencia de cada log con su user agent. El nombre del fichero es la suma md5 del campo User Agent del log. Por último la variable $robot_log es la que establece el fichero donde se almacenan los registros de todos los robots.

Se podría mejorar. Invito al lector a añadir:

  • Registro de la última entrada para no duplicarlas
  • Exclusiones

Y hasta aquí el post del día.

Nota Mental: Arreglar el css que se descuadra (el theme no es mío)

Thema: Linux, Perl, Receta, apache | Kommentare (0)