CA Tunneling with CAXY and SSH

Till Straumann <strauman@slac.stanford.edu>, 6/2012.

caxy is now also available as an implementation in java. This means that you don't have to compile anything but you can use the pre-compiled application on any platform supporting java (>= 1.5; 1.6 required for auto_address_list support).

Introduction

The CA protocol uses both, TCP and UDP messages. While it is easy to tunnel the former using the powerful port-forwarding features of SSH it is not straightforward to tunnel UDP messages.

This document explains how to set up a TCP tunnel and documents the caxy proxy software that encapsulates (and modifies) CA-UDP messages for tunneling them via TCP.


Quick Instructions for the Impatient

Terminology

insidehost
ssh-reachable machine on CA network ("inside"); has direct connectivity to IOCs.
outsidehost
'laptop' out on the internet ("outside"). Wishes to reach CA network via a secure tunnel.

Prerequisites

1. Launch Tunnel

On outside machine issue (in command line window):

LINUX
  java -jar caxy.jar -- ssh -D1080 user@insidehost java -jar caxy.jar -I 
WINDOWS

Using PUTTY; we assume a complete configuration 'inside' has already been created and saved under the name 'inside'. We also assume authentication has been resolved either using GSSAPI or public key files etc. As a last resort, you'd have to append a -pw <password_in_clear> option to the plink command here:

  java -jar caxy.jar -- plink inside java -jar caxy.jar -I

Once the tunnel is up, the message 'CAXY -- tunnel now established' appears. The tunnel remains running in the shell's 'foreground' and you can take it down by hitting <Ctrl-C> to kill it (and a few ugly error messages appear).

2. Tell CA library to Use Tunnel

You have to tell CA clients to use the tunnel by

either

using a PATCHED EPICS CA library and setting:

  export EPICS_SOCKS_PROXY=localhost

or

proxify current session (e.g., for DANTE -- note that the config file has to be set up beforehand; consult DANTE docs)

  . socksify

3. Use CA Clients Normally

Now you can use CA clients normally:

  caget my_PV


DETAILED DOCUMENATION OF INNER WORKINGS AND USE

TCP Tunnel

The most flexible way for tunneling CA TCP connections is using ssh's SOCKS proxy implementation together with a SOCKS wrapper implementation (a so-called proxifier) like tsocks or dante (client) or alternatively: patched versions of the EPICS CA client libraries (C/C++ or java, respectively). The patches which implement native SOCKS support for EPICS-CA have been submitted to the maintainers but are not yet present in the current releases of EPICS base or CAJ. Using these patches obsoletes the need for a proxifier.

Assume we have two hosts 'inside' and 'outside'. 'inside' is directly connected to the CA network, i.e., it can directly communicate with EPICS IOCs, receives their CA search replies, beacons etc.

The 'outside' host may only connect to the CA network via ssh.

On the 'outside' machine execute

  ssh -D 1080 user@inside

This sets up a SOCKS proxy on the standard port 1080. The ssh proxy - in combination with the SOCKS-patch for EPICS or (alternatively) a so called 'proxifier' wrapper library - can transparently tunnel TCP connections. E.g., if you issue

  tsocks telnet another_inside_machine

Then you can transparently telnet over ssh and here's how it works:

  1. tsocks library intercepts the 'connect(another_inside_machine:23)' system call and connects to the proxy host instead (e.g., 'localhost:1080').
  2. tsocks library negotiates with SOCKS proxy (1080 port on localhost talks over encrypted channel with sshd on 'inside') to create a connection to 'another_inside_machine:23'.
  3. sshd creates a new (not encrypted) connection from 'inside' to 'another_inside_machine:23' and forwards all further traffic going through the intercepted connection (steps 1+2) to/from this new one.
  4. tsocks library returns from 'connect' with the user's socket now apparently connected to 'another_inside_machine:23' (via 'proxy_host:proxy_port').
  5. user/client talks to socket as if a direct connection existed.

Consult tsocks or dante documentation for how to set up those packages (it is really easy; just have to define routes that use the proxy instead of trying to establish direct connections).

Native SOCKS Support for EPICS

A patch 'base-<vers>-socks-proxy.diff' to EPICS-base is available which adds SOCKS support to EPICS. This means that you can execute CA clients without the need for proxifier software (free versions of such software seems hard to come by under windows). You also don't have to worry about learning how to set up the proxifier.

The patch adds a new function 'epicsConnectViaProxy()' which is a plug-in replacement for 'connect()' but knows how to negotiate with a SOCKS proxy server when establishing a connection.

The patch also replaces the call to 'connect()' in the CA client library with a call to epicsConnectViaProxy().

You can define a proxy by setting the environment variable 'EPICS_SOCKS_PROXY' to point to your proxy server. If you use an ssh tunnel with 'dynamic port forwarding' then the ssh client (running on your machine) is the SOCKS proxy server. Hence you just need to

  export EPICS_SOCKS_PROXY=localhost

and you're done. Easy enough, isn't it? If the EPICS_SOCKS_PROXY variable is not defined then epicsConnectViaProxy() behaves just like an ordinary 'connect()', i.e., a direct connection to the target is established.

Note: If your CA libraries are dynamic libraries then you dont have to rebuild all your CA applications! Just patch EPICS base and rebuild base. Nothing in the API that would compromise binary compatibility is changed by this patch.

A analogous patch is available for the CAJ native java implementation of CA. Consult the section about CAJ and CSS below.

UDP Tunnel

This is trickier. While SOCKS-5 does define support for the UDP protocol this is not implemented by ssh. Hence, tunneling CA-UDP does not work out of the box.

An alternative to the 'caxy' program would have been a more generic proxy that handles SOCKS-5/UDP and tunnels the UDP frames via TCP/SSH. However, SOCKS-5/UDP only supports UDP 'connections' that originate at the SOCKS client (i.e., frames being sent to a peer and the peer repeating back to the sender/client). Hence, even with SOCKS-5/UDP it would not be possible to relay CA beacons which originate at EPICS IOCs.

This is where the 'caxy' program comes in. This program wraps CA-UDP messages and ships them through a dedicated TCP tunnel (which is set up in the usual way with ssh).

The same program is executed (with different flags) on the 'inside' and 'outside' machines, respectively.

'caxy' on 'outside' machine:

A UDP socket on port CA_SERVER_PORT (overrideable with the EPICS_CA_SERVER_PORT env-var) listens for incoming CA packets. The packet is then written to a TCP connection along with wrapping information that identifies the sender of the packet.

Here is a diagram:


Ascii-Art Legend

UDP communication ::

Machine-network boundary - - - - -

TCP communication ||

IPC boundary . . . . .

TCP encrypted XX

IPC between ssh/caxy (**)

UDP port |__|

TCP port ( )


                CA-client
                  ____
                 |    | UDP port
- - - - - - - - -     - - - - - - - - - - - - - - -
                  : ^       
                  : :
                CA_SEARCH               'outside'
                  : :                    network
          request : : reply                       
                  V :                              
- - - - - - - - -    - - - - - - - - - - - - - - - -
                |____| UDP port 5064 
  outside         ::   (CA srvr port)
  CAXY            ::
             encaps/decaps              'outside'
                  ||                      host
 . . . . . . . . (**) . . . . . . .
  SSH (clnt)      ||
                 (  )
- - - - - - - - -   - - - - - - - - - - - - - - - - 
                  XX
                  XX secure TCP         Internet
                  XX Channel
- - - - - - - - -   - - - - - - - - - - - - - - - -
                 (  )
  SSH (srvr)      ||
 . . . . . . . . (**) . . . . . . .
                  ||
  inside    decaps/encaps               'inside'
  CAXY            ::                      host
                  ::
                |    | UDP port
- - - - - - - - -    - - - - - - - - - - - - - - - 
                 :  ^
                 :  :
               CA_SEARCH                'inside'
         request :  : reply          (CA) network
                 V  :
- - - - - - - - -   - - - - - - - - - - - - - - - -
                |___| UDP port 5064
        
              CA server (IOC)           EPICS IOC

'caxy -I' on 'Inside' Machine

receives wrapped UDP messages from TCP tunnel, unwraps and sends to all IOCs listed in EPICS_CA_ADDR_LIST (as defined for caxy running 'inside').

For each 'sender' (in the outside world) the 'inside' caxy program maintains a UDP socket as a proxy so that replies arriving at that socket can be sent back (via the TCP tunnel) to the 'sender' of a request on the 'outside'.

When UDP messages arrive at a proxy socket then 'inside' retrieves the 'sender' address information from a trivial 'database', wraps the UDP message together with this info and stuffs it into the TCP tunnel.

'Outside' Machine

receives encapsulated UDP message, unwraps, retrieves 'sender' address and sends back to 'sender'.

CA SEARCH REPLY Hack

The CA protocol (since V48) support a feature where the IOC sending a search reply embeds its own address into the reply message. If this embedded address is undefined then the CA client assumes the sender of the reply is the IOC. Obviously, this would not work with this type of proxy (w/o resorting to tricks that intercept system calls). Therefore, caxy ensures that the server address is always embedded into search replies. Note that caxy does not work with very old (pre-V48) IOCs and/or clients.

Beacons

In a simliar way, CA repeater traffic is proxied and tunneled. The 'inside' caxy program registers with a caRepeater that must be running on the 'inside' machine (caxy does not attempt to launch a repeater).
Any traffic received from the 'inside' repeater is forwarded to the outside and sent to the repeater port on the outside machine (if a caRepeater is running on the outside machine then all subscribed clients will receive beacons). Likewise, the `outside' caxy does not attempt to start a repeater nor check if one is running.


               CA-server
                (IOC)
                 ___
                |   | UDP port
- - - - - - - - -   - - - - - - - - - - - - - - -
                  :
                  :                    'inside'
                CA-BEACON             (CA) network
                  :
                  V 
- - - - - - - - -   - - - - - - - - - - - - - - -
                |___|  UDP port 5065 
  inside          :   (repeater port)  'inside'
  REPEATER        ::                    host  
                |    |                        
 . . . . . . . . .    . . . . . . . 
                  :^
           beacon ::  subscription     'loopback'
                  V:                    network
 . . . . . . . . .    . . . . . . . 
                |____| 
                  : 
  inside          : 
  CAXY          encaps                  'inside'
                  |                       host
 . . . . . . . . .(**) . . . . . . . 
  SSH (srvr)      | 
                 (  )
- - - - - - - - -   - - - - - - - - - - - - - - - - 
                  X 
                  X  secure TCP         Internet
                  X  Channel
- - - - - - - - -   - - - - - - - - - - - - - - - -
                 (  )
  SSH (clnt)      | 
 . . . . . . . . .(**) . . . . . . . 
                  | 
  outside       decaps                  'outside'
  CAXY            :                       host
                 _:__
                |    | UDP port
 . . . . . . . . .    . . . . . . . 
                  :
                BEACON                  'loopback'
                  :                      network
                  V    
 . . . . . . . . .   . . . . . . . .
                |___| UDP port 5065
  outside                               'outside'
  REPEATER       :::                       host   
 . . . . . . . .  ::: . . . . . . .   
                 VVV Fanned-out beacons
- - - - - - - - - - - - - - - - - - - - - - - - - 
             CA Clients

Command-line Arguments and Environment Variables

For the latest information, check the output of caxy's '-h' option!

caxy supports the following options/switches:

-h
Prints (the latest version of) this message.
-V
Prints release/version information.
-I
Run in 'inside' mode. If this switch is not given then run in 'outside' mode.
-f
Ignored for compatibility with C version. Note that -S always executes in the foreground (unlike C version).
-a 'ca_addr_list'
Like the EPICS_CA_ADDR_LIST env-var. Multiple -a options and the env-var are combined into a single list. Only effective in 'inside' mode.
-A <bool>
Control the use of an automatically computed address list (assembled from all broadcast addresses gathered from the computer's network interfaces). This option has the final say (other ways to control this is the EPICS_CA_AUTO_ADDR_LIST env-var or the 'auto_addr_list' java property. However, this flag has the final say. It can be set to 'true' or 'false' but any value but 'true' is interpreted as 'false' (= do NOT use the auto address list).
-J <string>
Prefix string when looking up 'JCA' java properties; (e.g., 'gov.aps.jca.jni.JNIContext' or 'com.cosylab.epics.caj.CAJContext'). Caxy attempts to be compatible with JCA/CAJ so that any JCA setup can directly be read by caxy. However, it is not possible to guess this prefix since it is normally built into JCA/CAJ. If you use the 'default' prefix 'gov.aps.jca.Context' when defining properties then you don't need this option because caxy already tries this prefix.
-d <flags>

Enable debugging messages. <flags> is a bit-set composed of

                   0x1: dump UDP messages
                   0x2: dump TCP messages
                   0x8: suppress beacon messages from debug output
0x10: trace lifecycle of threads
0x10000: dump raw tunneled message contents 0x20000: trace how java properties are looked up 0x40000: trace rehow CA addr list is assembled
-p <tcp_port>
Override TCP port to use for tunneling UDP. If this switch is not given then the value 0 is used which instructs caxy to use SSH's STDIO stream for tunneling UDP.

The advantage of this approach is that the vanilla STDIO of ssh can be used w/o the extra need of a forwarded connection and the need for an open TCP port on the 'inside' machine which may collide with other users'. The only disadvantage is that it is not trivial to nail two pipes together which connect caxy to the ssh client. This is especially true under windows.

Luckily, caxy now helps you with this task: see '--'

NOTES: If you use this switch (with a non-zero value) then it indicates the TCP port caxy should connect to for tunneling its wrapped UDP traffic. SSH must forward this port to the 'inside' caxy. This switch ONLY applies to the TCP, not the UDP port. The TCP port MUST match the ports forwarded by ssh.

You probably don't need to use this option.

-P

Accept connections from anywhere (only relevant on 'inside' and when not using STDIO for tunnel traffic; see -p, -S). By default only connections from the machine where caxy is running are accepted.

-S

'server-mode'. Like '-I' but manage/accept multiple connections from multiple 'outside' clients. The main program remains running in the foreground. In this mode multiple (unrelated) 'outside' clients can create ssh tunnels from anywhere and connect to one single, shared caxy server. The caxy server may be started -- e.g., by a startup script -- locally on an inside machine.

Useful also if the 'outside' client dies and is restarted; no restart of the 'inside' is necessary in this mode.
REQUIRES -p, i.e., a server cannot use STDIO for tunnel traffic.
-- <args>
Any extra arguments given to caxy are interpreted as a secondary or child command and its arguments with which caxy communicates over the stdin and stdout streams and which it expects to connect it to its 'inside' peer.

It is strongly recommended to separate such a command with the option terminator: '--' so that any options given to the child command are not parsed by caxy itself but passed on to the child process.

Typically you would launch an 'ssh' session to the 'inside' machine where you would execute an 'inside' version of caxy:

  java -jar caxy.jar -- ssh -D 1080 user@inside java -jar caxy.jar -I

would bring up a complete tunnel.

Note that the stderr stream of the child process is copied (by a dedicated thread) to stderr of the JVM on the 'outside' machine so that you can see any potential error messages. A child process is only created by caxy running in 'outside' mode (it uses java's System.exec() method).

ENVIRONMENT and Java Properties

NOTE: Environment variables are ONLY read if the system property 'jca.use_env' is set to 'true' or unset and if the JCA property 'use_env' ('JCA' properties use one of the JCA prefixes, see '-J' above) is either not set or set to 'true'.

If 'use_env' is determined to be 'false' then environment variables are ignored and JCA properties 'server_port', 'repeater_port', 'addr_list' and 'auto_addr_list' are looked up, respectively.

JCA properties have the prefix as given (in order of precedence) by '-J <prefix>', or the string 'gov.aps.jca.Context' as a fallback.

Properties are first looked up in the user properties file (path itself defined by JCA property 'gov.aps.jca.JCALibrary.properties' or if such a property is not found then '.JCALibrary/JCALibrary.properties' in the user's home directory is used). If no user-specific property is found then the system-wide ones (located in 'lib/JCALibrary.properties' in the 'java.home' directory) are consulted and finally, a set of built-in resources 'JCALibrary.properties'.

This scheme essentially follows what JCA is doing. The features provided by '-J' as well as system and JCA properties are aimed at easing interoperability with JCA.

EPICS_CA_SERVER_PORT (or property 'server_port'):
Port where to listen for UDP messages ('outside' mode). In 'inside' mode, the value of this variable defines the default port where UDP messages are sent (for addresses in the ca-address list that don't explicitly specify a port).
EPICS_CA_REPEATER_PORT (or property 'repeater_port'):
UDP port where caxy repeater subscriptions ('inside' mode) and beacons ('outside' mode), respectively, are sent to. The default value for the repeater port is 5065. NOTE: it is perfectly possible to use different settings for EPICS_CA_SERVER_PORT on the inside and outside.
EPICS_CA_ADDR_LIST (or property 'addr_list'):
In 'inside' mode: list of addresses where UDP messages are sent to. Note that this variable has no effect in 'outside' mode. The addresses in this list are appended to any '-a' options given on the command line.
EPICS_CA_AUTO_ADDR_LIST (or property 'auto_addr_list'):

In 'inside' mode: unless this is set to 'NO' a list of all broadcast addresses of the host's network interfaces is assembled and appended to the list built with -a options and EPICS_CA_ADDR_LIST.

This feature is only available under java 1.6 and higher.

The auto address list can also be forced on or off by means of the -A true / -A false command line option which takes precedence if present.

NOTE: The server and repeater port definitions may differ on the inside and outside. The tunnel handles this transparently but the settings must of course be correct. I.e., the values for caxy on the 'outside' must match the settings for CA clients and outside caRepeater and the values for caxy on the 'inside' must match the settings for CA servers and caRepeater on the 'inside'.

This feature is quite useful if the 'inside' CA network uses non-standard ports. It is only necessary to communicate these non-standard settings to caxy on the 'inside' (e.g, via a acript that sets the relevant environment variables or java property files). On the 'outside', caxy and the CA-clients do not have to replicate the non-standard settings but may just use the default ones.

Examples

Simple Example:

# Start up a tunnel using STDIO to communicate with the inside:

  java -jar caxy.jar -- ssh -D1080 user@insidehost java -jar caxy.jar -I

# Use PATCHED version of EPICS base;

  export EPICS_SOCKS_PROXY=localhost

  caget some_PV

  edm -x some_edm_screen

Example using non-patched EPICS base (with DANTE proxifier under linux)

# Start up the tunnel as in the example above

  java -jar caxy.jar -- ssh -D1080 user@insidehost java -jar caxy.jar -I

# from now on all programs are 'socksified'
  . socksify
  caget some_PV

# corresponding dante.conf routes (only 'from' lines):
# all tcp connections to a.b.c.0/24
      from: 0.0.0.0/0 to: a.b.c.0/24 via: 127.0.0.1 port = 1080
      ...

caxy TCP can also be tunneled explicitly via ssh:

# Assume: CA on a.b.c.255 subnet uses port 5066
# We use port 5056 for caxy's tunnel instead of STDIO
# (assume inside has a script 'caxy' which starts up caxy.jar)

  ssh -t -D 1080 -L 5056:localhost:5056 user@insidehost caxy -I -p5056 -a a.b.c.255:5066

# caxy on outside is not socksified but CA clients are:

  java -jar caxy.jar -p 5056 &

  . socksify
  caget some_PV

caxy TCP using a SOCKS tunnel

# Same as previous example but caxy's tunnel uses the SOCKS proxy, too

  ssh -t -D 1080 user@insidehost java -jar caxy.jar -I -p5056 -a a.b.c.255:5066

# caxy on outside does use SOCKS; CA clients are proxified

  java -DsocksProxyHost=localhost caxy.jar -p 5056 &
  . socksify
  caget some_PV

multihop tunnel to 'inside' via 'hop':

# Note that anyone on 'hop' can possibly use the SOCKS proxy to 'inside'
# If this is a problem then a more sophisticated, encrypted tunnel
# must be set up. Also (and in the above examples alike), anyone
# on 'outside' can use the proxy port 1080.

  java -jar caxy.jar -- \
                     ssh -L 1080:localhost:1080 hophost \
                     ssh -D 1080 insidehost \
                     java -jar caxy.jar -I -a a.b.c.255

# More importantly, this only works for ONE user on 'hop'. A second user
# would find 1080 already in use and would have to use a different 
# port on 'hop', e.g., 3333:

  java -jar caxy.jar -- \
                     ssh -L 1080:localhost:3333 hophost \
                     ssh -tt -D 3333 insidehost \
                     java -jar caxy.jar -I -a a.b.c.255

# use socksified CA client

  . socksify
  caget some_PV

# a better approach to set up a multihop tunnel is using ssh's
# 'ProxyCommand'. This avoids any intermediate port on the 
# gateway/'hop' machine. It is more convenient to set this
# up in the 'config' file.
# The example uses command line options just to illustrate
# the setup:

  java -jar caxy.jar -- \
                     ssh -D 1080 -o 'ProxyCommand=ssh hophost \
                     /usr/bin/nc %h %p' insidehost \
                     java -jar caxy.jar -I -a a.b.c.255

Windows

# Under windows, caxy works perfectly, e.g., in combination with the
# PUTTY ssh client.
# Especially for a multi-hop setup you would want to set up a configuration
# (e.g., 'inside') which you then call up from the command line (plink).

  java -jar caxy.jar -- plink inside java -jar caxy.jar -I

CAJ and CSS

It is possible to tunnel CA for client applications which use the 'CAJ' java implementation of the CA protocol such as the CSS suite.

The caxy tunnel must be started exactly in the same way as described above.

However, 'proxify-ing' the JVM may or may not work on different platforms. Therefore, we recommend a different approach:

Some network-related java classes already support connections via a SOCKS proxy. Hence, this built-in support can be leveraged. When setting the system property 'socksProxyHost' then all TCP connections established by 'Socket' objects are transparently going through the proxy.

Unfortunately, the 'SocketChannel' class which is used by CAJ (version 1.1.7) completely ignores this built-in support.

A patch to CAJ has been developed (and submitted to the CAJ maintainers, so it will hopefully be incorporated into future CAJ releases) which adds the necessary support for SOCKS. (Consult the 'ProxiedSocketChannel.java' file for more detail.) With this modified CAJ (and the tunnel set up as described in the previous sections) you could e.g., launch an application as follows:

java -DsocksProxyHost=localhost someCAJApp myPV

The patch to CAJ serves the same purpose (but targeting the java implementation of CA) as the 'base-<vers>-socks-proxy.diff' to the C/C++ version of EPICS base.

Eclipse/CSS

Eclipse also has built-in support for SOCKS proxies (you can define a proxy under Preferences-General-Network_Connections) but out of the box this does not interplay with (patched) CAJ or the java built-in SOCKS support in the Socket class.

Luckily it is just a matter of installing a small plugin to glue these pieces together. Hence, you need the following ingredients:

Now you are able to tunnel your CA connections:

It is worthwhile noting that the patched CAJ as well as eclipse with the 'org.eclipse.scout.net' plugin continue working normally when not told to use a proxy (e.g., when you plug your laptop into an 'inside' network). You only must remember to disable the proxy in the 'Network_Connection' settings and/or remove the 'socksProxyHost' system property.

CAS - Channel Access Security Issues

If CAS is enabled and configured on an IOC then you may be denied access to a PV. Note that some CA clients produce no warning or error, the PV just seems to be unreachable or unwriteable.

To understand the issue here it is necessary to know that CAS authentication is based on a 'user name' and a 'host name'. These names are retrieved on the machine where the CA client is running and transmitted to the IOC 'inside' the CA protocol. Hence, it is irrelevant from which machine the CA/TCP connection originates (the IOC could also check that but it doesn't) which in our case would be the 'inside' machine (which probably is 'allowed' by CAS rules).

An example: When you are logged into your laptop 'mymachine' as 'Bob' then these credentials ('Bob/mymachine') are transferred by the CA protocol and will be checked by the IOC against its CAS rules.

Therefore, you must convince the IOC administrator(s) to add your name/machine to these rules.

CATUN Protocol Versions

'CATUN' is the (trivial) protocol caxy uses to wrap UDP datagrams for transmission over a stream. The first version of this tunnel protocol wrapped each individual CA message before shipping it through the stream/TCP connection. On the receiving end of the serialized stream, each CA message would be unwrapped and sent out in a UDP datagram. This means that even though the CA client might have packed multiple CA messages into a single UDP datagram, once this datagram was shipped through the tunnel (using the CATUN V1 protocol) it was broken up into multiple packets, each one holding only a single CA message.

CATUN protocol V2 remedies this and wraps an entire UDP datagram so that the original packet structure is preserved.

Note that the java version of caxy ONLY support protocol version 2. Hence it is not possible to co-operate with older versions of the C implementation.

CATUN protocol V3 defines a small albeit not backwards compatible addition to the initial handshake procedure which enables the outside to detect that the the tunnel has been established.

Building/Hacking CAXY

If you want to mess around with the source code or simply rebuild caxy (e.g., for running under java-1.5) then you need

Change directory to where 'build.xml' resides and issue 'ant'

The following targets are defined

clean
remove everyting generated by 'dist' or 'srcidst'
dist
build .class files and .jar file holding all .classes ('binary' distribution)
srcdist
build 'binary' distribution and package everything (including sources) into a .jar file