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.Iterator; import java.util.LinkedHashMap; 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.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(); /** * Pass plain user name, get existing connection (if any), or a fresh one * @param user (LDAP) user name */ public static WrappedConnection getForUser(String user) { 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) { for (AvailableClient ac : clientPool.values()) { if (ac.isInUseBy(user)) { freeClient = ac; break; } } if (freeClient == null) { for (AvailableClient ac : clientPool.values()) { if (ac.claim(user)) { freeClient = ac; break; } } } } if (freeClient == null) return null; // TODO: No more clients available -- how to handle? // 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 (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. */ private 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; } list = root.clients; if (list == null) { LOGGER.info("Client list null"); } if (root.locations != null) { for (JsonLocation l : root.locations) { LOGGER.info("Location " + l.name + " with pw " + l.password); } } synchronized (clientPool) { for (JsonClient cnew : list) { if (cnew.password == null || cnew.clientip == null) continue; // Invalid AvailableClient existing = clientPool.get(cnew.clientip); if (existing == null) { // New client clientPool.put(cnew.clientip, new AvailableClient(cnew)); 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"); 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; } }; }