Anatomy of an Asterisk rick roll

| | Comments (0) | TrackBacks (1)
I was inspired by a friend (EFNet - steve nash aka oz) on IRC - he (or his friend(s)) had setup an incoming phone number that would just rickroll you.  Nothing else.

Well, hell, that's easy to setup with Asterisk, but I was determined to do one better.  Not only would I setup a phone number that would rickroll you, but I would also make the number call you back and rickroll you again.

With Asterisk, PHP, MySQL, and great SIP service, this is cake.  First things first -- I made a new musiconhold class (musiconhold.conf):

[rickroll]
mode=files
directory=/var/lib/asterisk/custom-moh/rickroll
And the extension logic (extensions.conf) - this goes into your incoming SIP context:

exten => 6302061300,1,Answer
exten => 6302061300,n,AGI(rickroll)
exten => 6302061300,n,Ringing
exten => 6302061300,n,Wait(4)
exten => 6302061300,n,MusicOnHold(rickroll)
exten => 6302061300,n,Hangup

Bam - if you call 630-206-1300, you will get rickrolled.  However, if you notice the AGI(rickroll) on the second line of the extension logic -  that's what makes this rickroll funnier.

I created a MySQL table:

CREATE TABLE rickroll (
  rickrollid bigint(20) NOT NULL auto_increment,
  phonenum varchar(10) NOT NULL default '',
  dialtime int(11) default NULL,
  calls tinyint(4) default NULL,
  PRIMARY KEY (rickrollid),
  UNIQUE KEY (phonenum),
  KEY (dialtime)
);

And created an AGI with command line PHP -- in /var/lib/asterisk/agi-bin -- make sure it's got execute permission (chmod 755 it when yer done):

#!/usr/bin/php -q
<?

$stdin = fopen('php://stdin', 'r');
$stdout = fopen('php://stdout', 'w');

while (!feof($stdin)) {
    $temp = fgets($stdin);
    $temp = str_replace("\n", "", $temp);
    $s = explode(":", $temp);
    $agivar[ $s[0] ] = trim($s[1]);
    if (($temp == "") || ($temp == "\n")) {
       break;
       }
    }

$mysql=mysql_connect("localhost","RICKROLLUSER","RICKROLLPASS","rickroll");
mysql_select_db("rickroll");

$callerid = $agivar[agi_callerid];
$newd = time() + 300;

$q = "INSERT INTO rickroll (phonenum, dialtime, calls) VALUES ('$callerid','$newd',0)";
$qid = mysql_query($q, $mysql);

mysql_close($mysql);

exit;

There.  We now have incoming rickroll calls getting logged to a database.  It sets the dialtime to the current time + 300 seconds (5 minutes) - so in 5 minutes, the cronjob will pick the call up and make it.

Here's where the fun comes in.  We need a cronjob that uses the manager interface of Asterisk to initiate calls after they're read out of the database.  Add this to manager.conf in asterisk:

[rickroll]
secret = MANAGER_PASSWORD_HERE
deny=0.0.0.0/0.0.0.0
permit=127.0.0.0/255.255.255.0
read = system,call,log,verbose,command,agent,user
write = system,call,log,verbose,command,agent,user

Now we're ready for the evil.  Here's the PHP cronjob (heavily commented so you can understand the flow) - I wrote a couple functions for sending manager commands so I could reuse the code in other stuff:

#!/usr/bin/php -q
<?php

// Set some variables
$callerid = "";  // Holds the caller ID we'll use to rickroll the person back
$time = time();  // Current time - in epoch (seconds since 1970)
$del = array();  // Array of entries to delete/update after we call

// Connect to the database
$mysql=mysql_connect("localhost","RICKROLLUSER","RICKROLLPASS","rickroll");
mysql_select_db("rickroll");

// Open a socket to the manager interface of Asterisk.
$ast_man = fsockopen("127.0.0.1","5038", $errno, $errstr, 30);

// Read in the version string
$in = fread($ast_man, 4096);

// Login with the manager credentials we used in manager.conf
fwrite($ast_man, "Action: login\r\n");
fwrite($ast_man, "Username: rickroll\r\n");
fwrite($ast_man, "Secret: MANAGER_PASSWORD_HERE\r\n");
fwrite($ast_man, "Events: off\r\n\r\n");

// Get the response
$resp = get_response($ast_man);

// If we get a login error - die.
if ($resp['response'] != "Success") {
    exit;
}

// Get the list of people that need a return call from Rick Astley
$q = "SELECT rickrollid, phonenum, dialtime, calls FROM rickroll WHERE dialtime < $time";
$qid = mysql_query($q, $mysql);

while ($r = mysql_fetch_assoc($qid)) {
    // Set the variables from the array -- this is for pure laziness:
    $rickrollid = $r['rickrollid'];
    $phonenum = $r['phonenum'];
    $calls = $r['calls'];
    $dialtime = $r['dialtime'];

    if ($calls == 0) {
        // Never been called back yet -- set the caller ID to the rickroll #
        $callerid = "6302061300";
    } else {
        // They got one callback already.  This is the second (last) one -- so
        // we'll randomize the last 4 digits of their phone # and use that as our
        // caller ID.
        $callerid = substr($phonenum, 0, 6) . rand(0,9) . rand(0,9) . rand(0,9) . rand(0,9);
    }

    // Set the array of asterisk manager parameters to initiate the call:
    $evil = array('Action' => 'Originate',
              'Channel' => "LOCAL/$phonenum@rickroll",
              'Context' => 'rickroll',
              'Exten' => '1000',
              'Priority' => '1',
              'Callerid' => $callerid,
              'Timeout' => '30000');

    // We're not going to call ourselves back.
    if ($phonenum != '6302061300') {
        send_command($ast_man, $evil);
    }

    // Add this phone number to the array - so we can update/delete the records
    $del[] = array('phonenum' => $phonenum, 'calls' => $calls, 'rickrollid' => $rickrollid);
    }

// Process the array of delete/updates
foreach($del as $r) {
    if ($r['calls'] == 0) {
        // This was their first call -- the next one will be done in a random amount of time.

        // The next call will be in some random amount of time 15 minutes from now.
        // I did 3 rand's of 0-300 to increase the odds of it not calling right back.
        $newd = time() + rand(0,300) + rand(0,300) + rand(0,300);

        // Update their entry in the database with the new call time.
        $q = "UPDATE rickroll SET dialtime = $newd, calls = 1 WHERE rickrollid = " . $r['rickrollid'];
        $qid = mysql_query($q, $mysql);
    } else {
        // Second call - just delete them from the table.
        $q = "DELETE FROM rickroll WHERE rickrollid = " . $r['rickrollid'];
        $qid = mysql_query($q, $mysql);
    }
}

fclose($ast_man); 

mysql_close($mysql);

exit;

function send_command($fp, $cmdarray) {
    $_t = "";

    foreach($cmdarray as $k => $v) {
      $_t .= "$k: $v\r\n";
    }

    $_t .= "\r\n";

    fwrite($fp, $_t);
}

function get_response($fp) {

    $ret = "";

    while(1) {
        $pkt = fread($fp, 1);
        $ret .= $pkt;

        if (substr($ret, -4) == "\r\n\r\n") break;
    }

    $a = explode("\r\n", $ret);

    foreach($a as $k => $v) {
        if (strlen($v) < 5) {
            unset($a[$k]);
        } else {
            list($param, $value) = explode(": ", $v, 2);
            $p = strtolower($param);
            $r[$p] = trim($value);
        }
    }

    return $r;
}

The comments should hopefully explain it all, if not, feel free to drop me a line w/ questions.  Basically, the cronjob grabs all the upcoming calls, and then proceeds to make them.  If its their first callback - it will set the callerID to the rickroll phone number (630-206-1300).  If its their second/last - it will set the caller ID to a number similar to the caller's # - if you call from 303-555-1122, it will use the same NPA-NXX - 303-555, and then append 4 random #'s.  I figured if someone notices rickroll is calling them back, they'll ignore the callback.  A call phone a number in their same area code + prefix might make someone answer.

To install the cronjob - stick the cronjob bin (make sure it has execute permission) somewhere.  /usr/local/bin works for me.  Add this to your crontab:

*/2 * * * *    /usr/local/bin/cron_rickroll.php  >> /tmp/rickroll.log 2>&1

The last piece is the extension context [rickroll].  In extensions.conf:

[rickroll]

;  The extension we connect the called party to -- rickroll!
exten => 1000,1,Answer
exten => 1000,n,MusicOnHold(rickroll)
exten => 1000,n,Hangup

;  The outbound calling of our rickroll victims - standard outdial
exten => _NXXNXXXXXX,1,Dial(SIP/1${EXTEN}@primary-sip-out)
exten => _NXXNXXXXXX,n,Dial(SIP/1${EXTEN}@secondary-sip-out)
exten => _NXXNXXXXXX,n,HangUp

The code isn't super pretty, and I'm sure there are a thousand improvements that can be made to the code/logic, but for a quick one hour joke - its not too bad.

Feel free to comment/email with questions :)

Andy

Links for more information:

Asterisk
Asterisk AGI
Asterisk Dial Command
Asterisk extensions.conf
Asterisk manager

Reblog this post [with Zemanta]

2009 Hydroponic Garden Week 1

| | Comments (0) | TrackBacks (0)
2009 Hydroponic Garden - Week 1

This is the beginning of my 2009 indoor garden.  I have started tomatoes (Roma + Beefsteak), strawberries, oregeno, chives, basil, and cilantro.  I'm going to use the lessons learned of last year (maintenance/upkeep/trimming) to have a better garden this time.

I went to Home Depot and purchased some cinderblock and shelves, so now under the light is two tiers of shelving, one for the self contained hydroponic buckets and the other for all my herbs and anything else I wish to grow.

The picture shows progress after six days.  This year I've got a plastic moisture hood over the seedlings, as well as a heat pad underneath (to promote better root growth).

From seeds to plants - 2008 version

| | Comments (0) | TrackBacks (0)
2008-05-08-plants.jpg


Has it already been a year?!

It's been some time since I've posted the progress on the tomato plants.  Unfortunately, the demands of work and life have prevented me from giving the time necessary to accurately document the growth, as well as take care of the tomato plants.

The plants ended up overgrowing the green/leaf/vegitation and the fruit wasn't the size I wanted.  These three were a first lesson in tomato growing.  This was a lot of fun for me, and there will be round two (which is already started).

After the jump - I will share what I did document in progress, and the link to the photostream on Flickr that has more detail.

The next post I'll show the beginnings of my next round of indoor gardening!

Three weeks of great progress.

| | Comments (0) | TrackBacks (0)
week3-tomato1.jpg

These are the tomato plants now after 3 weeks or so.  They've developed quite a bit since the simple seedlings that popped out of the rockwool.  With watering everyday and plenty of light, they have really grown strong.  The roots have completely enveloped the rockwool and they are ready for a bigger place.

I have to transplant/move them into bigger pots - and I have already done so.

I will be stating another seedling  run soon as well.

 

week3-tomato2.jpg

I have already used my camera to completely document all the steps I have taken, which I will be posting this weekend.

So far - I am very excited with the rapid rate of progress that I have in my garden, and I really can't wait to have tomatoes hanging off these plants.

Check back this weekend - for the full photo/instructionals/comments from a beginner.  Also - feel free to ask me questions, or leave comments on your thoughts!

Indoor Gardening (hydroponics)

| | Comments (0) | TrackBacks (0)
2008-03-23-seedling-thumb.jpg

While it may appear to the people that know me that I'm always into gadgets/computers/electronic stuff, I'm also fascinated by many other things outside of computers.  Last year I started getting more into cooking, watching the Food Network and trying things I watched on TV.  I've made some pretty great stuff (details I'll save for another blog post).

One of the things I learned is the tomatoes in the grocery stores are un-ripe, and given a chemical to treat them and make them red.  I feel cheated robbed like I'm missing out on the true taste of tomatoes.  I've also always wanted to grow my own tomatoes (after growing a bunch of stuff in my window), so I went nuts at a local hydroponic supply store.

I have a 400W grow light, reflector, hydroponic grow buckets, rockwool seed starting cubes, and I have started with tomato seeds.  I have 3 kinds of tomato seeds - beefsteak, roma, and a yellow one.

New Blog

| | Comments (0) | TrackBacks (0)
So, I have decided to go back to a blog (instead of the couch picture of me).  I've been wanting a better platform to post/share things, and while I'm a bit fanatical about wanting to code it all myself, it seems some things are better left to others.

I have chosen Movable Type over Wordpress (booooo, perl over PHP!?), but while I code PHP all day and really detest Perl (reasons belonging in another post), I rather like how Movable Type uses the database to publish static files.  Plus, the back-end interface is really snazzy!

As for this blog, I have a bunch of PHP code snippets, some lessons/things I've learned over the years, and just general geek info I want to share with the world.  Everybody has big goals for their little blog to be read by many - I just want to have a nice repository for all these snippets/things I've learned/hacked up in the past.

Stay tuned!