/* Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. The MySQL Connector/J is licensed under the terms of the GPLv2 , like most MySQL Connectors. There are special exceptions to the terms and conditions of the GPLv2 as it is applied to this software, see the FOSS License Exception . 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; version 2 of the License. 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, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package com.mysql.jdbc; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnknownHostException; import java.sql.ResultSet; import java.sql.SQLException; public interface SocketMetadata { public boolean isLocallyConnected(ConnectionImpl conn) throws SQLException; /* * Provides a standard way of determining whether a socket connection is local. * * This ensures socket factories (e.g. StandardSocketFactory, StandardSSLSocketFactory) which need to implement this interface, can delegate to a generic * implementation. */ static class Helper { public static final String IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME = "com.mysql.jdbc.test.isLocalHostnameReplacement"; public static boolean isLocallyConnected(com.mysql.jdbc.ConnectionImpl conn) throws SQLException { long threadId = conn.getId(); java.sql.Statement processListStmt = conn.getMetadataSafeStatement(); ResultSet rs = null; String processHost = null; // "inject" for tests if (System.getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME) != null) { processHost = System.getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME); } else if (conn.getProperties().getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME) != null) { processHost = conn.getProperties().getProperty(IS_LOCAL_HOSTNAME_REPLACEMENT_PROPERTY_NAME); } else { // get it from server try { processHost = findProcessHost(threadId, processListStmt); if (processHost == null) { // http://bugs.mysql.com/bug.php?id=44167 - connection ids on the wire wrap at 4 bytes even though they're 64-bit numbers conn.getLog() .logWarn(String.format( "Connection id %d not found in \"SHOW PROCESSLIST\", assuming 32-bit overflow, using SELECT CONNECTION_ID() instead", threadId)); rs = processListStmt.executeQuery("SELECT CONNECTION_ID()"); if (rs.next()) { threadId = rs.getLong(1); processHost = findProcessHost(threadId, processListStmt); } else { conn.getLog().logError( "No rows returned for statement \"SELECT CONNECTION_ID()\", local connection check will most likely be incorrect"); } } } finally { processListStmt.close(); } } if (processHost != null) { conn.getLog().logDebug(String.format("Using 'host' value of '%s' to determine locality of connection", processHost)); int endIndex = processHost.lastIndexOf(":"); if (endIndex != -1) { processHost = processHost.substring(0, endIndex); try { boolean isLocal = false; InetAddress[] allHostAddr = InetAddress.getAllByName(processHost); // mysqlConnection should be the raw socket SocketAddress remoteSocketAddr = conn.getIO().mysqlConnection.getRemoteSocketAddress(); if (remoteSocketAddr instanceof InetSocketAddress) { InetAddress whereIConnectedTo = ((InetSocketAddress) remoteSocketAddr).getAddress(); for (InetAddress hostAddr : allHostAddr) { if (hostAddr.equals(whereIConnectedTo)) { conn.getLog().logDebug( String.format("Locally connected - HostAddress(%s).equals(whereIconnectedTo({%s})", hostAddr, whereIConnectedTo)); isLocal = true; break; } conn.getLog() .logDebug(String.format("Attempted locally connected check failed - ! HostAddress(%s).equals(whereIconnectedTo(%s)", hostAddr, whereIConnectedTo)); } } else { String msg = String.format("Remote socket address %s is not an inet socket address", remoteSocketAddr); conn.getLog().logDebug(msg); } return isLocal; } catch (UnknownHostException e) { conn.getLog().logWarn(Messages.getString("Connection.CantDetectLocalConnect", new Object[] { processHost }), e); return false; } } conn.getLog().logWarn(String .format("No port number present in 'host' from SHOW PROCESSLIST '%s', unable to determine whether locally connected", processHost)); return false; } conn.getLog().logWarn(String .format("Cannot find process listing for connection %d in SHOW PROCESSLIST output, unable to determine if locally connected", threadId)); return false; } private static String findProcessHost(long threadId, java.sql.Statement processListStmt) throws SQLException { String processHost = null; ResultSet rs = processListStmt.executeQuery("SHOW PROCESSLIST"); while (rs.next()) { long id = rs.getLong(1); if (threadId == id) { processHost = rs.getString(3); break; } } return processHost; } } }