Category Archives: Development

Net_DNS2 Version 1.3.1 Released

I’ve released version 1.3.1 of the PEAR Net_DNS2 library- you can install it now through the command line PEAR installer:

pear install Net_DNS2

Download it directly from the Google Code page here.

Or, you can also add it to your project using composer.

Version 1.3.1

  • added the Net_DNS2_Packet_Request and Net_DNS2_Packet_Response objects to the Net_DNS2_Exception object
  • added support in the TSIG class for SHA algorithms (requires the hash extension, which is included in PHP >= 5.1.2), patch provided by Manuel Mausz
  • added support for the NID, L32, L64, and LP DNS RR’s (RFC6742)
  • lots of phpcs cleanup

Mining Twitter API v1.1 Streams from PHP – with OAuth

This is a quick update to my post about a year ago, with details on how to mine Twitter streams in real-time using PHP. This new code includes updates for the v1.1 API, including authentication using OAuth.

The first thing you need to do is sign in to the Twitter developer portal with your Twitter account here: https://dev.twitter.com/user/login 

Once you’ve logged in, click on your profile icon in the top right hand corner, select
“My applications”, and create a new application if you don’t already have one.

Select the option to create the access token as well, as the requests need to be signed by a Twitter account.

The Code

ctwitter_stream.php

class ctwitter_stream
{
    private $m_oauth_consumer_key;
    private $m_oauth_consumer_secret;
    private $m_oauth_token;
    private $m_oauth_token_secret;

    private $m_oauth_nonce;
    private $m_oauth_signature;
    private $m_oauth_signature_method = 'HMAC-SHA1';
    private $m_oauth_timestamp;
    private $m_oauth_version = '1.0';

    public function __construct()
    {
        //
        // set a time limit to unlimited
        //
        set_time_limit(0);
    }

    //
    // set the login details
    //
    public function login($_consumer_key, $_consumer_secret, $_token, $_token_secret)
    {
        $this->m_oauth_consumer_key     = $_consumer_key;
        $this->m_oauth_consumer_secret  = $_consumer_secret;
        $this->m_oauth_token            = $_token;
        $this->m_oauth_token_secret     = $_token_secret;

        //
        // generate a nonce; we're just using a random md5() hash here.
        //
        $this->m_oauth_nonce = md5(mt_rand());

        return true;
    }

    //
    // process a tweet object from the stream
    //
    private function process_tweet(array $_data)
    {
        print_r($_data);

        return true;
    }

    //
    // the main stream manager
    //
    public function start(array $_keywords)
    {
        while(1)
        {
            $fp = fsockopen("ssl://stream.twitter.com", 443, $errno, $errstr, 30);
            if (!$fp)
            {
                echo "ERROR: Twitter Stream Error: failed to open socket";
            } else
            {
                //
                // build the data and store it so we can get a length
                //
                $data = 'track=' . rawurlencode(implode($_keywords, ','));

                //
                // store the current timestamp
                //
                $this->m_oauth_timestamp = time();

                //
                // generate the base string based on all the data
                //
                $base_string = 'POST&' . 
                    rawurlencode('https://stream.twitter.com/1.1/statuses/filter.json') . '&' .
                    rawurlencode('oauth_consumer_key=' . $this->m_oauth_consumer_key . '&' .
                        'oauth_nonce=' . $this->m_oauth_nonce . '&' .
                        'oauth_signature_method=' . $this->m_oauth_signature_method . '&' . 
                        'oauth_timestamp=' . $this->m_oauth_timestamp . '&' .
                        'oauth_token=' . $this->m_oauth_token . '&' .
                        'oauth_version=' . $this->m_oauth_version . '&' .
                        $data);

                //
                // generate the secret key to use to hash
                //
                $secret = rawurlencode($this->m_oauth_consumer_secret) . '&' . 
                    rawurlencode($this->m_oauth_token_secret);

                //
                // generate the signature using HMAC-SHA1
                //
                // hash_hmac() requires PHP >= 5.1.2 or PECL hash >= 1.1
                //
                $raw_hash = hash_hmac('sha1', $base_string, $secret, true);

                //
                // base64 then urlencode the raw hash
                //
                $this->m_oauth_signature = rawurlencode(base64_encode($raw_hash));

                //
                // build the OAuth Authorization header
                //
                $oauth = 'OAuth oauth_consumer_key="' . $this->m_oauth_consumer_key . '", ' .
                        'oauth_nonce="' . $this->m_oauth_nonce . '", ' .
                        'oauth_signature="' . $this->m_oauth_signature . '", ' .
                        'oauth_signature_method="' . $this->m_oauth_signature_method . '", ' .
                        'oauth_timestamp="' . $this->m_oauth_timestamp . '", ' .
                        'oauth_token="' . $this->m_oauth_token . '", ' .
                        'oauth_version="' . $this->m_oauth_version . '"';

                //
                // build the request
                //
                $request  = "POST /1.1/statuses/filter.json HTTP/1.1\r\n";
                $request .= "Host: stream.twitter.com\r\n";
                $request .= "Authorization: " . $oauth . "\r\n";
                $request .= "Content-Length: " . strlen($data) . "\r\n";
                $request .= "Content-Type: application/x-www-form-urlencoded\r\n\r\n";
                $request .= $data;

                //
                // write the request
                //
                fwrite($fp, $request);

                //
                // set it to non-blocking
                //
                stream_set_blocking($fp, 0);

                while(!feof($fp))
                {
                    $read   = array($fp);
                    $write  = null;
                    $except = null;

                    //
                    // select, waiting up to 10 minutes for a tweet; if we don't get one, then
                    // then reconnect, because it's possible something went wrong.
                    //
                    $res = stream_select($read, $write, $except, 600, 0);
                    if ( ($res == false) || ($res == 0) )
                    {
                        break;
                    }

                    //
                    // read the JSON object from the socket
                    //
                    $json = fgets($fp);

                    //
                    // look for a HTTP response code
                    //
                    if (strncmp($json, 'HTTP/1.1', 8) == 0)
                    {
                        $json = trim($json);
                        if ($json != 'HTTP/1.1 200 OK')
                        {
                            echo 'ERROR: ' . $json . "\n";
                            return false;
                        }
                    }

                    //
                    // if there is some data, then process it
                    //
                    if ( ($json !== false) && (strlen($json) > 0) )
                    {
                        //
                        // decode the socket to a PHP array
                        //
                        $data = json_decode($json, true);
                        if ($data)
                        {
                            //
                            // process it
                            //
                            $this->process_tweet($data);
                        }
                    }
                }
            }

            fclose($fp);
            sleep(10);
        }

        return;
    }
};

The “process_tweet()” method will be called for each matching tweet- just modify that method to process the tweet however you want (load it into a database, print it to screen, email it, etc). The keyword matching isn’t perfect- if you search for a string of words, it won’t necessarily match the words in that exact order, but you can check that yourself from the process_tweet() method.

Then create a simple PHP application to run the collector:

require 'ctwitter_stream.php';

$t = new ctwitter_stream();

$t->login('consumer_key', 'consumer secret', 'access token', 'access secret');

$t->start(array('facebook', 'fbook', 'fb'));

You’ll need to provide the Consumer Key, Consumer Secret, Access Token, and the Access Secret, all of which are available from the Details section of your Application.

This new class uses the PHP hash_hmac() function for OAuth, which is available only in PHP 5.2.1 and up, and in the PECL hash extension 1.1 and up.

You can also Download the file here: http://mikepultz.com/uploads/ctwitter_stream.php.zip

Net_DNS2 Version 1.3.0 – More DNSSEC Features

This release includes many new DNSSEC changes, including a new, simple “dnssec” flag that tells the server to send all the DNSSEC related resource records for the given zone, as well as include the AD flag indicating if the data is authentic. This is analogous to the “+dnssec” option on the command line dig command.

Setting “dnssec” to true makes Net_DNS2 automatically add an OPT record to the additional section of the request, with the DO bit set to 1, indicating that we would like the DNSSEC information related to the given zone.

$resolver = new Net_DNS2_Resolver(array('nameservers' => array('8.8.8.8')));

$resolver->dnssec = true;

$result = $resolver->query('org', 'SOA', 'IN');

print_r($result);

Produces:

Net_DNS2_Packet_Response Object
(
    [answer_from] => 8.8.8.8
    [answer_socket_type] => 2
    [header] => Net_DNS2_Header Object
        (
            [id] => 31102
            [qr] => 1
            [opcode] => 0
            [aa] => 0
            [tc] => 0
            [rd] => 1
            [ra] => 1
            [z] => 0
            [ad] => 1
            [cd] => 0
            [rcode] => 0
            [qdcount] => 1
            [ancount] => 2
            [nscount] => 0
            [arcount] => 1
        )

    [question] => Array
        (
            [0] => Net_DNS2_Question Object
                (
                    [qname] => org
                    [qtype] => SOA
                    [qclass] => IN
                )

        )

    [answer] => Array
        (
            [0] => Net_DNS2_RR_SOA Object
                (
                    [mname] => a0.org.afilias-nst.info
                    [rname] => noc.afilias-nst.info
                    [serial] => 2010472684
                    [refresh] => 1800
                    [retry] => 900
                    [expire] => 604800
                    [minimum] => 86400
                    [name] => org
                    [type] => SOA
                    [class] => IN
                    [ttl] => 886
                    [rdlength] => 51
                )

            [1] => Net_DNS2_RR_RRSIG Object
                (
                    [typecovered] => SOA
                    [algorithm] => 7
                    [labels] => 1
                    [origttl] => 900
                    [sigexp] => 20130429014033
                    [sigincep] => 20130408004033
                    [keytag] => 31380
                    [signname] => org
                    [signature] => KBWEIC7BTypmbMTPU2KjCkPDbN1tV29ShWqa2zoGb4uQcRDBgYhz2ajpOaaJPrK+YY2E7BavLI+kulhJn9r/5kjXlOHQG/34B+OFlQwTTwHIRqtSmBu1qJorJSrSObQGVjZt4hteNVF6rfbS2u1m/Rh43eaoVCHfhJaeyr+MzLA=
                    [name] => org
                    [type] => RRSIG
                    [class] => IN
                    [ttl] => 886
                    [rdlength] => 151
                )

        )

    [authority] => Array
        (
        )

    [additional] => Array
        (
            [0] => Net_DNS2_RR_OPT Object
                (
                    [option_code] => 
                    [option_length] => 0
                    [option_data] => 
                    [extended_rcode] => 0
                    [version] => 0
                    [do] => 1
                    [z] => 0
                    [name] => 
                    [type] => OPT
                    [class] => 512
                    [ttl] => 32768
                    [rdlength] => 0
                    [rdata] => 
                )

        )
)

You can see that the response includes the original OPT RR in the additional section, with the DO bit set to 1. The header section also includes the AD bit set to 1, indicating that the server considers the data authentic.

I’ve also included the ability to adjust the AD flag  when making a query (to indicate to the server that we’d like the value of the AD bit, without having to set the DO bit in the OPT RR – see RFC6840 section 5.7), and to adjust the CD flag (telling the server that the client will perform it’s own signature validation).

Net_DNS2 does not validate the DNSSEC signatures itself, but it does provide all the data from DNS needed so that users can. Future versions of Net_DNS2 may provide support for this.

See the change log page for a full list of changes in this release.

You can install Net_DNS2 version 1.3.0 directly from PEAR, using the command line PEAR installer:

pear install Net_DNS2

Or download it directly from the Google Code page here.

Generator Labs: Automated, Real-Time Black List (RBL) Tracking

Generator Labs is a new project I’ve been working on- an automated, real-time black list (RBL) tracking service.

RBL and URIBL Monitoring

The Generator Labs system automatically scans over 60 RBLs,  and 20 URIBLs, multiple times per day, to see if any of your IP addresses or website domains are listed, giving you the peace of mind you need to focus on your business.

The list of RBLs that Generator Labs monitors will always be kept up to date with the most current list.

Hosts

Generator Labs is a fully automated monitoring service, which checks your IP addresses and website domains against the most frequently used real-time black lists (RBLs) and Safe Browsing Databases.

 

Contacts

Get alerted immediately when one of your hosts is found on an RBL, URIBL, or in a Safe Browsing database.

Your Generator Labs account can be configured with multiple email addresses and phone numbers, for receiving alerts about your hosts. Each contact can be individually configured with different notification rules, to control how each contact receives alerts when one of your hosts is blocked.

Google Safe Browsing

The Google Safe Browsing database includes lists of website domains that may be dangerous to visitors, because they are suspected of phishing or malware.

Generator Labs will check your websites against the Google Safe Browsing database, and alert you immediately if any errors are found, ensuring that your visitors can reach your websites.

API Access

Generator Labs includes a simple, read-only, REST based API, that lets you poll our database for the current status of your hosts.

The Generator Labs API can easily be integrated into existing monitoring systems, like Nagios or Zabbix, by performing a simple HTTP GET request for the list of currently blocked hosts. The response data can be returned either as simple XML, or as a JSON object.

<?php
    echo file_get_contents('https://generatorlabs.com/api/blocks.json?api_token=123');
?>

{
    "status_code": 200,
    "status_message": "Ok",
    "total_blocks": 1,
    "data": [
        {
            "id": "5afd618836c251cbb066803f25b87fa1",
            "host": "192.168.1.1",
            "name": "Primary Mail Server",
            "status": "active",
            "last_checked": "2012-12-30 21:00:07 EST",
            "first_blocked": "2012-12-17 11:05:03 EST",
            "block_period": "13 days 13:35:58",
            "blocked": "1"
        }
    ]
}

Don’t let your customers be the first to know when your email systems or websites get blocked.

Signup for FREE today!

 

Net_DNS2 Version 1.2.5 Released

I’ve released version 1.2.5 of the PEAR Net_DNS2 library- you can install it now through the command line PEAR installer:

pear install Net_DNS2

Or download it directly from the Google Code page here.

This release includes some important fixes to the way I was calculating the offset values when building the DNS packets. Here is the full list of changes for this release:

  • changed the socket_connect() code to start off non-blocking, and call select() after connect() so a timeout on a invalid server works properly
  • added the new TLSA RR – RFC 6698
  • fixed the socket defines again; apparently the values of the SOCK are different under solaris
  • changed the Net_DNS2_Updater::update() so you can pass a reference to a variable that will be populated with the response object
  • moved the lines that add the response server/type to after the is_null() check- it should have been there to begin with.
  • fixed a whole bunch of cases where I wasn’t incrementing the offset values properly
  • added support to set the RD (recursion desired) bit when making a request