001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.server.platform;
021
022 import com.google.common.collect.Lists;
023 import org.apache.commons.codec.digest.DigestUtils;
024 import org.apache.commons.lang.StringUtils;
025 import org.slf4j.LoggerFactory;
026
027 import java.io.UnsupportedEncodingException;
028 import java.net.InetAddress;
029 import java.net.NetworkInterface;
030 import java.net.SocketException;
031 import java.net.UnknownHostException;
032 import java.util.Enumeration;
033 import java.util.List;
034
035 /**
036 * @since 2.11
037 */
038 public class ServerIdGenerator {
039
040 /**
041 * Increment this version each time the algorithm is changed. Do not exceed 9.
042 */
043 static final String VERSION = "1";
044
045 static final int CHECKSUM_SIZE = 14;
046
047 private final boolean acceptPrivateAddress;
048
049 public ServerIdGenerator() {
050 this(false);
051 }
052
053 ServerIdGenerator(boolean acceptPrivateAddress) {
054 this.acceptPrivateAddress = acceptPrivateAddress;
055 }
056
057 public String generate(String organisation, String ipAddress) {
058 String id = null;
059 if (StringUtils.isNotBlank(organisation) && StringUtils.isNotBlank(ipAddress)) {
060 InetAddress inetAddress = toValidAddress(ipAddress);
061 if (inetAddress != null) {
062 id = toId(organisation, inetAddress);
063 }
064 }
065 return id;
066 }
067
068 boolean isFixed(InetAddress address) {
069 // Loopback addresses are in the range 127/8.
070 // Link local addresses are in the range 169.254/16 (IPv4) or fe80::/10 (IPv6). They are "autoconfiguration" addresses.
071 // They can assigned pseudorandomly, so they don't guarantee to be the same between two server startups.
072 return acceptPrivateAddress || (!address.isLoopbackAddress() && !address.isLinkLocalAddress());
073 }
074
075 String toId(String organisation, InetAddress address) {
076 String id = new StringBuilder().append(organisation).append("-").append(address.getHostAddress()).toString();
077 try {
078 return VERSION + DigestUtils.shaHex(id.getBytes("UTF-8")).substring(0, CHECKSUM_SIZE);
079
080 } catch (UnsupportedEncodingException e) {
081 throw new IllegalArgumentException("Organisation is not UTF-8 encoded: " + organisation, e);
082 }
083 }
084
085 public InetAddress toValidAddress(String ipAddress) {
086 if (StringUtils.isNotBlank(ipAddress)) {
087 List<InetAddress> validAddresses = getAvailableAddresses();
088 try {
089 InetAddress address = InetAddress.getByName(ipAddress);
090 if (validAddresses.contains(address)) {
091 return address;
092 }
093 } catch (UnknownHostException e) {
094 // ignore, not valid property
095 }
096 }
097 return null;
098 }
099
100 public List<InetAddress> getAvailableAddresses() {
101 List<InetAddress> result = Lists.newArrayList();
102 try {
103 Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
104 while (networkInterfaces.hasMoreElements()) {
105 NetworkInterface networkInterface = networkInterfaces.nextElement();
106 Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
107 while (addresses.hasMoreElements()) {
108 InetAddress ownedAddress = addresses.nextElement();
109 if (isFixed(ownedAddress)) {
110 result.add(ownedAddress);
111 }
112 }
113 }
114 } catch (SocketException e) {
115 LoggerFactory.getLogger(ServerIdGenerator.class).error("Fail to browse network interfaces", e);
116 }
117 return result;
118 }
119 }