Java tutorial
/* * ManagementClient.java * Copyright (C) Apr 5, 2014 Wannes De Smet * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package be.neutrinet.ispng.openvpn; import be.neutrinet.ispng.config.Config; import be.neutrinet.ispng.vpn.Manager; import com.google.common.collect.LinkedListMultimap; import org.apache.log4j.Logger; import java.io.*; import java.net.Socket; import java.util.HashMap; import java.util.Map; /** * @author wannes */ public class ManagementInterface implements Runnable { private final Watchdog watchdog = new Watchdog(); private final ServiceListener listener; protected boolean echoOpenVPNCommands = false; protected String host; protected int port; protected String instanceId; private Thread thread; private Socket sock; private BufferedReader br; private BufferedWriter bw; private String line; private boolean run; public ManagementInterface(ServiceListener listener, String host, int port) { this.listener = listener; this.host = host; this.port = port; this.instanceId = host + ":" + port; } public String getInstanceId() { return instanceId; } public void connect() throws IOException { listener.setManagementInterface(this); // dispose of possible old socket if (sock != null && !sock.isConnected()) sock.close(); // check if a connection already exists if (sock != null && !sock.isClosed()) return; sock = new Socket(host, port); } public void connected() { listener.managementConnectionEstablished(); Config.get().getAndWatch("debug/OpenVPN/echoCommands", "false", (String value) -> echoOpenVPNCommands = value.equals("true")); } public Watchdog getWatchdog() { return watchdog; } public void authorizeClient(int id, int kid) { String authorize = "client-auth-nt " + id + ' ' + kid + '\n'; try { writeLine(authorize); } catch (IOException ex) { Logger.getLogger(getClass()).error("Failed to write command", ex); } } public void authorizeClient(int id, int kid, LinkedListMultimap<String, String> options) { String authorize = "client-auth " + id + ' ' + kid + '\n'; try { writeLine(authorize); for (Map.Entry<String, String> entry : options.entries()) { writeLine(entry.getKey() + (entry.getValue() != null ? " " + entry.getValue() : "") + '\n'); } writeLine("END\n"); } catch (IOException ex) { Logger.getLogger(getClass()).error("Failed to write command", ex); } } public void denyClient(int id, int kid, String reason) { String cmd = String.format("client-deny %s %s DENIED \"%s\"\n", id, kid, reason); try { writeLine(cmd); } catch (IOException ex) { Logger.getLogger(getClass()).error("Failed to write command", ex); } } public void killClient(int id) { String cmd = String.format("client-kill %s \n", id); try { writeLine(cmd); } catch (IOException ex) { Logger.getLogger(getClass()).error("Failed to write command", ex); } } protected Client buildClient(String[] args) throws IOException { boolean reachedEnd = false; HashMap<String, String> clientEnv = new HashMap<>(); while (!reachedEnd) { line = br.readLine(); if (line.equals(">CLIENT:ENV,END")) { reachedEnd = true; Client client = ObjectMarshall.fuzzyBuild(clientEnv, Client.class); client.id = Integer.parseInt(args[0]); if (args.length > 1) { client.kid = Integer.parseInt(args[1]); } return client; } String key = line.substring(line.indexOf(',') + 1, line.indexOf('=')); String value = line.substring(line.indexOf('=') + 1); clientEnv.put(key, value); } return null; } public void setBandwidthMonitoringInterval(int interval) { try { if (interval < 0) throw new IllegalArgumentException("interval cannot be lower than zero"); writeLine("bytecount " + interval + "\n"); } catch (IOException ex) { Logger.getLogger(getClass()).error("Failed to write command", ex); } } private synchronized void writeLine(String line) throws IOException { if (echoOpenVPNCommands) Logger.getLogger(getClass()).debug("Sent OpenVPN command '" + line + "'"); System.out.print(line); bw.write(line); bw.flush(); } @Override public void run() { main: while (run) { try { connect(); this.br = new BufferedReader(new InputStreamReader(sock.getInputStream())); this.bw = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream())); connected(); line = br.readLine(); commandHandling: while (run) { if (line == null) { // Abort and recover break; } if (line.startsWith(">")) { if (echoOpenVPNCommands) Logger.getLogger(getClass()).debug("OpenVPN command: " + line); int cmdSep = line.indexOf(','); String cmd, subCmd = ""; String[] args; if (cmdSep != -1) { cmd = line.substring(1, cmdSep); args = line.substring(cmdSep + 1).split(","); int subCmdSep = cmd.indexOf(':'); if (subCmdSep != -1) { subCmd = cmd.substring(subCmdSep + 1); cmd = cmd.substring(0, subCmdSep); } } else { cmdSep = line.indexOf(':'); cmd = line.substring(1, cmdSep); args = new String[] { line.substring(cmdSep + 1) }; } switch (cmd) { case "CLIENT": switch (subCmd) { case "CONNECT": Client client = buildClient(args); listener.clientConnect(client); break; case "ESTABLISHED": client = buildClient(args); listener.connectionEstablished(client); break; case "DISCONNECT": client = buildClient(args); listener.clientDisconnect(client); break; case "ADDRESS": client = new Client(); client.id = Integer.parseInt(args[0]); listener.addressInUse(client, args[1], args[2].equals("1")); break; case "REAUTH": client = buildClient(args); listener.clientReAuth(client); break; } break; case "BYTECOUNT_CLI": Client client = new Client(); client.id = Integer.parseInt(subCmd); listener.bytecount(client, Long.parseLong(args[0]), Long.parseLong(args[1])); break; case "INFO": Logger.getLogger(getClass()).info("OpenVPN: " + line); break; default: Logger.getLogger(getClass()).warn("Unhandled OpenVPN command " + cmd); break; } } line = br.readLine(); } sock.close(); } catch (Exception ex) { Logger.getLogger(getClass()).error("Management client failure", ex); break; } } } public final class Watchdog extends Thread { @Override public void run() { run = true; try { while (run) { // Threads cannot be restarted, create a new instance each time thread = new Thread(ManagementInterface.this, "ManagementClient-" + host + ":" + port); thread.start(); thread.join(); if (run) { Logger.getLogger(getClass()).warn("Recovering from management interface failure"); // wait one second 'til recover attempt Thread.sleep(1000); } } } catch (Exception ex) { Logger.getLogger(getClass()).error("Recovery failure", ex); Manager.get().shutItDown("Could not recover from mgmt client failure"); run = false; } } } }