Network Protocol Java

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);
}