Friday, June 25, 2010

blueipd - The public IP Address finder daemon

There are times when I had to figure out the public IP address of the network i was in.This blog describes a daemon which i had developed for personal use. This daemon can be installed to run at startup on Linux. I called it blueipdblueipd periodically sends a request to the server with date and time in xml format. The server looks at the HTTP headers of the request and figures out the Public Ip address of my office router.

There is php script on the server which formats and displays this in a presentable format. The php script can be enhanced to send an SMS/Email to a precofigured mobile number/Email Address when there is a change in Ip address. In short, you could look up a particular server url and figure out the public ip address of the network where blueipd is running from.

The code for blueipd , the initscript and the Makefile is available for download from www.devicemantra.com at this url Download

A use case for blueipd
I had an application server running on my PC at office on port 8888. This PC was in a LAN. I wanted to devise a way to access this server from home by typing in a url like www.devicemantra.com/myappserver. This script looks up the database and finds out the current public ip address of my office which was updated by blueipd. It then redirects (HTTP 302 Status code) me to the public ip and port number 8888. I had mapped (port mapped) my office routers port 8888 to an internal ip address and port number ( 192.168.1.2222 and port 8888). So all connections coming on 8888 are now redirected to my office PC's application server. And now, i can reach it with one permanent url !

Otherwise i had no way of finding out my office public ip, blueipd is really a very helpfull utility. It could also be used to do ssh logins by looking up the ip address and of course adding a port mapping on your office router.

blueipd - Directory structure.


The code is split into the following directory structure:
ipaddressdaemon
- initscript
-src
       - app
       - bluehttp
       - bluelogger
       - bluexml
       - include
       - Makefile

First let us look at the main code for the daemon and go through it step by step.
blueapp.cpp

The main loop for the daemon

#include<string>
#include<iostream>
using namespace std;

#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
#include<syslog.h>


/* Application includes */
#include<bluemantra.h>
#include<bluehttppost.h>
#include<bluexml/bluexmlbuilder.h>

#define BLUEMANTRA_POST_URL     "http://www.devicemantra.com/bluemantra/time_update.php"
#define BLUEMANTRA_FORM_NAME     "time"
#define BLUEMANTRA_FORM_CONTENT_TYPE "application/xml"
#define BLUEMANTRA_INTERVAL     3600

bool bDoExit = false;


void  daemonize()
{
    /* Start logging to the syslog daemon */
    openlog(" blueipd  ",LOG_PID,LOG_DAEMON);
    syslog(LOG_INFO,"Started .....");


    /* First check whether we are a daemon already ? */
    if( 1 == getpid() )
    {
        //"Already a daemon pid = "<<getpid()<<endl;
        return;
    }
   

    pid_t pid = fork();
   
    /* Handle fork failure */
    if( pid < 0 )
    {
        syslog(LOG_INFO,"Fork failed exiting ..");
        exit(-1);
    }

    if( pid > 0 )
    {
        syslog(LOG_INFO,"Parent process exiting ..");
        exit(0);
    }

    /* From now on it is the child which is executing */
   
    /* Let us install a SIGNAL handler */

    signal(SIGTERM,SIG_IGN);   
    signal(SIGHUP,SIG_IGN);   
   
    /* First let us close all the open file descriptors */
    for (int count = getdtablesize();count >=0 ; count--)
    {
        close(count); /* close all descriptors */
     }

    /* Change the session id for this process */

    setsid();


    /* Start logging to the syslog daemon */
    openlog(" blueipd  ",LOG_PID,LOG_DAEMON);
    BLUEMANTRA_RESULT lResult;
    int lCount=1;
    while( false == bDoExit )
    {
        syslog(LOG_INFO,"Executing Main  loop");
        BlueXmlBuilder *lXmlBuilder = new BlueXmlBuilder();
        string lXmlStr = "";
        BlueHttpPost *lPoster = new BlueHttpPost();
        lPoster->SetUrl(BLUEMANTRA_POST_URL);
        lXmlBuilder->GetTimeInXml(lXmlStr);
        if(BLUEMANTRA_SUCCESS == lPoster->Post(BLUEMANTRA_FORM_NAME, lXmlStr, BLUEMANTRA_FORM_CONTENT_TYPE))
        {
            syslog(LOG_INFO, "[SUCCESS] Posting to www.devicemantra.com");
        }
        else
        {
            syslog(LOG_ERR, "[ERROR]  Posting to www.devicemantra.com");
        }
               
        delete lXmlBuilder;
        delete lPoster;
        syslog(LOG_INFO,"Finished executing loop");

        /* Let us sleep for some time now */
        sleep(BLUEMANTRA_INTERVAL);
    }
}
   
int main()
{
    daemonize();
}

blueapp.cpp - Code walkthrough 

The code for the main loop is quite straight forward. The important parts are highlighted. The daemonize function does all the work.
  1. It first forks to create a child process.
  2. Installs some signal handlers to handle ignore SIGTERM, SIGHUP signals delivered to it. 
  3. Closes all file descriptors for the child process. 
  4. setsid() call detached the process from the parent and controlling terminal.
  5. Create a BlueXmlBuilder and BlueHttpPost (Helper classes for building XML and HTTP request objects, please download and check the code for more) objects.
  6. Post to the server url. 
  7. Sleep for a predefined interval.
Note: Steps 1 to  4 can be used to write any simple daemon.

Installing blueipd on your system.
1) Download the C++ source + init scripts + php script together from this link - Download.
2) Unzip it to the directory ipaddressdaemon in your home directory.
3)  run the make command in the src directory.
4)  If everything went fine you will find blueipd (executable in the src/app directory).
5)  Copy blueipd to /usr/bin as root. 
6)  Copy ipaddressdaemon/initscript/blueipd to /etc/rc.d/init.d/ directory
7)  chkconfig --add blueipd  adds the blueipd daemon to services.
8)  chkconfig --level 345  blueipd on   - enables blueipd in runlevels 3,4 and 5.
9)  service blueipd start  - should start the blueipd daemon on your system.


The only thing that is left to be done is uploading the script to the web server and pointing to that url from the blueipd.

Installing the php script on the server

<?
  $time_xml = $_POST['time'];
  $pa_public_ip = $_SERVER['REMOTE_ADDR'];
  $domain = GetHostByName($REMOTE_ADDR);

  if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]))
  {
   $pa_public_ip =  $_SERVER["HTTP_X_FORWARDED_FOR"];
  }
  else
  {
   $pa_public_ip =  $_SERVER["REMOTE_ADDR"];
  }

 
 
 The above php script logs the public ip address in the database. The code is self explanatory but not well structured !  I feel such code does not need undue attention !

  1. The code retreives the the Public ip address by looking at the incoming post request $pa_public_ip = $_SERVER['REMOTE_ADDR'];
      $domain = GetHostByName($REMOTE_ADDR);

      if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]))
      {
       $pa_public_ip =  $_SERVER["HTTP_X_FORWARDED_FOR"];
      }
      else
      {
       $pa_public_ip =  $_SERVER["REMOTE_ADDR"];
      }                           
That is all there is to it. It is a very simple script the actual script does more like logging it to the database and updating the time etc. That part can be added by anyone with a little knowledge of php or any scripting language for that matter and mysql.

 The blueipd init script. ( I modified vsftpd init script for this one ).

#!/bin/bash
#
### BEGIN INIT INFO
# Provides: blueipd
# Short-Description: blueipd Daemon
# Description: blueipd is a public ip address finder daemon.

### END INIT INFO


# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

RETVAL=0
prog="blueipd"

start() {
        # Start daemons.

    # Check that networking is up.
    [ ${NETWORKING} = "no" ] && exit 1


        if [ -f /usr/bin/blueipd ] ; then
                        echo -n $"Starting $prog for $site: "
                        daemon /usr/bin/blueipd
                        RETVAL=$?
                        [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
                        echo
                done
        else
                RETVAL=1
        fi
        return $RETVAL
}

stop() {
        # Stop daemons.
        echo -n $"Shutting down $prog: "
        killproc $prog
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
        return $RETVAL
}

# See how we were called.
case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart|reload)
        stop
        start
        RETVAL=$?
        ;;
  condrestart)
        if [ -f /var/lock/subsys/$prog ]; then
            stop
            start
            RETVAL=$?
        fi
        ;;
  status)
        status $prog
        RETVAL=$?
        ;;
  *)
        echo $"Usage: $0 {start|stop|restart|condrestart|status}"
        exit 1
esac

exit $RETVAL


The redirect script
Name : myappserver.php 
This script redirects to my appserver when you access this url from a browser. 
Upload this script to your server. There has to be a port mapping on port 4444 on your office router to your appserver / Other Applications  ip address and port.

<?php
 /* Open connection to the database */
   include '../library/config_bm.php'; 
 include '../library/opendb.php';
 
 header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past

 $select_sql = "SELECT * from public_ip_table LIMIT 1;";
 $result = mysql_query($select_sql) or die('Query failed.'. mysql_error());

 $num_rows = mysql_num_rows($result);
 if( $num_rows > 0 )
       {
         /* Check the details */ 
           $row = mysql_fetch_row($result);
         $redirect_address = "Location:"."http://".$row[1].":4444";
         header($redirect_address);       }else {
echo "FAILED: to fetch public ip "; 
}    
?>

The script queries the database for the ip address blueipd has updated. Then redirects (302 HTTP response)  the page to that ip and port number.

I hope there is enough information to implement this Public Ip Address daemon and have it working for you. Please use this link to download the code and see it for yourself. Good luck !

For any questions or feedback please send an email to amithmit (at) yahoo (dot) com.

- Voice~Streams




1 comment:

  1. Good information about the Finding ip address process...But is it using to check out the ip easily? Normally we can get the IP address details from here http://www.ip-details.com/ @ free of costs.. Also in here we can get internet speed...

    ReplyDelete

Followers

About Me

I'm a software developer with interests in Design Patterns, Distributed programming, Big Data, Machine Learning and anything which excites me. I like to prototype new ideas and always on the lookout for tools which help me get the job done faster. Currently, i'm loving node.js + Mongodb.