Turn your Java application into a Botnet

From JVMLanguages

Getting Inside Your Programs

Enough with the theory -- let's get practical. Imagine you're a developer working on a client-side Java application that is deployed to a few thousand users across a global corporate intranet. This application's configuration is complex, and it maintains connection pools to a wide variety of remote services. It is your job to work with your support team to tweak the overall performance of the system in response to network latency and bandwidth constraints. To do this, you will need to start and stop services, monitor and adjust connection pool statistics, tweak configurations individually for each user, and perform other minor tasks behind the scenes. Oh, and by the way, you only have a few days to put something in place before the final build happens. What are you going to do?

Well, one way to approach this problem is to have all of your applications connect to a centralized service and post statistics as well as listening for requests to perform some action. However, you're not sure exactly what tasks you'll want the application to perform, or what information you may want to extract in order to make this decision later. You can't possibly enumerate every possible part of the system that may need to be analyzed and tweaked ahead of time. Even if you did, you wouldn't want to take the time to write actual Java code to do this, if only a very small percentage of it would ever be used. In addition, it's likely that every bit of support code that you do write will make the application more brittle and difficult to modify in the future. The basic problem that you've encountered here is that while the vast majority of your application must be maintainable, which requires well-architected and encapsulated code, and rock-solid, which requires efficient and resiliant code, this part of the application doesn't need to be any of those things. If you were adding new business features to the application, some design time and added complexity would be a small price to pay for the improved flexibility of each component and enhanced functionality. However, in this case you don't want to increase the complexity of the application, and you don't really need a well-architected, efficient solution. You simply need a backdoor, where you can easily peek into the application and maybe twiddle a few bits.

So, what's the best way to accomplish this? I would argue that Java is not the right tool for the job. By leveraging the strengths of other, more dynamic programming languages, this problem can be solved quickly and efficiently. What tools do we already have available that can be used to solve this problem? First, we need a communication mechanism. JMX might be a good tool to use here, or perhaps even SNMP, but neither of these will help us get this done quickly. You could create your own solution using RMI, SOAP, or even pure sockets, but then you need to create a client application as well. You'll need a way to address the client applications either individually or in groups (perhaps divided geographically, or by release number). The best tool that I can think of for this, believe it or not, is IRC. Off-the-shelf IRC servers and clients are readily available -- there's a good chance that your organization uses both already -- and an IRC interface is relatively easy to build into your application.

There are various Java components that can act as IRC clients that we could use, but this only addresses a very small portion of our requirements (the communication protocol). You also need some way to allow the support team to poke around inside of the application. In theory, you could build a parser for a query language and use Java reflection to retrieve data, but this is starting to sound like an incredible amount of work. But there are already programming languages that can be parsed and interpreted at run-time, and many of them can interface with existing Java code using reflection.

In fact, there is one language in particular which:

  • has a very powerful and relatively standard IRC client component,
  • can be parsed and executed dynamically, and
  • is likely to be used by the support team already.

Care to guess which? Take a look at this:

   use Net::IRC;

   my $number = 1;
   my $irc = new Net::IRC;
   my $conn = $irc->newconn(Nick => "bot0001", Server => "localhost") or die;

   $conn->add_handler([376,422], sub { shift->join("#botnet") });
   $conn->add_handler(433, sub { shift->nick(sprintf("bot%04d", ++$number)) });

   $conn->add_handler('public', sub {
       my ($self, $event) = @_;

       unless ($event->nick =~ /bot/) {
   	my $res = eval(($event->args)[0]);
   	$self->privmsg($event->to, $@ || $res);
       }
   });

   $conn->add_handler('msg', sub {
       my ($self, $event) = @_;

       my $res = eval(($event->args)[0]);
       $self->privmsg($event->nick, $@ || $res);
   });

   $irc->start;

With just that much code you could have a fully functional IRC bot that can respond to interactive requests (in the form of arbitrary Perl expressions). If you were to run this as a standalone Perl script, you'd have a fun little toy that could perhaps be used as a calculator. However, instead I'm going to do something far more interesting. Remember that Java application that you were trying to support? I'm going to embed this script within it using something called the Bean Scripting Framework. Now we are not limited to simply evaluating arbitrary Perl expressions, but we can actually reference Java classes and invoke Java methods.

By integrating the code in Example 3 with Java, we can take the ordinary Java classes that already exist in our application, and know nothing about IRC or user interfaces, such as:

   package com.oreilly.javalangint.diagbot;

   public class ConnectionPool
   {
       public int getNumOpenConnections() { ... }
       public int getMaxConnections() { ... }
       public void flushConnections() { ... }
   }

and interact with them directly in real-time through IRC:

   #botnet> $pool = $bsf->lookupBean("ConnectionPool")
   <bot0001:#botnet> com.oreilly.javalangint.diagbot.ConnectionPool@a981ca
   <bot0002:#botnet> com.oreilly.javalangint.diagbot.ConnectionPool@a981ca
   <bot0003:#botnet> com.oreilly.javalangint.diagbot.ConnectionPool@a981ca
   <bot0004:#botnet> com.oreilly.javalangint.diagbot.ConnectionPool@a981ca
   #botnet> sprintf("%d/%d", $pool->getNumOpenConnections, $pool->getMaxConnections)
   <bot0001:#botnet> 6/512
   <bot0002:#botnet> 12/512
   <bot0003:#botnet> 510/512
   <bot0004:#botnet> 8/512
   ->bot0003> $pool->flushConnections
   ->bot0003> sprintf("%d/%d", $pool->getNumOpenConnections, $pool->getMaxConnections)
   *bot0003* 0/512

You can even define variables and functions that can simplify repeated tasks:

   #botnet> sub percent { sprintf("%.2f%% used", 100 * $pool->getNumOpenConnections / $pool->getMaxConnections) }
   #botnet> percent()
   <bot0001:#botnet> 1.17% used
   <bot0002:#botnet> 2.34% used
   <bot0003:#botnet> 99.61% used
   <bot0004:#botnet> 1.56% used

Or you can schedule periodic events to notify the support staff:

   #botnet> $conn->schedule(60, sub { shift->privmsg("#botnet", "warning: " . percent()) if ($pool->getNumOpenConnections > 100) })
   ... 60 seconds later...
   <bot0003:#botnet> warning: 99.61% used
   ... 60 seconds later...
   <bot0003:#botnet> warning: 98.63% used
   ...

I know what you're thinking -- "No fair, you cheated! That's Perl." Yes, it is. Perl has a simple, well-written, standard IRC module. Perl can parse and evaluate dynamic content efficiently. And as you'll see in later chapters, integrating Perl into Java in this way is almost trivial. Is this solution particularly secure? No, but it could be made so. Is it going to win you awards for your fantastic architecture? Maybe not. But it's fast, it's easy, and it's amazingly powerful. That's the purpose of this site -- building a community of people learning to leverage the strengths of other programming languages without giving up the knowledge, experience, and code that you rely on right now.