Perl IRC Bot (Goki) + ChanOp plugin

Goki is an IRC Bot written in perl, very easy to install, use and develop. One of the best things about Goki is it doesn’t require any additional modules, just give it a try http://goki.sf.net.

Since Goki has no authentication yet, I did a small plugin which will handle a very primitive user’s access list and a few basic channel operator’s commands, nothing more but what you are reading πŸ˜‰

Follow the instructions:

– Create a file plugin/chanop.pm (or whatever you want)
– Paste the following code:

package chanop; 
#use warnings; # we don't need warnings, we know it's dirty code ;) 
 
# Module wide variables 
 
# add as many nicks as you want, and remember, in order to authenticate  
# you need to have the same nick name (not case sensitive) 
my %chanops = ( 
'xUx' => '12345', 
'demonick' => 'demopass', 
'nick2' => 'somethinghere' 
); 
 
# careful, moving things here could make the bot crash :) 
my %chdata = (); # hash that will hold all data 
 
foreach $key (sort keys %chanops) { 
%{$chdata{lc($key)}} = ('nick' => lc($key), 'pass' => $chanops{$key}); 
} 
 
# Module load functions. Set default values here. 
BEGIN { 
our $VERSION = 0.4; 
$irc = main::IRC; 
# private events 
$irc->add_handler('privcmd auth','do_auth'); 
$irc->add_handler('privcmd who','do_who'); 
$irc->add_handler('privcmd join','do_join'); 
$irc->add_handler('privcmd part','do_part'); 
$irc->add_handler('privcmd kick','do_kick'); 
$irc->add_handler('privcmd ban','do_ban'); 
$irc->add_handler('privcmd voice','do_voice'); 
$irc->add_handler('privcmd devoice','do_devoice'); 
$irc->add_handler('privcmd op','do_op'); 
$irc->add_handler('privcmd deop','do_deop'); 
$irc->add_handler('privcmd sh','do_sh'); 
$irc->add_handler('privcmd say','do_say'); 
} 
 
sub do_say { 
my ( $nick, $hostmask, $text ) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
my @args = split(" ",$text); 
my $msg = join(" ",@args[1 .. scalar(@args)-1]); 
main::plog "Message sent from $nick to $args[0]\n"; 
$irc->say($args[0],$msg); 
} 
 
sub do_sh { 
my ( $nick, $hostmask, $text ) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
main::plog "Exec attempt by $nick\n"; 
my @output = `$text`; 
my $line; 
foreach $line (@output) { 
$irc->say($nick, $line); 
} 
} 
 
sub do_deop { 
# deop #channel nick 
my ($nick,$hostmask,$text) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
my @args = split(" ",$text); 
if ($args[0] !~ /^\#/) { $args[0] = "#" . $args[0]; } 
main::plog "Deop on $args[0] to $args[1] by $nick\n"; 
$irc->deop($args[0],$args[1]); 
} 
 
sub do_op { 
# op #channel nick 
my ($nick,$hostmask,$text) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
my @args = split(" ",$text); 
if ($args[0] !~ /^\#/) { $args[0] = "#" . $args[0]; } 
main::plog "Op on $args[0] to $args[1] by $nick\n"; 
$irc->op($args[0],$args[1]); 
} 
 
sub do_devoice { 
# devoice #channel nick 
my ($nick,$hostmask,$text) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
my @args = split(" ",$text); 
if ($args[0] !~ /^\#/) { $args[0] = "#" . $args[0]; } 
$irc->devoice($args[0],$args[1]); 
} 
 
sub do_voice { 
# voice #channel nick 
my ($nick,$hostmask,$text) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
my @args = split(" ",$text); 
if ($args[0] !~ /^\#/) { $args[0] = "#" . $args[0]; } 
main::plog "Voice on $args[0] to $args[1] by $nick\n"; 
$irc->voice($args[0],$args[1]); 
} 
 
sub do_ban { 
# ban #channel nick|hostmask 
my ($nick,$hostmask,$text) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
my @args = split(" ",$text); 
if ($args[0] !~ /^\#/) { $args[0] = "#" . $args[0]; } 
main::plog "Ban on $args[0] to $args[1] by $nick\n"; 
$irc->mode($args[0],"+b",$args[1]); 
} 
 
sub do_kick { 
# kick #channel nick reason 
my ($nick,$hostmask,$text) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
my @args = split(" ",$text); 
if ($args[0] !~ /^\#/) { $args[0] = "#" . $args[0]; } 
my $reason = join(" ",@args[2 .. scalar(@args)-1]) || $args[1]; 
main::plog "Kick on $args[0] to $args[1] ($reason) by $nick\n"; 
$irc->kick($args[0],$args[1],$reason); 
} 
 
sub do_part { 
# part #channel 
my ($nick,$hostmask,$text) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
my @args = split(" ",$text); 
if ($args[0] !~ /^\#/) { $args[0] = "#" . $args[0]; } 
main::plog "Parting $args[0] by $nick\n"; 
$irc->part($args[0]); 
} 
 
sub do_join { 
# join #channel 
my ($nick,$hostmask,$text) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
my @args = split(" ",$text); 
if ($args[0] !~ /^\#/) { $args[0] = "#" . $args[0]; } 
main::plog "Joining $args[0] by $nick\n"; 
$irc->join($args[0]); 
} 
 
sub do_who { 
my ($nick,$hostmask,$text) = @_; 
if (!&do_auth_check($nick,$hostmask)) { return; } 
foreach my $key (sort keys %chdata) { 
if (exists($chdata{$key}{'hostmask'})) { 
$irc->say($nick, $chdata{$key}{'nick'} . " (". $chdata{$key}{'hostmask'}.")"); 
} 
} 
return; 
} 
 
sub do_auth_check { 
my ($nick,$hostmask) = @_; 
my $tmphostmask = (split("\!",$hostmask))[1]; 
if (!exists($chdata{lc($nick)}{'hostmask'})) { 
main::plog "Unauthorized access from $hostmask\n"; 
return 0; 
} 
if ($chdata{lc($nick)}{'hostmask'} eq $tmphostmask) { return 1; } 
return 0; 
} 
 
sub do_auth { 
my ($nick,$hostmask,$text) = @_; 
my $tmphostmask = (split("\!",$hostmask))[1]; 
if (!exists($chdata{lc($nick)})) { 
main::plog "Invalid user tried to AUTH: $nick ($tmphostmask)\n"; 
return; 
} 
my @args = split(" ",$text); 
if ($chdata{lc($nick)}{'pass'} ne $args[0]) { 
main::plog "Invalid Login attemp from $nick ($tmphostmask)\n"; 
$irc->notice($nick,"Invalid Password, attemp logged!"); 
return; 
} 
if (exists($chdata{lc($nick)}{'hostmask'})) { 
main::plog "RE-AUTH from $nick from ".$chdata{lc($nick)}{'hostmask'}." to $tmphostmask\n"; 
} 
else { main::plog "AUTH from $nick from $tmphostmask\n"; } 
$chdata{lc($nick)}{'hostmask'} = $tmphostmask; 
$irc->notice($nick, "Authentication Succesful!"); 
} 
 
return 1; 
 
# Module unload functions, free memory and close open filehandles here 
END { 
# Does not currently work, but is here for future compatibility 
# $irc->del_handler( '', '' ); 
} 

– Edit file conf/plugin.conf and make it load your plugin by adding a line with the word “chanop” (or the first part of your thanemayoupicked.pm)
– Start your bot and have fun πŸ˜‰

For future reference and user’s comments go to http://sourceforge.net/forum/forum.php?thread_id=2185241&forum_id=621728

PHP Generate Random Passwords

This is a small script written in PHP which will help you to generate N passwords of N length in less than 1ms πŸ˜›

Make sure you have PHP installed, then copy this code to a file called randompass.php

#!/usr/bin/php -q
<?
// no i,l,o keep passwords easy
$chars = "abcdefghjkmnpqrstuvwxyz0123456789";
$passlen = (intval($argv[1])? intval($argv[1]):6);
$passnum = (intval($argv[2])? intval($argv[2]):1);

echo "Generating $passnum passwords of $passlen letters/numbers\n";
// feed the random God :P
srand(((int)((double)microtime()*1000003)));

for ($i=1;$i<=$passnum;$i++) _gen_pass();

function _gen_pass() {
  global $chars, $passlen;
  $cnt = 1;
  while ($cnt <= $passlen) {
    $myrand = rand() % 33;
    $tmp = substr($chars, $myrand, 1);
    if (rand(0,1)) $tmp = strtoupper($tmp);
    $pass = $pass . $tmp;
    $cnt++;
  }
  echo $pass . "\n";
}

?>

Now you have the file don’t forget to make it executable:

chmod +x randompass.php

How it works? See some examples:

Usage: ./randompass.php [password_length] [password_number]

# The default execution will drop 1 password of 6 letters/numbers
./randompass.php
Generating 1 passwords of 6 letters/numbers
9hBEq1

# want 5 passwords of 12 letters/numbers ?
./randompass.php 12 5
Generating 5 passwords of 12 letters/numbers
ug5Tj8fP3w26
Tn9fnSjy2PmJ
NRqC6m8J0svn
YFQ6g3WnHH8r
ds56SnQvsBEq

That’s all, use it for what you need, don’t try to break it or find stupid bugs πŸ˜›

Easy RRDtool Install (1.2.27)

As you should know RRDtool is the OpenSource industry standard, high performance data logging and graphing system for time series data. Use it to write your custom monitoring shell scripts or create whole applications using its Perl, Python, Ruby, TCL or PHP bindings.

This post will guide you to setup RRDtool 1.2.27 on your Linux server without pain πŸ˜‰ I have tested this configuration on CentOS 4,5, RHEL 3,4

Before you start make sure you install the following apps:

  • libart_lgpl-2.3.16-3
  • libart_lgpl-devel-2.3.16-3
  • zlib-1.2.1.2-1.2
  • zlib-devel-1.2.1.2-1.2
  • freetype-2.1.9-6.el4
  • freetype-devel-2.1.9-6.el4
  • libpng-1.2.7-3.el4_5.1
  • libpng-devel-1.2.7-3.el4_5.1

Use the power of yum to get them on your system… if you ran into trouble then you shouldn’t continue unless you get someone to fix your mess πŸ˜›

Now, you need to download and install RRDtool

cd /usr/local/src
wget http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.2.27.tar.gz
tar -zxf rrdtool-1.2.27.tar.gz
cd rrdtool-1.2.27
./configure --disable-tcl
# if you get an error while configuring make sure you read
# what caused that, and try to fix it
make
make install
ln -s /usr/local/rrdtool-1.2.27/bin/rrdtool /usr/bin/rrdtool
ln -s /usr/local/rrdtool-1.2.27/bin/rrdupdate /usr/bin/rrdupdate
ln -s /usr/local/rrdtool-1.2.27/bin/rrdcgi /usr/bin/rrdcgi

Was that hard? I don’t think so, actually it was pretty easy and now you can start coding your own graphs πŸ˜€

You need to start diggin’ the Tutorials, Documentation and Wiki provided by Tobias Oetiker in order to start with your own cool graphs, and remember, RRDtool is FREE and if it helps you and saves you time/money you should really consider make Tobi happy πŸ˜‰

I’ll be posting later some basic examples for RRDtool graphs and other scripts, be patient πŸ™‚

IP updater for EditDNS.net

As anyone know (and should know) EditDNS it’s the best alternative for DNS Management and the best of all it is FREE πŸ˜‰

Here I wrote/adapted some code which will allow you to update your dynamic IP through EditDNS’s API.

Requirements:

  • You need to register first! (duh)
  • Donations are optional, but if it makes your life easier you should consider it and you’ll also get more services.
  • Perl!

File: editdns.pl

#!/usr/bin/perl

use strict;

## Configure ONLY this 2 variables
my $editdns_pass   = "a"; # put your password
my $editdns_record = "b"; # put the record you wish to update

## ###############
## Nothing else should be changed unless you know what to do
## ###############

my $host = "DynDNS.EditDNS.net";
my $port = 80;
my $editdns_post = "p=$editdns_pass&r=$editdns_record";

my $editdns_req = join("",
  "POST /api/dynLinux.php HTTP/1.0\r\n",
  "Host: $host:$port\r\n",
  "User-Agent: EditDNS Browser 0.1\r\n",
  "Referer: http://www.editdns.net\r\n",
  "Content-Type: application/x-www-form-urlencoded\r\n",
  "Content-Length: ".length($editdns_post)."\r\n\r\n",
  "$editdns_post\n"
);

my $hostaddr = (gethostbyname($host))[4] || &error("Couldn't get IP for $host");
my $remotehost= pack('S n a4 x8',2,$port,$hostaddr);
socket(S,2,1,6) || &error("Couldn't create socket");
connect(S,$remotehost) || &error("Couldn't connect to $host:$port");
select((select(S),$|=1)[0]);
print S $editdns_req;
vec(my $rin='',fileno(S),1)= 1 ;
select($rin,undef,undef,60) || &error("No response from $host:$port");
undef($/);
close(S);
print "[DONE]\n";
exit;

sub error {
        print "[ERROR] $_[0]\n";
        exit;
}

Next and once you have configured the script:

chmod +x editdns.pl
pico /etc/crontab
# Add editdns.pl to execute every 15 minutes
*/15 * * * * root /path/editdns.pl > /dev/null 2>&1

Do not set intervals lower than 15 minutes, since it can be considered as an abuse and you’ll get banned.

Part of this code was taken from James Marshal, happy coding!

*** If you are looking for SSL support and multiple records you might want to check http://xux.in/blog/post/php-editdns-updater-for-linux/

Monitor open ports using PHP (snippet)

How can you really know if a port is open or closed? Most of the scripts around the web fail doing it’s job, not because they are wrong but because they are not doing their job as they should.

Am I on drugs? No, not now πŸ˜‰ basicly, what all scripts do is:

[root@local]# telnet yourhost port

That will tell you if yourhost is open on that port but sometimes it will just hang there, why? there are many reasons why a server or service could hang (I’m not covering that part … at least not for free :P), but the only thing you really need to know is, IT HAPPENS… when? how? why? it will.

So, if you are in the middle of coding some script that let’s you monitor your servers / services without worrying about that “small particular issue”, you are in the right place, check out the code:

function check_port($ip="",$port="",$request="",$replies="") {
  if (!$ip || !$port) {
    echo "No ip/port to check";
	  return;
  }
	if (!($fp = @fsockopen($ip,$port,$junk,$junk,10)) {
	  echo "Connection DOWN!";
		return;
	}
	if (!$request && !$replies) {
	  echo "Connection UP! (open socket)";
		return;
	}
	if ($request) {
	  fputs($fp,$request,strlen($request));
		fputs($fp,"\r\n\r\n",8);
	}
	stream_set_timeout($fp,10);
	do {
		$response .= fgets($fp);
	} while (!feof($fp));
	@fclose($fp);
	$response = preg_replace("/\n|\r/","",$response);
	$result = false;
	$error = $response;
	$array_replies = explode(",",$replies);
	if (is_array($array_replies)) {
	  foreach ($array_replies as $v) {
		  if (!$v) continue;
			if (preg_match("/$v/i",$response)) {
			  $result = true;
                          break;
			}
		}
	}
	if ($result) {
	  echo "Connection UP!";
		return;
	}
	echo "Connection error: $response");
	return;
}

That’s a mess! Yes I know, it is dirty and uggly but it works. That function takes 4 arguments, $ip (server’s IP), $port (server’s port), $request and $replies (you can use comma delimited here in case you need to receive one or more answers).

How it works? Well copy that piece of code to any php file and call it this way:

// This first example will tell us if google.com is up ;)
// it sends the request "HEAD / HTTP/1.0" to the IP 72.14.207.99 on the port 80
// and expects 2 answers: "200" or "OK"
check_port("72.14.207.99",80,"HEAD / HTTP/1.0","200,OK");
// it sends the request "HEAD / HTTP/1.0" to the IP 72.14.207.99 on the port 80
// and expects 2 answers: "200" or "OK"

// another example?
check_port("148.235.52.179",110,"","\+OK");
// this one will check port 110 (pop3) on that IP, it won't send a request but
// it will sit till gets a "\+OK"

Remember, all requests and replies depends on the server’s side, be aware of that πŸ˜‰