Reflector.java Source code

Java tutorial

Introduction

Here is the source code for Reflector.java

Source

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;

public class Reflector implements Runnable {

    // valid names found in the config file:
    public static final String MODE = "Mode";

    public static final String SOURCE_IP = "SourceIP";

    public static final String SOURCE_PORT = "SourcePort";

    public static final String DEST_IP = "DestIP";

    public static final String DEST_PORT = "DestPort";

    // valid modes in the config file, unicast to
    // multicast or multicast to unicast
    public static final String INPUT_UNITOMULTI = "UNI_TO_MULTI";

    public static final String INPUT_MULTITOUNI = "MULTI_TO_UNI";

    // possible modes the reflector can be set to:
    public static final int MODE_NONE = 0;

    public static final int MODE_UNI_TO_MULTI = 1;

    public static final int MODE_MULTI_TO_UNI = 2;

    // variables to indicate source or destination
    public static final int SOURCE = 1;

    public static final int DEST = 2;

    // min and max network ports allowed
    public static final int MIN_PORT = 1024;

    public static final int MAX_PORT = 65095;

    // which mode the reflector is being run in:
    private int mode = 0;

    // source and destination hold variables:
    private Address source;

    private Hashtable dest;

    private Address hold_dest = null;

    // logging toggle and logger class
    boolean logging = true;

    Logger logger;

    public Reflector() {
    }

    public void run() {
        // validate the config file
        if (readConfig() != 0) {
            System.err.println("Error parsing config file\n");
            System.exit(-1);
        }

        // start the logger
        logger = new Logger(logging);

        // spawn a thread to listen for packets
        ReflectorListener listener = new ReflectorListener(source, mode, logger);
        System.out.println("Listening on " + source.toString());

        // spawn threads for each source address packets
        // are to be forwarded on. Register each thread as
        // PacketListenerInterface with the listener thread.
        System.out.println("Sending on:");
        for (Enumeration e = dest.elements(); e.hasMoreElements();) {
            Address a = (Address) e.nextElement();
            ReflectorSender sender = new ReflectorSender(a, mode, logger);
            sender.start();
            listener.addPacketListener((PacketListenerInterface) sender);
            System.out.println("           " + a.toString());
        }

        // start the listener
        listener.start();
    }

    public int readConfig() {
        // validate the contents of the config file

        BufferedReader input = null;
        String name, value, inputLine = null;

        dest = new Hashtable();

        // open and read the config file
        try {
            input = new BufferedReader(new FileReader("reflector.conf"));
            inputLine = input.readLine();
        } catch (IOException e) {
            System.err.println("Error reading reflector.conf.");
            return (-1);
        }

        // loop until entire config file is read
        while (inputLine != null) {

            // skip comments:
            if (inputLine.charAt(0) != '#') {

                // extract a name/value pair, and branch
                // based on the name:

                StringTokenizer tokenizer = new StringTokenizer(inputLine, "=");
                name = tokenizer.nextToken();
                value = tokenizer.nextToken();

                if (name == null) {
                    System.out.println("no name");
                    continue;
                } else if (name.equals(MODE)) {
                    if (setMode(value) != 0) {
                        System.err.println("Error setting mode to " + value);
                        return (-1);
                    }
                } else if (name.equals(SOURCE_IP)) {
                    if (setSourceIP(value) != 0) {
                        System.err.println("Error setting src IP address to " + value);
                        return (-1);
                    }
                } else if (name.equals(SOURCE_PORT)) {
                    if (setSourcePort(value) != 0) {
                        System.err.println("Error setting src port to " + value);
                        return (-1);
                    }
                } else if (name.equals(DEST_IP)) {
                    if (setDestIP(value) != 0) {
                        System.err.println("Error setting dest IP address to " + value);
                        return (-1);
                    }
                } else if (name.equals(DEST_PORT)) {
                    if (setDestPort(value) != 0) {
                        System.err.println("Error setting dest port to " + value);
                        return (-1);
                    }
                } else {
                    System.err.println("Skipping invalid config file value: " + name);
                }
            }
            // read next line in the config file
            try {
                inputLine = input.readLine();
            } catch (IOException e) {
                System.err.println("Error reading reflector.conf.");
                return (-1);
            }
        }

        // close the config file
        try {
            input.close();
        } catch (IOException e) {
            System.err.println("Error closing reflector.conf.");
            return (-1);
        }

        // validate that the combined contents of the config file
        // make sense
        if (!isConfigValid()) {
            System.err.println("Configuration file is not complete.");
            return (-1);
        }
        return (0);
    }

    private int setMode(String value) {
        // validate and set the mode from the config file
        if (value.equals(INPUT_UNITOMULTI)) {
            mode = MODE_UNI_TO_MULTI;
            return (0);
        } else if (value.equals(INPUT_MULTITOUNI)) {
            mode = MODE_MULTI_TO_UNI;
            return (0);
        } else {
            return (-1);
        }
    }

    private int setSourceIP(String value) {
        // validate and set the source IP from the config file

        // call modeToAddress to validate IP address
        InetAddress inet = modeToAddress(value, SOURCE);
        if (inet == null)
            return -1;

        if (source != null) {
            if (source.getAddress() != null)
                System.err.println("Warning: overwriting src address " + source.getAddress().getHostAddress()
                        + " with " + inet.getHostAddress() + ".");
            source.setAddress(inet);
        } else {
            source = new Address(inet);
        }

        return (0);
    }

    private int setSourcePort(String value) {
        // validate and set the source port from the config file

        int port;

        try {
            port = Integer.parseInt(value);
        } catch (NumberFormatException nfe) {
            return (-1);
        }

        if ((port < MIN_PORT) || (port > 65095))
            return (-1);

        if (source != null) {
            if (source.getPort() != 0)
                System.err
                        .println("Warning: overwriting src port " + source.getPort() + " with port " + port + ".");
            source.setPort(port);
        } else {
            source = new Address(port);
        }

        return (0);
    }

    private int setDestIP(String value) {
        // validate and set the dest IP from the config file

        // call modeToAddress to validate IP address
        InetAddress inet = modeToAddress(value, DEST);
        if (inet == null)
            return -1;

        if (hold_dest != null) {
            if (hold_dest.getAddress() != null)
                System.err.println("Warning: overwriting dest address " + hold_dest.getAddress().getHostAddress()
                        + " with " + inet.getHostAddress() + ".");
            hold_dest.setAddress(inet);
            if (hold_dest.isComplete())
                return (addDest());
        } else {
            hold_dest = new Address(inet);
        }
        return (0);
    }

    private int setDestPort(String value) {
        // validate and set the dest port from the config file

        int port;

        try {
            port = Integer.parseInt(value);
        } catch (NumberFormatException nfe) {
            return (-1);
        }

        if ((port < MIN_PORT) || (port > MAX_PORT))
            return (-1);

        if (hold_dest != null) {
            if (hold_dest.getPort() != 0)
                System.err.println(
                        "Warning: overwriting dest port " + hold_dest.getPort() + " with port " + port + ".");
            hold_dest.setPort(port);
            if (hold_dest.isComplete())
                return (addDest());
        } else {
            hold_dest = new Address(port);
        }

        return (0);
    }

    private int addDest() {
        // once both a dest IP and port have been read, add them
        // to our vector of all destinations.

        switch (mode) {
        case MODE_UNI_TO_MULTI:
            if (!dest.isEmpty()) {
                System.err.println("Warning: dest address overwritten");
                dest.clear();
            }
            dest.put(hold_dest.toString(), hold_dest);
            break;
        case MODE_MULTI_TO_UNI:
            dest.put(hold_dest.toString(), hold_dest);
            break;
        default:
            // no mode set
            System.err.println("Destination " + hold_dest.toString() + " skipped because no mode set.");
            hold_dest = null;
            return (-1);
        }
        hold_dest = null;
        return (0);
    }

    private InetAddress modeToAddress(String value, int type) {
        // validate the IP Address based on its text value, its
        // type (DEST or SOURCE), and the mode (UNI_TO_MULTI or
        // MULTI_TO_UNI). Returns an InetAddress if succesfull and
        // null on failure.

        InetAddress inet;

        if ((type != DEST) && (type != SOURCE)) {
            System.err.println("Invalid type passed to modeToAddress (" + type + ")");
            return (null);
        }

        switch (mode) {
        case MODE_UNI_TO_MULTI:
            if (type == DEST)
                inet = returnValidMCIP(value);
            else
                inet = returnValidIP(value);
            break;
        case MODE_MULTI_TO_UNI:
            if (type == DEST)
                inet = returnValidIP(value);
            else
                inet = returnValidMCIP(value);
            break;
        default:
            // no mode set
            System.err.println("Error: No Mode Selected.");
            return (null);
        }

        if (inet == null)
            System.err.println("Invalid dest IP address (" + value + ").");

        return (inet);
    }

    private InetAddress returnValidIP(String IP) {
        // return InetAddress if IP is valid, null otherwise

        InetAddress inet;
        try {
            inet = InetAddress.getByName(IP);
        } catch (UnknownHostException e) {
            return (null);
        }
        return (inet);
    }

    private InetAddress returnValidMCIP(String IP) {
        // return InetAddress if IP is valid multicast addr,
        // null otherwise

        InetAddress inet = returnValidIP(IP);
        if (inet.isMulticastAddress()) {
            return (inet);
        } else {
            return (null);
        }
    }

    public boolean isConfigValid() {
        // validate that the mode, source IP/port, and
        // dest IP(s)/port(s) are all valid and a valid
        // combination.

        if (mode == MODE_NONE) {
            System.err.println("No mode selected.");
            return (false);
        }
        if (!source.isComplete()) {
            if ((source.getPort() != 0) && (mode == MODE_UNI_TO_MULTI)) {
                // if source is unicast local IP is implied
                try {
                    source.setAddress(InetAddress.getLocalHost());
                } catch (UnknownHostException e) {
                    System.err.println("Incomplete source address.");
                    return (false);
                }
            } else {
                System.err.println("Incomplete source address.");
                return (false);
            }
        }
        if (dest.isEmpty()) {
            System.err.println("No destination addresses.");
            return (false);
        }
        for (Enumeration e = dest.elements(); e.hasMoreElements();) {
            Address a = (Address) e.nextElement();
            if (!a.isComplete()) {
                System.err.println("Incompete destination address.");
                return (false);
            }
        }
        return (true);
    }

    public static void main(String args[]) {
        Reflector r = new Reflector();
        r.run();
    }
}

//Address class is used to store an IP address and port
//combination.

class Address {

    private InetAddress address = null;

    private int port = 0;

    public Address(InetAddress address, int port) {
        this.address = address;
        this.port = port;
    }

    public Address(InetAddress address) {
        this.address = address;
    }

    public Address(int port) {
        this.port = port;
    }

    public InetAddress getAddress() {
        return (address);
    }

    public int getPort() {
        return (port);
    }

    public void setPort(int port) {
        this.port = port;
    }

    public void setAddress(InetAddress address) {
        this.address = address;
    }

    public boolean isComplete() {
        // return true if both IP and port are populated,
        // false otherwise.

        if ((address != null) && (port != 0))
            return (true);
        else
            return (false);
    }

    public String toString() {
        // return a string representation of the IP/port.

        String str;

        if (address == null)
            str = "";
        else
            str = address.getHostAddress();

        str = str + "/" + port;

        return (str);
    }
}

//Logger class opens and writes to the log file
//if boolean true is passed as constructor argument.

class Logger {

    private boolean logging;

    private FileWriter logfile;

    public Logger(boolean logging) {
        this.logging = logging;

        if (logging) {
            try {
                // open logfile for append
                logfile = new FileWriter("reflector.log", true);
            } catch (IOException e) {
                System.err.println("Error opening log file.");
            }
            log("Reflector started: " + new Date());
        }
    }

    public void log(String str) {
        // write string to logfile

        // if logging disabled return
        if (!logging)
            return;

        try {
            logfile.write(str + "\n");
            logfile.flush();
        } catch (IOException e) {
            System.err.println("Error writing to log file.");
        }
    }
}

//ReflectorSender creates a unicast or multicast socket
//and is registered to receive incoming packet notifications
//via the PacketListenerInterface. Incoming packets
//are forwarded on the outgoing socket.

class ReflectorSender extends Thread implements PacketListenerInterface {
    private InetAddress sendAddr;

    private int sendPort;

    private int mode;

    private DatagramSocket ds = null;

    private Logger logger;

    public ReflectorSender(Address a, int mode, Logger logger) {
        sendAddr = a.getAddress();
        sendPort = a.getPort();
        this.mode = mode;
        this.logger = logger;
    }

    public void run() {
        // initialize a DatagramSocket or MulticastSocket
        // based on the mode:

        switch (mode) {
        case Reflector.MODE_MULTI_TO_UNI:
            ds = initUnicastSocket();
            break;
        case Reflector.MODE_UNI_TO_MULTI:
            ds = (DatagramSocket) initMulticastSocket();
            break;
        default:
            break;
        }
    }

    private MulticastSocket initMulticastSocket() {
        // initialize a MulticastSocket

        MulticastSocket mc;
        try {
            mc = new MulticastSocket(sendPort);
        } catch (Exception e) {
            e.printStackTrace();
            return (null);
        }
        return (mc);
    }

    private DatagramSocket initUnicastSocket() {
        // initialize a DatagramSocket

        DatagramSocket ds;
        try {
            ds = new DatagramSocket(sendPort);
        } catch (Exception e) {
            e.printStackTrace();
            return (null);
        }
        return (ds);
    }

    public void packetReceived(DatagramPacket packet) {
        // An incoming packet has been received. Override
        // the old packet addressing to the new outgoing
        // addressing, send it and log it.

        try {
            packet.setAddress(sendAddr);
            packet.setPort(sendPort);
            ds.send(packet);
            logger.log("Packet forwarded to " + packet.getAddress().getHostAddress() + "/" + packet.getPort() + ", "
                    + packet.getLength() + " bytes");
        } catch (IOException e) {
            System.err.println("Error sending packet");
            e.printStackTrace();
        }
    }
}

//ReflectorListener thread listens for packets
//and notifies one or more interested threads
//who register as PacketListenerInterfaces.

class ReflectorListener extends Thread {
    private InetAddress listenAddr;

    private int listenPort;

    private int mode;

    private Vector packetListeners;

    private Logger logger;

    private static final int MAX_PACKET_SIZE = 1500;

    public ReflectorListener(Address a, int mode, Logger logger) {
        listenAddr = a.getAddress();
        listenPort = a.getPort();
        this.mode = mode;
        this.logger = logger;

        packetListeners = new Vector();
    }

    public void run() {
        // create a unicast or multicast socket
        // depending on the mode and listen for packets.

        switch (mode) {
        case Reflector.MODE_UNI_TO_MULTI:
            DatagramSocket ds = initUnicastSocket();
            if (ds != null)
                listen(ds);
            break;
        case Reflector.MODE_MULTI_TO_UNI:
            MulticastSocket mc = initMulticastSocket();
            if (mc != null)
                listen((DatagramSocket) mc);
            break;
        default:
            break;
        }
    }

    private MulticastSocket initMulticastSocket() {
        // initialize a MulticastSocket and join the group

        MulticastSocket mc;
        try {
            mc = new MulticastSocket(listenPort);
            mc.joinGroup(listenAddr);
        } catch (Exception e) {
            System.err.println("Failed to create MulticastSocket on " + "port " + listenPort);
            return (null);
        }
        return (mc);
    }

    private DatagramSocket initUnicastSocket() {
        // initialize a DatagramSocket

        DatagramSocket ds;
        try {
            ds = new DatagramSocket(listenPort);
        } catch (Exception e) {
            System.err.println("Failed to create DatagramSocket on " + "port " + listenPort);
            return (null);
        }
        return (ds);
    }

    private void listen(DatagramSocket ds) {
        // loop forever listening to packets, when they
        // arrive log them and notify all interested threads.

        byte[] buffer;
        DatagramPacket packet;

        while (true) {
            try {
                buffer = new byte[MAX_PACKET_SIZE];
                packet = new DatagramPacket(buffer, buffer.length);

                ds.receive(packet);

                logger.log("Packet received, " + packet.getLength() + " bytes");
                eventNotify(packet);

            } catch (IOException e) {
                System.err.println("Error receiving packet\n");
                e.printStackTrace();
            }
        }
    }

    public void addPacketListener(PacketListenerInterface pl) {
        // add interested thread to listeners vector
        packetListeners.addElement(pl);
    }

    public void removePacketListener(PacketListenerInterface pl) {
        // remove thread to listeners vector
        packetListeners.removeElement(pl);
    }

    private void eventNotify(DatagramPacket packet) {
        // notify all registered threads that a packet has arrived
        // using the packetReceived(DatagramPacket) method.

        for (Enumeration e = packetListeners.elements(); e.hasMoreElements();) {

            PacketListenerInterface pl = (PacketListenerInterface) e.nextElement();
            pl.packetReceived(packet);
        }
    }
}

//PacketListenerInterface used by threads that need to
//be notified of datagram packet receipt. A single
//interface function packetReceived passes the packet
//information to the thread requiring the information.

interface PacketListenerInterface {
    public void packetReceived(DatagramPacket packet);
}