KEYCLOAK-900 Fix resolving of current hostname
This commit is contained in:
parent
526e25abc7
commit
e62858cefd
4 changed files with 503 additions and 19 deletions
|
@ -8,29 +8,67 @@ import java.net.UnknownHostException;
|
|||
*/
|
||||
public class HostUtils {
|
||||
|
||||
// Best effort to find the most proper hostname of this server.
|
||||
public static String getHostName() {
|
||||
String jbossHostName = System.getProperty("jboss.host.name");
|
||||
if (jbossHostName != null) {
|
||||
return jbossHostName;
|
||||
} else {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException uhe) {
|
||||
throw new IllegalStateException(uhe);
|
||||
}
|
||||
}
|
||||
return getHostNameImpl().trim().toLowerCase();
|
||||
}
|
||||
|
||||
public static String getIpAddress() {
|
||||
try {
|
||||
String jbossHostName = System.getProperty("jboss.host.name");
|
||||
if (jbossHostName != null) {
|
||||
return InetAddress.getByName(jbossHostName).getHostAddress();
|
||||
} else {
|
||||
return java.net.InetAddress.getLocalHost().getHostAddress();
|
||||
}
|
||||
String hostname = getHostName();
|
||||
return InetAddress.getByName(hostname).getHostAddress();
|
||||
} catch (UnknownHostException uhe) {
|
||||
throw new IllegalStateException(uhe);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getHostNameImpl() {
|
||||
// Return bind address if available
|
||||
String bindAddr = System.getProperty("jboss.bind.address");
|
||||
if (bindAddr != null && !bindAddr.trim().equals("0.0.0.0")) {
|
||||
return bindAddr;
|
||||
}
|
||||
|
||||
// Fallback to qualified name
|
||||
String qualifiedHostName = System.getProperty("jboss.qualified.host.name");
|
||||
if (qualifiedHostName != null) {
|
||||
return qualifiedHostName;
|
||||
}
|
||||
|
||||
// If not on jboss env, let's try other possible fallbacks
|
||||
// POSIX-like OSes including Mac should have this set
|
||||
qualifiedHostName = System.getenv("HOSTNAME");
|
||||
if (qualifiedHostName != null) {
|
||||
return qualifiedHostName;
|
||||
}
|
||||
|
||||
// Certain versions of Windows
|
||||
qualifiedHostName = System.getenv("COMPUTERNAME");
|
||||
if (qualifiedHostName != null) {
|
||||
return qualifiedHostName;
|
||||
}
|
||||
|
||||
try {
|
||||
return NetworkUtils.canonize(getLocalHost().getHostName());
|
||||
} catch (UnknownHostException uhe) {
|
||||
uhe.printStackTrace();
|
||||
return "unknown-host.unknown-domain";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods returns InetAddress for localhost
|
||||
*
|
||||
* @return InetAddress of the localhost
|
||||
* @throws UnknownHostException if localhost could not be resolved
|
||||
*/
|
||||
private static InetAddress getLocalHost() throws UnknownHostException {
|
||||
InetAddress addr;
|
||||
try {
|
||||
addr = InetAddress.getLocalHost();
|
||||
} catch (ArrayIndexOutOfBoundsException e) { //this is workaround for mac osx bug see AS7-3223 and JGRP-1404
|
||||
addr = InetAddress.getByName(null);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
|
446
core/src/main/java/org/keycloak/util/NetworkUtils.java
Normal file
446
core/src/main/java/org/keycloak/util/NetworkUtils.java
Normal file
|
@ -0,0 +1,446 @@
|
|||
package org.keycloak.util;
|
||||
|
||||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2011, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Utility methods related to networking.
|
||||
*
|
||||
* @author Brian Stansberry (c) 2011 Red Hat Inc.
|
||||
*/
|
||||
public class NetworkUtils {
|
||||
|
||||
private static final int MAX_GROUP_LENGTH = 4;
|
||||
private static final int IPV6_LEN = 8;
|
||||
private static final boolean can_bind_to_mcast_addr; // are we running on Linux ?
|
||||
|
||||
static {
|
||||
can_bind_to_mcast_addr = checkForLinux() || checkForSolaris() || checkForHp();
|
||||
}
|
||||
|
||||
public static String formatPossibleIpv6Address(String address) {
|
||||
if(address == null) {
|
||||
return null;
|
||||
}
|
||||
String ipv6Address;
|
||||
if (address.startsWith("[") && address.endsWith("]")) {
|
||||
ipv6Address = address.substring(0, address.lastIndexOf(']')).substring(1);
|
||||
} else {
|
||||
ipv6Address = address;
|
||||
}
|
||||
// Definitely not an IPv6, return untouched input.
|
||||
if (!mayBeIPv6Address(ipv6Address)) {
|
||||
return ipv6Address;
|
||||
}
|
||||
return '[' + canonize(ipv6Address) + ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convert IPv6 adress into RFC 5952 form.
|
||||
* E.g. 2001:db8:0:1:0:0:0:1 -> 2001:db8:0:1::1</p>
|
||||
*
|
||||
* <p>Method is null safe, and if IPv4 address or host name is passed to the
|
||||
* method it is returned wihout any processing.</p>
|
||||
*
|
||||
* <p>Method also supports IPv4 in IPv6 (e.g. 0:0:0:0:0:ffff:192.0.2.1 ->
|
||||
* ::ffff:192.0.2.1), and zone ID (e.g. fe80:0:0:0:f0f0:c0c0:1919:1234%4
|
||||
* -> fe80::f0f0:c0c0:1919:1234%4).</p>
|
||||
*
|
||||
* @param ipv6Address String representing valid IPv6 address.
|
||||
* @return String representing IPv6 in canonical form.
|
||||
* @throws IllegalArgumentException if IPv6 format is unacceptable.
|
||||
*/
|
||||
public static String canonize(String ipv6Address) throws IllegalArgumentException {
|
||||
|
||||
if (ipv6Address == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Definitely not an IPv6, return untouched input.
|
||||
if (!mayBeIPv6Address(ipv6Address)) {
|
||||
return ipv6Address;
|
||||
}
|
||||
|
||||
// Length without zone ID (%zone) or IPv4 address
|
||||
int ipv6AddressLength = ipv6Address.length();
|
||||
if (isIPv4AddressInIPv6(ipv6Address)) {
|
||||
// IPv4 in IPv6
|
||||
// e.g. 0:0:0:0:0:FFFF:127.0.0.1
|
||||
int lastColonPos = ipv6Address.lastIndexOf(":");
|
||||
int lastColonsPos = ipv6Address.lastIndexOf("::");
|
||||
if (lastColonsPos >= 0 && lastColonPos == lastColonsPos + 1) {
|
||||
// IPv6 part ends with two consecutive colons, last colon is part of IPv6 format.
|
||||
// e.g. ::127.0.0.1
|
||||
ipv6AddressLength = lastColonPos + 1;
|
||||
} else {
|
||||
// IPv6 part ends with only one colon, last colon is not part of IPv6 format.
|
||||
// e.g. ::FFFF:127.0.0.1
|
||||
ipv6AddressLength = lastColonPos;
|
||||
}
|
||||
} else if (ipv6Address.contains(":") && ipv6Address.contains("%")) {
|
||||
// Zone ID
|
||||
// e.g. fe80:0:0:0:f0f0:c0c0:1919:1234%4
|
||||
ipv6AddressLength = ipv6Address.lastIndexOf("%");
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
char [][] groups = new char[IPV6_LEN][MAX_GROUP_LENGTH];
|
||||
int groupCounter = 0;
|
||||
int charInGroupCounter = 0;
|
||||
|
||||
// Index of the current zeroGroup, -1 means not found.
|
||||
int zeroGroupIndex = -1;
|
||||
int zeroGroupLength = 0;
|
||||
|
||||
// maximum length zero group, if there is more then one, then first one
|
||||
int maxZeroGroupIndex = -1;
|
||||
int maxZeroGroupLength = 0;
|
||||
|
||||
boolean isZero = true;
|
||||
boolean groupStart = true;
|
||||
|
||||
/*
|
||||
* Two consecutive colons, initial expansion.
|
||||
* e.g. 2001:db8:0:0:1::1 -> 2001:db8:0:0:1:0:0:1
|
||||
*/
|
||||
StringBuilder expanded = new StringBuilder(ipv6Address);
|
||||
int colonsPos = ipv6Address.indexOf("::");
|
||||
int length = ipv6AddressLength;
|
||||
int change = 0;
|
||||
|
||||
if (colonsPos >= 0 && colonsPos < ipv6AddressLength - 2) {
|
||||
int colonCounter = 0;
|
||||
for (int i = 0; i < ipv6AddressLength; i++) {
|
||||
if (ipv6Address.charAt(i) == ':') {
|
||||
colonCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
if (colonsPos == 0) {
|
||||
expanded.insert(0, "0");
|
||||
change = change + 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < IPV6_LEN - colonCounter; i++) {
|
||||
expanded.insert(colonsPos + 1, "0:");
|
||||
change = change + 2;
|
||||
}
|
||||
|
||||
|
||||
if (colonsPos == ipv6AddressLength - 2) {
|
||||
expanded.setCharAt(colonsPos + change + 1, '0');
|
||||
} else {
|
||||
expanded.deleteCharAt(colonsPos + change + 1);
|
||||
change = change - 1;
|
||||
}
|
||||
length = length + change;
|
||||
}
|
||||
|
||||
|
||||
// Processing one char at the time
|
||||
for (int charCounter = 0; charCounter < length; charCounter++) {
|
||||
char c = expanded.charAt(charCounter);
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
c = (char) (c + 32);
|
||||
}
|
||||
if (c != ':') {
|
||||
groups[groupCounter][charInGroupCounter] = c;
|
||||
if (!(groupStart && c == '0')) {
|
||||
++charInGroupCounter;
|
||||
groupStart = false;
|
||||
}
|
||||
if (c != '0') {
|
||||
isZero = false;
|
||||
}
|
||||
}
|
||||
if (c == ':' || charCounter == (length - 1)) {
|
||||
// We reached end of current group
|
||||
if (isZero) {
|
||||
++zeroGroupLength;
|
||||
if (zeroGroupIndex == -1) {
|
||||
zeroGroupIndex = groupCounter;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isZero || charCounter == (length - 1)) {
|
||||
// We reached end of zero group
|
||||
if (zeroGroupLength > maxZeroGroupLength) {
|
||||
maxZeroGroupLength = zeroGroupLength;
|
||||
maxZeroGroupIndex = zeroGroupIndex;
|
||||
}
|
||||
zeroGroupLength = 0;
|
||||
zeroGroupIndex = -1;
|
||||
}
|
||||
++groupCounter;
|
||||
charInGroupCounter = 0;
|
||||
isZero = true;
|
||||
groupStart = true;
|
||||
}
|
||||
}
|
||||
|
||||
int numberOfGroups = groupCounter;
|
||||
|
||||
// Output results
|
||||
for (groupCounter = 0; groupCounter < numberOfGroups; groupCounter++) {
|
||||
if (maxZeroGroupLength <= 1 || groupCounter < maxZeroGroupIndex
|
||||
|| groupCounter >= maxZeroGroupIndex + maxZeroGroupLength) {
|
||||
for (int j = 0; j < MAX_GROUP_LENGTH; j++) {
|
||||
if (groups[groupCounter][j] != 0) {
|
||||
result.append(groups[groupCounter][j]);
|
||||
}
|
||||
}
|
||||
if (groupCounter < (numberOfGroups - 1)
|
||||
&& (groupCounter != maxZeroGroupIndex - 1
|
||||
|| maxZeroGroupLength <= 1)) {
|
||||
result.append(':');
|
||||
}
|
||||
} else if (groupCounter == maxZeroGroupIndex) {
|
||||
result.append("::");
|
||||
}
|
||||
}
|
||||
|
||||
// Solve problem with three colons in IPv4 in IPv6 format
|
||||
// e.g. 0:0:0:0:0:0:127.0.0.1 -> :::127.0.0.1 -> ::127.0.0.1
|
||||
int resultLength = result.length();
|
||||
if (result.charAt(resultLength - 1) == ':' && ipv6AddressLength < ipv6Address.length()
|
||||
&& ipv6Address.charAt(ipv6AddressLength) == ':') {
|
||||
result.delete(resultLength - 1, resultLength);
|
||||
}
|
||||
|
||||
/*
|
||||
* Append IPv4 from IPv4-in-IPv6 format or Zone ID
|
||||
*/
|
||||
for (int i = ipv6AddressLength; i < ipv6Address.length(); i++) {
|
||||
result.append(ipv6Address.charAt(i));
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Heuristic check if string might be an IPv6 address.
|
||||
*
|
||||
* @param input Any string or null
|
||||
* @return true, if input string contains only hex digits and at least two colons, before '.' or '%' character.
|
||||
*/
|
||||
private static boolean mayBeIPv6Address(String input) {
|
||||
if (input == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean result = false;
|
||||
int colonsCounter = 0;
|
||||
int length = input.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = input.charAt(i);
|
||||
if (c == '.' || c == '%') {
|
||||
// IPv4 in IPv6 or Zone ID detected, end of checking.
|
||||
break;
|
||||
}
|
||||
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
|
||||
|| (c >= 'A' && c <= 'F') || c == ':')) {
|
||||
return false;
|
||||
} else if (c == ':') {
|
||||
colonsCounter++;
|
||||
}
|
||||
}
|
||||
if (colonsCounter >= 2) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Check if it is an IPv4 in IPv6 format.
|
||||
* e.g. 0:0:0:0:0:FFFF:127.0.0.1
|
||||
*
|
||||
* @param ipv6Address the address
|
||||
* @return true, if input string is an IPv4 address in IPv6 format.
|
||||
*/
|
||||
private static boolean isIPv4AddressInIPv6(String ipv6Address) {
|
||||
return (ipv6Address.contains(":") && ipv6Address.contains("."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats input address. For IPV4 returns simply host address, for IPV6 formats address according to <a
|
||||
* href="http://tools.ietf.org/html/rfc5952">RFC5952</a> rules. It does not embed IPV6 address in '[', ']', since those are part of IPV6 URI literal.
|
||||
*
|
||||
* @param inet
|
||||
* @return
|
||||
*/
|
||||
public static String formatAddress(InetAddress inet){
|
||||
if(inet == null){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if(inet instanceof Inet4Address){
|
||||
return inet.getHostAddress();
|
||||
} else if (inet instanceof Inet6Address){
|
||||
byte[] byteRepresentation = inet.getAddress();
|
||||
int[] hexRepresentation = new int[IPV6_LEN];
|
||||
|
||||
for(int i=0;i < hexRepresentation.length;i++){
|
||||
hexRepresentation[i] = ( byteRepresentation[2*i] & 0xFF) << 8 | ( byteRepresentation[2*i+1] & 0xFF );
|
||||
}
|
||||
compactLongestZeroSequence(hexRepresentation);
|
||||
return formatAddress6(hexRepresentation);
|
||||
} else {
|
||||
return inet.getHostAddress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts socket address into string literal, which has form: 'address:port'. Example:<br>
|
||||
* <ul>
|
||||
* <li>127.0.0.1:8080</li>
|
||||
* <li>dns.name.com:8080</li>
|
||||
* <li>[0fe:1::20]:8080</li>
|
||||
* <li>[::1]:8080</li>
|
||||
* </ul>
|
||||
* @param inet
|
||||
* @return
|
||||
*/
|
||||
public static String formatAddress(InetSocketAddress inet){
|
||||
if(inet == null){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
StringBuilder result = new StringBuilder();
|
||||
if(inet.isUnresolved()){
|
||||
result.append(inet.getHostName());
|
||||
}else{
|
||||
result.append(formatPossibleIpv6Address(formatAddress(inet.getAddress())));
|
||||
}
|
||||
result.append(":").append(inet.getPort());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts IPV6 int[] representation into valid IPV6 string literal. Sequence of '-1' values are converted into '::'.
|
||||
* @param hexRepresentation
|
||||
* @return
|
||||
*/
|
||||
private static String formatAddress6(int[] hexRepresentation){
|
||||
if(hexRepresentation == null){
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if(hexRepresentation.length != IPV6_LEN){
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
boolean inCompressedSection = false;
|
||||
for(int i = 0;i<hexRepresentation.length;i++){
|
||||
if(hexRepresentation[i] == -1){
|
||||
if(!inCompressedSection){
|
||||
inCompressedSection = true;
|
||||
if(i == 0){
|
||||
stringBuilder.append("::");
|
||||
} else {
|
||||
stringBuilder.append(':');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
inCompressedSection = false;
|
||||
stringBuilder.append(Integer.toHexString(hexRepresentation[i]));
|
||||
if(i+1<hexRepresentation.length){
|
||||
stringBuilder.append(":");
|
||||
}
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public static boolean isBindingToMulticastDressSupported() {
|
||||
return can_bind_to_mcast_addr;
|
||||
}
|
||||
|
||||
private static void compactLongestZeroSequence(int[] hexRepresentatoin){
|
||||
int bestRunStart = -1;
|
||||
int bestRunLen = -1;
|
||||
boolean inRun = false;
|
||||
int runStart = -1;
|
||||
for(int i=0;i<hexRepresentatoin.length;i++){
|
||||
|
||||
if(hexRepresentatoin[i] == 0){
|
||||
if(!inRun){
|
||||
runStart = i;
|
||||
inRun = true;
|
||||
}
|
||||
} else {
|
||||
if(inRun){
|
||||
inRun = false;
|
||||
int runLen = i - runStart;
|
||||
if(bestRunLen < 0){
|
||||
bestRunStart = runStart;
|
||||
bestRunLen = runLen;
|
||||
} else {
|
||||
if(runLen > bestRunLen){
|
||||
bestRunStart = runStart;
|
||||
bestRunLen = runLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bestRunStart >=0){
|
||||
Arrays.fill(hexRepresentatoin, bestRunStart, bestRunStart + bestRunLen, -1);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkForLinux() {
|
||||
return checkForPresence("os.name", "linux");
|
||||
}
|
||||
|
||||
private static boolean checkForHp() {
|
||||
return checkForPresence("os.name", "hp");
|
||||
}
|
||||
|
||||
private static boolean checkForSolaris() {
|
||||
return checkForPresence("os.name", "sun");
|
||||
}
|
||||
|
||||
private static boolean checkForWindows() {
|
||||
return checkForPresence("os.name", "win");
|
||||
}
|
||||
|
||||
public static boolean checkForMac() {
|
||||
return checkForPresence("os.name", "mac");
|
||||
}
|
||||
|
||||
private static boolean checkForPresence(final String key, final String value) {
|
||||
final String tmp = System.getProperty(key, value);
|
||||
try {
|
||||
return tmp != null && tmp.trim().toLowerCase(Locale.ENGLISH).startsWith(value);
|
||||
} catch (Throwable t) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// No instantiation
|
||||
private NetworkUtils() {
|
||||
|
||||
}
|
||||
}
|
|
@ -64,7 +64,7 @@ public class NodesRegistrationManagement {
|
|||
protected void sendRegistrationEvent(KeycloakDeployment deployment) {
|
||||
log.debug("Sending registration event right now");
|
||||
|
||||
String host = HostUtils.getIpAddress();
|
||||
String host = HostUtils.getHostName();
|
||||
try {
|
||||
ServerRequest.invokeRegisterNode(deployment, host);
|
||||
NodeRegistrationContext regContext = new NodeRegistrationContext(Time.currentTime(), deployment);
|
||||
|
@ -84,7 +84,7 @@ public class NodesRegistrationManagement {
|
|||
protected boolean sendUnregistrationEvent(KeycloakDeployment deployment) {
|
||||
log.debug("Sending Unregistration event right now");
|
||||
|
||||
String host = HostUtils.getIpAddress();
|
||||
String host = HostUtils.getHostName();
|
||||
try {
|
||||
ServerRequest.invokeUnregisterNode(deployment, host);
|
||||
log.debugf("Node '%s' successfully unregistered from Keycloak", host);
|
||||
|
|
|
@ -103,7 +103,7 @@ public class ServerRequest {
|
|||
formparams.add(new BasicNameValuePair(OAuth2Constants.REDIRECT_URI, redirectUri));
|
||||
if (sessionId != null) {
|
||||
formparams.add(new BasicNameValuePair(AdapterConstants.APPLICATION_SESSION_STATE, sessionId));
|
||||
formparams.add(new BasicNameValuePair(AdapterConstants.APPLICATION_SESSION_HOST, HostUtils.getIpAddress()));
|
||||
formparams.add(new BasicNameValuePair(AdapterConstants.APPLICATION_SESSION_HOST, HostUtils.getHostName()));
|
||||
}
|
||||
HttpResponse response = null;
|
||||
HttpPost post = new HttpPost(codeUrl);
|
||||
|
|
Loading…
Reference in a new issue