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