Article Figure 1 Figure 2 Table 1 sep2006.tar

VNC Port Monitoring

George Wolanin

A dilemma surfaced at my office where users needed remote access to a server for capturing network traffic using Ethereal. Corporate network security wouldn't allow X display to be sent because of a security vulnerability with ports that use it, so my responsibility as the systems administrator was to provide a solution that provided the access. My solution was to use VNC (http://www.realvnc.com) on the Ethereal servers.

VNC is the access of choice because of its ability to work on multiple platforms and its ease of use and user setup. The VNC servers (located in different states) have approximately 10 VNC server sessions running on them to which the users can connect. With this many VNC server sessions, I needed a way to monitor the connections for troubleshooting purposes.

The Problem

After a few months with multiple VNC servers running on a couple of servers and users capturing network data, I became aware of some instability issues with the servers. Users were complaining that their access would disappear and then come back with all of their test results gone. To keep it short, the users who couldn't access their assigned VNC port were rebooting the server to get it back (at the time all trusted users had root access), because I had started the VNC server process with the -DisconnectClients=off option:

#vncserver -DisconnectClients=off 
            
This option prevents a user from "stealing" the session when someone else is connected. Since the root password was known to a couple of users, the initial solution was to reboot the server. However, rebooting the server interrupted other users who were capturing network traffic and caused a systems administration nightmare! This is the action that provoked me to create a way to monitor the ports and provide that availability to the users so they would know which port they could use.

The Solution

The solution uses a Perl script that checks the VNC server port status and updates a MySQL database. I used Perl because of its availability on multiple platforms, such as RHEL and Solaris OS. The Web script is based on a very simple PHP script that accesses the MySQL database to make the status visible on the Web. Finally, there is an addition to the crontab that will need to be added to each server that contains the Perl script, and its responsibility is to talk to the database server updating its VNC port status.

Scripts

The Perl script is the heart of the monitoring tool; it does two things: it checks current server VNC port status, and updates the MySQL database. Since VNC utilizes the ports 5901-59xx for *nix-based systems, the netstat command comes in very handy to find the current connections to those ports in getting their connection status for VNC. Another option would be to use the lsof command within the script. The script is installed on each server that will have the multiple VNC ports active as shown in Figure 1.

Setup

To start multiple VNC sessions at system boot, add the following script to the end of the /etc/init.d/rc.local file on Linux. Note that this script will create vncserver sessions for the same UID, and you must have already executed the VNC server command at the prompt as the same UID to create the VNC password file:

Append to /etc/init.d/rc.d/rc.local 
## 
## NOTE THAT THIS SCRIPT IS NOT A REPLACEMENT FOR rc.local 
## but add the following to the end of rc.local 
## 

n=0; 
while [ "$n" -lt x ] ## x being the number of VNC server  
                     ## sessions you want 
do 
    su UID -c "/usr/bin/vncserver -DisconnectClients=off" &
    n=$(expr $n + 1) 
    sleep 5 
done 
    
Create a database in MySQL then import vnc.sql to create the table definitions:

vnc.sql 
CREATE TABLE `servers` ( 
  `id` int(5) NOT NULL auto_increment, 
  `date_time_checked` timestamp NOT NULL default CURRENT_TIMESTAMP, 
  `server` varchar(50) NOT NULL default '', 
  `server_ip` varchar(16) default NULL, 
  `client` varchar(16) default NULL, 
  `vncport` int(1) NOT NULL default '0', 
  `status` varchar(35) NOT NULL default '', 
  PRIMARY KEY  (`id`) 
)
I usually place my cron scripts in /usr/local/scripts:
vnccheck.pl 
#!/usr/bin/perl 
# 
use Shell qw( netstat ); 
use DBI; 
use Socket; 
use Sys::Hostname; 

## Enter the server's IP address that this script 
## Will be located on 

$local_ipaddr = ""; 
$hostname = hostname(); 

## 
## the netstat -at option looks at all TCP connections 
## below we will search for TCP ports 5900 - 5919 
## 

@lis = netstat('-at | grep 59[0-1][0-9] | grep LISTEN'); 
@est = netstat('-at | grep 59[0-1][0-9] | grep ESTABLISHED'); 

foreach $i ( @lis ) { 
      @tmp = split( /:/, $i ); 
      push @pAssign, substr( $tmp[1], 0, 4 ) 
} 

foreach $k ( @pAssign ) { 

      $found = 0; 
      foreach $m ( @est ) { 

            @tmp = split( /:/, $m ); 
            @ip =  split( / +/, $tmp[1] ); 

            if ( $k == $ip[0] ) {   
                  ### print "VNC port $k is taken by $ip[1]\n"; 
                  $found = 1; 
            
                  push @final, "$k\t$ip[1]";       
     
                  last; 
            } 
      } 

      if ( $found == 0 ) { 
           ### print "VNC port $k is AVAILABLE\n"; 
                 push @final, "$k\tAVAILABLE"; 
      } 
} 
print "------------------QUERY RESULTS-------------------------\n";
 
### ACCESS DB AND STORE THE QUERY RESULTS ###
 
my $dsn = 'dbi:mysql:vnc_ethereal:$dbIP:3306'; 
my $user = 'vnc'; 
my $pass = 'password'; 
my $dbh = DBI->connect( $dsn, $user, $pass ) 
      or die "Cannot connect to the DB:  $DBI::errstr\n"; 

### CHECK TO SEE IF SERVER IS ENTERED INTO THE DATABASE 
### IF NOT, ADD IT 
### IF IT IS, CHECK TO SEE IF THE CURRENTLY HELD PORTS ARE IN THE DB 
### IF NOT, INSERT THEM 
### IF THEY ARE, UPDATE THEM 

$sE = 0; 

$sth = $dbh->prepare( "SELECT server FROM servers WHERE \
  server_ip = '$local_ipaddr'" ); 
$sth->execute(); 

if ( $sth->rows == 0 ) { 
     print "SERVER: $local_ipaddr does not exist in the database\n";
} else { 
     $sE = 1; 
}
 
### 
### write VNC info to DB 
###
 
### If serverIP exists, delete ALL records containg it, and 
### rewrite them (this should be reworked to be more efficient 

print "value of sE: $sE\n"; 

if( $sE == 1 ) { 
      $sth = $dbh->prepare( "DELETE FROM servers \
        WHERE server_ip = '$local_ipadddr'" ); 
      $sth->execute(); 
} 

foreach $a ( @final ) { 

      @tmp = split( /\t/, $a );
 
      ### 
      ### tmp[0] = VNC PORT 
      ### tmp[1] = IP | {connected ip} 
      ### 

      my $wth = $dbh->prepare( "INSERT INTO servers ( server, \     
        server_ip, vncport, status ) VALUES \       
        ( '$hostname','$local_ipaddr','$tmp[0]','$tmp[1]' ) " ); 
      $wth->execute(); 
} 
PHP Web Script

The PHP Web script is a simple script that accesses the database that has the data for each VNC server. The following is my PHP script to pull the data from the database:

vnc.php 
<html>
<head>
<title>VNC Port Status</title>
<meta http-equiv=" Content-Type" content=" text/html; charset=iso-8859-1">
<style type="text/css">
<!-- 
.vncstyle { 
      font-family: "Lucida Sans"; 
      font-size: 12px; 
} 
-->
</style> 
</head>

<body>
<p align="center">&nbsp;</p>
<p align="
center"
>The content of this page gets updated every 15 minutes</p>
<p align="center">Click REFRESH/RELOAD on your browser to see the \
     latest info</p> 
<p align="center">(i.e. HR:00, HR:15, HR:30, HR:45)</p>
<p align="left">
  <?php 
$dbServer="localhost"; 
$dbName="your_db_name"; 
$dbUser="your_db_user"; 
$dbPass="your_db_pass"; 

$dbConn = mysql_connect( $dbServer, $dbUser, $dbPass ); 
if ( !$dbConn ) { 
      die( 'Could not connect to the database:  <b>' . mysql_error() ); 
} 

$db_selected = mysql_select_db( 'vnc_ethereal' ); 
if( !$db_selected ) { 
      die( 'Could not connect to the database : <b>' . mysql_error() ); 
} 

$result = mysql_query( "SELECT * FROM servers ORDER BY server,vncport ASC" ); 
if( !$result ) { 
      die( 'Could not query the vnc_server database : <b>' . mysql_error() ); 
} 
print "<table class=\"vncstyle\" align=\"center\" border=\"1\">\n"; 

$previous = ""; 
            print "<tr>\n"; 
            print "\t<td align=\"center\" valign=\"top\"><b>VNC \           
              server</b></td>\n"; 
            print "\t<td align=\"center\" \         
              valign=\"top\"><b>Date/Time Last Checked</b></td>\n"; 
            print "\t<td align=\"center\" \         
              valign=\"top\"><b>VNC Port</b></td>\n"; 
            print "\t<td align=\"center\" \         
              valign=\"top\"><b>Status of Port</b></td>\n"; 
            print "</tr>";
while( $row = mysql_fetch_assoc( $result ) ) { 
      $current = $row['server']; 
      if( $current != $previous ) { 
            print "<tr>\n\t<td colspan=\"4\" align=\"left\" \
              valign=\"top\"><b>"; 
            print $row['server'] . " -- " . $row['server_ip']; 
            print "</b></td>\n"; 
            print "</tr>"; 
      } else { 
            if( $row['vncport'] != "5801" || $row['vncport'] \     
              != "5901" ) { 
                   $linkme = "<a href=\"http://" . \ 
                     $row['server_ip'] . ":" . str_replace( '59', \     
                     '58', $row['vncport'] ) . "\" \           
                     target=\"_new\">AVAILABLE</a>"; 
                   // $linkme = "AVAILABLE"; 
                   print "\t<td>&nbsp;</td>\n"; 
                   print "\t<td>" . $row['date_time_checked'] . "</td>\n"; 
                   print "\t<td align=\"center\">" . \           
                     $row['vncport'] . "</td>\n"; 
                   print "\t<td align=\"center\">" . \           
                     ( ( $row['status'] == "AVAILABLE" ) ? \             
                     $linkme : $row['status'] ) . "</td>\n</tr>"; 
            } 
      } 
      $previous = $row['server']; 
} 
print "</table>\n"; 
mysql_free_result( $result ); 

?>
</p>

</body> 
</html> 
Figure 2 shows a screenshot of what the VNC monitor will look like with the attached PHP script. As you can see there are four columns. These are defined in Table 1.

Next, install the following entry into root's crontab:

su - 
crontab -e 

0,15,30,45 * * * * /usr/bin/perl /usr/local/scripts/vnccheck.pl 
Conclusion

This solution has provided all the current users with an easy way to see the status of a port. I do not receive many stability issue calls on these servers anymore. Occasionally, there are a few problems with a particular Linux server. When this user disconnects from a separate network that is within our normal backbone network, the VNC server doesn't disconnect the connection and continues to show it as being used by that user. Other than this single issue, there haven't been any drastic problems for users like I had experienced before implementing this solution. When users are happy, I can go home for the day.

I hope you will have some use for these scripts or even enhancements. Feel free to email me with ideas or questions, and I will be more than happy to answer them for you.

George Wolanin has been a system/network and security administrator in AT&T labs for eight years. His expertise is with Solaris and Linux. He holds a BSEET from DeVRY Institute of Technology (DeVRY University) and Master's Degree in Information Systems Management with a specialization in security from Keller Graduate School of Management.