package de.bwlehrpool.bwlp_guac; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.*; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.guacamole.form.Field; import org.apache.guacamole.net.auth.credentials.CredentialsInfo; import org.apache.guacamole.net.auth.credentials.GuacamoleCredentialsException; import org.apache.guacamole.net.auth.credentials.GuacamoleInsufficientCredentialsException; import org.codehaus.jackson.map.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Broker for all clients available. * Keeps track of which clients are online, offline, assigned to a user etc. */ public class ConnectionManager { private static final Logger LOGGER = LoggerFactory.getLogger(ConnectionManager.class); // TODO: Config private static final String SOURCE_URL = SlxConfig.clientListUrl(); private static final LinkedHashMap clientPool = new LinkedHashMap(); private static final LinkedHashMap locationPool = new LinkedHashMap(); public static LinkedHashMap getLocationPool() { return locationPool; } /** * Pass plain user name, get existing connection (if any), or a fresh one * @param user (LDAP) user name */ public static WrappedConnection getForUser(String user, int locationid) throws GuacamoleCredentialsException { if (SOURCE_URL == null) { LOGGER.warn("Don't have a source URL for client machines!"); return null; } try { updateList(); // Find existing/free client AvailableClient freeClient; for (; ; ) { freeClient = null; synchronized (clientPool) { Collection clients; if (locationid == 0) { clients = new HashSet(); for (JsonLocation loc : locationPool.values()) { if (!loc.hasPassword()) clients.addAll(loc.clientList); } } else { JsonLocation location = locationPool.get(locationid); if (location == null) { // Request the user to select a location throw new GuacamoleInsufficientCredentialsException( "Select Location", new CredentialsInfo( Collections.singletonList(new LocationField()) )); } clients = location.clientList; } for (AvailableClient ac : clients) { if (ac.isInUseBy(user)) { freeClient = ac; break; } } if (freeClient == null) { for (AvailableClient ac : clients) { if (ac.claim(user)) { freeClient = ac; break; } } } } if (freeClient == null) { // Request the user to select another location throw new GuacamoleCredentialsException( "No free client. Select another location.", new CredentialsInfo( Collections.singletonList(new LocationField()) )); } // Found free or existing client, check if connection is (still) possible if (freeClient.checkConnection(0)) { LOGGER.info("Establishing mapping for user " + user + " to " + freeClient); return freeClient.getConnection(user); } // Connection check failed - release and loop again freeClient.releaseConnection(user); } } catch (GuacamoleCredentialsException e) { throw e; } catch (Exception e) { LOGGER.warn("KACKE AM DAMPFEN", e); return null; } } private static long lastUpdate = 0; /** * Fetch fresh client list from satellite server. * Cache for 15 seconds. */ public static synchronized void updateList() { long now = System.currentTimeMillis(); if (now < lastUpdate) { lastUpdate = now; } if (now - lastUpdate < 15000) return; // OK GO lastUpdate = now; ByteArrayOutputStream baos = new ByteArrayOutputStream(3000); HttpURLConnection con; try { con = (HttpURLConnection) new URL(SOURCE_URL).openConnection(); } catch (MalformedURLException e1) { LOGGER.warn("Bad Connection Pool URL", e1); return; } catch (IOException e1) { LOGGER.warn("Cannot connect to Connection Pool URL", e1); return; } if (con instanceof HttpsURLConnection) { ((HttpsURLConnection) con).setHostnameVerifier(ignorer); ((HttpsURLConnection) con).setSSLSocketFactory(sockFac); } try (BufferedInputStream in = new BufferedInputStream(con.getInputStream())) { byte dataBuffer[] = new byte[2048]; int bytesRead; while ((bytesRead = in.read(dataBuffer, 0, dataBuffer.length)) != -1) { baos.write(dataBuffer, 0, bytesRead); } } catch (IOException e) { LOGGER.warn("Error while reading reply of Connection Pool", e); return; } populateList(baos.toByteArray()); } private static void populateList(byte[] data) { ObjectMapper mapper = new ObjectMapper(); JsonClient[] list; JsonRoot root; try { root = mapper.readValue(data, JsonRoot.class); } catch (Exception e) { LOGGER.warn("Could not deserialize JSON from Connection Pool", e); LOGGER.warn("Not updating local list"); return; } if (root.locations == null) { LOGGER.info("Location list null"); } synchronized (locationPool) { for (JsonLocation lnew : root.locations) { JsonLocation existing = locationPool.get(lnew.id); boolean redoClientMapping = false; if (existing == null) { locationPool.put(lnew.id, lnew); existing = lnew; redoClientMapping = true; } else { if (existing.locationids != lnew.locationids) redoClientMapping = true; existing.locationids = lnew.locationids; existing.name = lnew.name; existing.password = lnew.password; } if (redoClientMapping) { existing.clientList = new ArrayList(); for (AvailableClient client : clientPool.values()) { int locationid = client.getLocationid(); for (int id : existing.locationids) { if (id == locationid) { existing.clientList.add(client); break; } } } } existing.checked = true; LOGGER.info("Location " + lnew.name + " with pw " + lnew.password); } for (JsonLocation loc : locationPool.values()) { if (loc.checked) loc.checked = false; else locationPool.remove(loc.id); } } if (root.clients == null) { LOGGER.info("Client list null"); } synchronized (clientPool) { for (JsonClient cnew : root.clients) { if (cnew.password == null || cnew.clientip == null) continue; // Invalid AvailableClient existing = clientPool.get(cnew.clientip); if (existing == null) { // New client AvailableClient newClient = new AvailableClient(cnew); clientPool.put(cnew.clientip, newClient); for (JsonLocation loc : locationPool.values()) { for (int id : loc.locationids) { if (id == cnew.locationid) { loc.clientList.add(newClient); newClient.locationList.add(loc); break; } } } LOGGER.info("New client " + cnew.clientip); } else { existing.update(cnew); } } final long NOW = System.currentTimeMillis(); for (Iterator it = clientPool.values().iterator(); it.hasNext();) { AvailableClient c = it.next(); if (c.isTimeout(NOW)) { LOGGER.info("Removing client " + c + " from list"); for (JsonLocation loc : c.locationList) { loc.clientList.remove(c); } it.remove(); } } LOGGER.info("List updated. " + clientPool.size() + " clients."); } } /* * Make SSL insecure */ static { SSLContext ctx = null; try { ctx = SSLContext.getInstance("TLSv1.2"); } catch (NoSuchAlgorithmException e) { LOGGER.warn("Could not get TLSv1.2 context, SSL will be secure :-(", e); } if (ctx != null) { try { ctx.init(null, new TrustManager[] { new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } } }, null); } catch (KeyManagementException e) { LOGGER.warn("Could not initialize TLSv1.2 SSL context", e); ctx = null; } } if (ctx == null) { sockFac = null; } else { sockFac = ctx.getSocketFactory(); } } private static final SSLSocketFactory sockFac; private static final HostnameVerifier ignorer = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }; }