/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 1999-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
*/
// SLPConfig.java
//
/**
* This class is a singleton - it has the configuration which
* is the default. It reads from a configuration file and
* this overrides the default. If the config file does not
* expressly forbid it, the ServiceLocationManager interface
* allows some of these configuration options to be modified.
* This configuration is refered to by many points of the
* implementation. Note that the class itself is abstract,
* and is extended by two classes, one that allows slpd to
* run as an SA server only, the other allows it to run
* as a DA as well.
*
* @see com.sun.slp.ServiceLocationManager
*/
package com.sun.slp;
import java.net.*;
import java.util.*;
import java.text.*;
import java.io.*;
/*
* This class contains all configuration information. It
* is hard coded to know the defaults, and will read a config
* file if it is present. The config file will override the
* default values and policy. If the config file allows it
* the user may reset some of these values using the
* ServiceLocationManager interface.
*
*/
class SLPConfig {
/**
* A Java properties file defines `\' as an escape character, which
* conflicts with the SLP API escape convention. Therefore, we need
* to subclass properties so we can parse in the file ourselves.
*/
public static class SLPProperties extends Properties {
SLPProperties(Properties p) {
super(p);
}
// Parse the SLP properties file ourselves. Groan! We don't recognize
// backslash as an escape.
public synchronized void load(InputStream in) throws IOException {
BufferedReader rd = new BufferedReader(new InputStreamReader(in));
while (rd.ready()) {
String ln = rd.readLine();
// Throw out anything that begins with '#' or ';'.
if (ln.startsWith("#") ||
ln.startsWith(";") ||
ln.length() <= 0) {
continue;
}
// Parse out equals sign, if any. Note that we trim any
// white space preceding or following data strings.
// Although the grammar doesn't allow it, users may
// enter blanks in their configuration files.
// NOTE: White space is not allowed in the data of
// property tag or values. If included, according
// to RFC 2614, Section 2.1 these MUST be escaped,
// ie. space would be represented with '\20'.
// Therefore, it is *completely* safe to perform
// these trim()s. They will catch errors resulting
// from sloppy data entry in slp.conf files and
// never corrupt or alter correctly formatted
// properties.
SLPTokenizer tk = new SLPTokenizer(ln, "=");
if (!tk.hasMoreTokens()) {// empty line...
continue;
}
String prop = tk.nextToken().trim();
if (prop.trim().length() <= 0) {// line has just spaces...
continue;
}
if (!tk.hasMoreTokens()) {// line has no definition...
continue;
}
// Register the property.
String def = tk.nextToken().trim();
this.setProperty(prop, def);
}
}
}
protected SLPConfig() {
// Create a temporary, default log to report any errors during
// configuration.
log = new StderrLog();
// Initialize properties. Properties on command line override config
// file properties, and both override defaults.
Properties sysProps = (Properties)(System.getProperties().clone());
// Load Defalts.
try {
Class.forName("com.sun.slp.Defaults");
} catch (ClassNotFoundException ex) {
Assert.printMessageAndDie(this,
"no_class",
new Object[] {"com.sun.slp.Defaults"});
}
// System properties now contain Defaults
Properties defaultProps = System.getProperties();
// Load config file.
SLPProperties slpProps = new SLPProperties(new Properties());
try {
InputStream fis = getConfigURLStream();
if (fis != null) {
slpProps.load(fis);
System.setProperties(slpProps);
}
} catch (IOException ex) {
writeLog("unparsable_config_file",
new Object[] {ex.getMessage()});
}
// Add config properties to Defaults, overwritting any pre-existing
// entries
defaultProps.putAll(slpProps);
// Now add in system props, overwritting any pre-existing entries
defaultProps.putAll(sysProps);
System.setProperties(defaultProps);
// Initialize useScopes property. This is read-only after the file
// has been loaded.
configuredScopes = initializeScopes("net.slp.useScopes");
saConfiguredScopes = (Vector)configuredScopes.clone();
// Add default scope to scopes for SA.
if (saConfiguredScopes.size() <= 0) {
saConfiguredScopes.addElement(Defaults.DEFAULT_SCOPE);
}
// Initialize SA scopes. This uses a Sun specific property for
// scopes only used by the SA and adds in the DA scopes.
saOnlyScopes = initializeScopes(DATable.SA_ONLY_SCOPES_PROP);
// Initialized preconfigured DAs.
preconfiguredDAs = initializePreconfiguredDAs();
// Initialize broadcast flag.
broadcastOnly = Boolean.getBoolean("net.slp.isBroadcastOnly");
// Initialize logging. Default is stderr, first check for alternate.
String failed = null;
try {
String loggerClassName =
System.getProperty("sun.net.slp.loggerClass");
if (loggerClassName != null) {
Class loggerClass = Class.forName(loggerClassName);
// Protect against disastrous pilot error, such as trying
// to use com.sun.slp.SLPConfig as the log class
// (causes stack recursion)
if (Class.forName("java.io.Writer").isAssignableFrom(
loggerClass)) {
Object logger = loggerClass.newInstance();
log = (Writer) logger;
} else {
failed = formatMessage(
"bad_log_class",
new Object[] {
loggerClass.toString()}) + "\n";
}
}
} catch (Throwable ex) {
log = null;
failed = formatMessage(
"bad_log",
new Object[] {
ex.toString()}) + "\n";
}
// If no alternate log, revert to minimal default
if (log == null) {
log = new StderrLog();
// If the alternate log failed, log it through the default log
if (failed != null) {
try {
synchronized (log) {
log.write(failed);
log.flush();
}
} catch (IOException giveUp) {}
}
}
}
private InputStream getConfigURLStream() {
// Open a URL onto the configuration file.
String conf = System.getProperty("sun.net.slp.configURL");
if (conf == null) {
conf = Defaults.SOLARIS_CONF;
}
InputStream str = null;
try {
URL confURL = new URL(conf);
str = confURL.openStream();
} catch (MalformedURLException ex) {
writeLog("url_malformed",
new Object[] {conf});
} catch (IOException ex) {
if (conf != Defaults.SOLARIS_CONF) {
// don't complain if we can't find our own default
writeLog("unparsable_config_file",
new Object[] {ex.getMessage()});
}
}
return str;
}
// ------------------------------------------------------------
// Property manipulation functions
//
private boolean OKBound(int i, int lb, int ub) {
if (i < lb || i > ub)
return false;
else
return true;
}
int getIntProperty(String prop, int df, int lb, int ub) {
int i = Integer.getInteger(prop, df).intValue();
if (OKBound(i, lb, ub)) {
return i;
} else {
writeLog("bad_prop_tag", new Object[] {prop});
return df;
}
}
// ------------------------------------------------------------
// Multicast radius
//
private int iMinMCRadius = 1; // link local scope
private int iMaxMCRadius = 255; // universal scope
int getMCRadius() {
return getIntProperty("net.slp.multicastTTL",
Defaults.iMulticastRadius,
iMinMCRadius,
iMaxMCRadius);
}
// ------------------------------------------------------------
// Heartbeat interval, seconds.
//
private final int iMinHeart = 2000; // 10 minutes
private final int iMaxHeart = 259200000; // 3 days
int getAdvertHeartbeatTime() {
return getIntProperty("net.slp.DAHeartBeat",
Defaults.iHeartbeat,
iMinHeart,
iMaxHeart);
}
// ------------------------------------------------------------
// Active discovery interval, seconds.
//
private final int iMinDisc = 300; // 5 minutes
private final int iMaxDisc = 10800; // 3 hours
int getActiveDiscoveryInterval() {
// We allow zero in order to turn active discovery off, but
// if 5 minutes is the smallest actual time.
int prop = getIntProperty("net.slp.DAActiveDiscoveryInterval",
Defaults.iActiveDiscoveryInterval,
0,
iMaxDisc);
if (prop > 0 && prop < iMinDisc) {
writeLog("bad_prop_tag",
new Object[] {"net.slp.DAActiveDiscoveryInterval"});
return iMinDisc;
}
return prop;
}
// ------------------------------------------------------------
// Active discovery granularity, seconds.
//
private int iMaxDiscGran = iMaxDisc * 2;
int getActiveDiscoveryGranularity() {
return getIntProperty("sun.net.slp.DAActiveDiscoveryGranularity",
Defaults.iActiveDiscoveryGranularity,
0,
iMaxDiscGran);
}
// ------------------------------------------------------------
// Bound for random wait, milliseconds.
//
private final int iMinWait = 1000; // 1 sec.
private final int iMaxWait = 3000; // 3 sec.
int getRandomWaitBound() {
return getIntProperty("net.slp.randomWaitBound",
Defaults.iRandomWaitBound,
iMinWait,
iMaxWait);
}
private static Random randomWait = null;
long getRandomWait() {
if (randomWait == null) {
randomWait = new Random();
}
double r = randomWait.nextDouble();
double max = (double)getRandomWaitBound();
return (long)(max * r);
}
// ------------------------------------------------------------
// TCP timeout, milliseconds.
//
final static private int iMinTimeout = 100;
final static private int iMaxTimeout = 360000;
int getTCPTimeout() {
return getIntProperty("sun.net.slp.TCPTimeout",
Defaults.iTCPTimeout,
iMinTimeout,
iMaxTimeout);
}
// ------------------------------------------------------------
// Path MTU
//
private final int iMinMTU = 128; // used for some ppp connections
private final int iMaxMTU = 8192; // used on some LANs
int getMTU() {
return getIntProperty("net.slp.MTU",
Defaults.iMTU,
iMinMTU,
iMaxMTU);
}
// ------------------------------------------------------------
// Serialized registrations.
//
String getSerializedRegURL() {
return System.getProperty("net.slp.serializedRegURL", null);
}
// ------------------------------------------------------------
// Are we running as a DA or SA server?
//
protected static boolean isSA = false;
boolean isDA() {
return false;
}
boolean isSA() {
return isSA;
}
// ------------------------------------------------------------
// DA and SA attributes
//
Vector getDAAttributes() {
return getAttributes("net.slp.DAAttributes",
Defaults.defaultDAAttributes,
true);
}
Vector getSAAttributes() {
return getAttributes("net.slp.SAAttributes",
Defaults.defaultSAAttributes,
false);
}
private Vector getAttributes(String prop,
Vector defaults,
boolean daAttrs) {
String attrList =
System.getProperty(prop);
if (attrList == null || attrList.length() <= 0) {
return (Vector)defaults.clone();
}
try {
Vector sAttrs =
SrvLocHeader.parseCommaSeparatedListIn(attrList, false);
Vector attrs = new Vector();
int i, n = sAttrs.size();
// Create attribute objects.
for (i = 0; i < n; i++) {
String attrExp = (String)sAttrs.elementAt(i);
ServiceLocationAttribute attr =
new ServiceLocationAttribute(attrExp, false);
// If this is the min-refresh-interval, then check the value.
if (daAttrs &&
attr.getId().equals(
Defaults.MIN_REFRESH_INTERVAL_ATTR_ID)) {
Vector values = attr.getValues();
boolean errorp = true;
if (values != null && values.size() == 1) {
Object val = values.elementAt(0);
if (val instanceof Integer) {
int ival = ((Integer)val).intValue();
if (ival >= 0 &&
ival <= ServiceURL.LIFETIME_MAXIMUM) {
errorp = false;
}
}
}
// Throw exception if it didn't work.
if (errorp) {
throw new ServiceLocationException(
ServiceLocationException.PARSE_ERROR,
"syntax_error_prop",
new Object[] {prop, attrs});
}
}
// Add attribute to vector.
attrs.addElement(attr);
}
return attrs;
} catch (Exception ex) {
writeLog("syntax_error_prop",
new Object[] {prop, attrList});
return (Vector)defaults.clone();
}
}
// -------------------------------------------------------------
// Do we support V1?
//
boolean isV1Supported() {
return false;
}
// -------------------------------------------------------------
// Queue length for server socket.
//
int getServerSocketQueueLength() {
return getIntProperty("sun.net.slp.serverSocketQueueLength",
Defaults.iSocketQueueLength,
0,
Integer.MAX_VALUE);
}
// ------------------------------------------------------------
// Testing options
//
boolean traceAll() {// not official!
return Boolean.getBoolean("sun.net.slp.traceALL");
}
boolean regTest() {
if (Boolean.getBoolean("sun.net.slp.traceALL") ||
Boolean.getBoolean("net.slp.traceReg"))
return true;
else
return false;
}
boolean traceMsg() {
if (Boolean.getBoolean("sun.net.slp.traceALL") ||
Boolean.getBoolean("net.slp.traceMsg"))
return true;
else
return false;
}
boolean traceDrop() {
if (Boolean.getBoolean("sun.net.slp.traceALL") ||
Boolean.getBoolean("net.slp.traceDrop"))
return true;
else
return false;
}
boolean traceDATraffic() {
if (Boolean.getBoolean("sun.net.slp.traceALL") ||
Boolean.getBoolean("net.slp.traceDATraffic"))
return true;
else
return false;
}
// cannot use Boolean.getBoolean as the default is 'true'
// using that mechanism, absense would be considered 'false'
boolean passiveDADetection() {
String sPassive =
System.getProperty("net.slp.passiveDADetection", "true");
if (sPassive.equalsIgnoreCase("true"))
return true;
else
return false;
}
// Initialized when the SLPConfig object is created to avoid changing
// during the program.
private boolean broadcastOnly = false;
boolean isBroadcastOnly() {
return broadcastOnly;
}
// ------------------------------------------------------------
// Multicast/broadcast socket mangement.
//
DatagramSocket broadSocket = null; // cached broadcast socket.
// Reopen the multicast/broadcast socket bound to the
// interface. If groups is not null, then join all
// the groups. Otherwise, this is send only.
DatagramSocket
refreshMulticastSocketOnInterface(InetAddress interfac,
Vector groups) {
try {
// Reopen it.
DatagramSocket dss =
getMulticastSocketOnInterface(interfac,
(groups == null ? true:false));
if ((groups != null) && (dss instanceof MulticastSocket)) {
int i, n = groups.size();
MulticastSocket mss = (MulticastSocket)dss;
for (i = 0; i < n; i++) {
InetAddress maddr = (InetAddress)groups.elementAt(i);
mss.joinGroup(maddr);
}
}
return dss;
} catch (Exception ex) {
// Any exception in error recovery causes program to die.
Assert.slpassert(false,
"cast_socket_failure",
new Object[] {ex, ex.getMessage()});
}
return null;
}
// Open a multicast/broadcast socket on the interface. Note that if
// the socket is broadcast, the network interface is not specified in the
// creation message. Is it bound to all interfaces? The isSend parameter
// specifies whether the socket is for send only.
DatagramSocket
getMulticastSocketOnInterface(InetAddress interfac, boolean isSend)
throws ServiceLocationException {
DatagramSocket castSocket = null;
// Substitute broadcast if we are configured for it.
if (isBroadcastOnly()) {
try {
// If transmit, then simply return a new socket.
if (isSend) {
castSocket = new DatagramSocket();
} else {
// Return cached socket if there.
if (broadSocket != null) {
castSocket = broadSocket;
} else {
// Make a new broadcast socket.
castSocket =
new DatagramSocket(Defaults.iSLPPort,
getBroadcastAddress());
}
// Cache for future reference.
broadSocket = castSocket;
}
} catch (SocketException ex) {
throw
new ServiceLocationException(
ServiceLocationException.NETWORK_INIT_FAILED,
"socket_creation_failure",
new Object[] {
getBroadcastAddress(), ex.getMessage()});
}
} else {
// Create a multicast socket.
MulticastSocket ms;
try {
if (isSend) {
ms = new MulticastSocket();
} else {
ms = new MulticastSocket(Defaults.iSLPPort);
}
} catch (IOException ex) {
throw
new ServiceLocationException(
ServiceLocationException.NETWORK_INIT_FAILED,
"socket_creation_failure",
new Object[] {interfac, ex.getMessage()});
}
try {
// Set the TTL and the interface on the multicast socket.
// Client is responsible for joining group.
ms.setTimeToLive(getMCRadius());
ms.setInterface(interfac);
} catch (IOException ex) {
throw
new ServiceLocationException(
ServiceLocationException.NETWORK_INIT_FAILED,
"socket_initializtion_failure",
new Object[] {interfac, ex.getMessage()});
}
castSocket = ms;
}
return castSocket;
}
// ------------------------------------------------------------
// Type hint
//
// Return a vector of ServiceType objects for the type hint.
Vector getTypeHint() {
Vector hint = new Vector();
String sTypeList = System.getProperty("net.slp.typeHint", "");
if (sTypeList.length() <= 0) {
return hint;
}
// Create a vector of ServiceType objects for the type hint.
try {
hint = SrvLocHeader.parseCommaSeparatedListIn(sTypeList, true);
int i, n = hint.size();
for (i = 0; i < n; i++) {
String type = (String)hint.elementAt(i);
hint.setElementAt(new ServiceType(type), i);
}
} catch (ServiceLocationException ex) {
writeLog("syntax_error_prop",
new Object[] {"net.slp.typeHint", sTypeList});
hint.removeAllElements();
}
return hint;
}
// ------------------------------------------------------------
// Configured scope handling
//
// Vector of configured scopes.
private Vector configuredScopes = null;
// Vector of configures scopes for SA.
private Vector saConfiguredScopes = null;
// Vector of scopes only in the sa server.
private Vector saOnlyScopes = null;
// Return the configured scopes.
Vector getConfiguredScopes() {
return (Vector)configuredScopes.clone();
}
// Return SA scopes.
Vector getSAOnlyScopes() {
return (Vector)saOnlyScopes.clone();
}
// Return the configured scopes for the SA.
Vector getSAConfiguredScopes() {
return (Vector)saConfiguredScopes.clone();
}
// Add scopes discovered during preconfigured DA contact.
// These count as configured scopes.
void addPreconfiguredDAScopes(Vector scopes) {
int i, n = scopes.size();
for (i = 0; i < n; i++) {
Object scope = scopes.elementAt(i);
if (!configuredScopes.contains(scope)) {
configuredScopes.addElement(scope);
}
// There better be none extra here for the SA server/DA.
if (isSA() || isDA()) {
Assert.slpassert(saConfiguredScopes.contains(scope),
"sa_new_scope",
new Object[] {scope, saConfiguredScopes});
}
}
}
// Initialize the scopes list on property.
private Vector initializeScopes(String prop) {
String sScopes = System.getProperty(prop);
if (sScopes == null || sScopes.length() <= 0) {
return new Vector();
}
try {
Vector vv =
SrvLocHeader.parseCommaSeparatedListIn(sScopes, true);
// Unescape scope strings.
SLPHeaderV2.unescapeScopeStrings(vv);
// Validate, lower case scope names.
DATable.validateScopes(vv, getLocale());
if (vv.size() > 0) {
return vv;
}
} catch (ServiceLocationException ex) {
writeLog("syntax_error_prop",
new Object[] {
prop,
sScopes});
}
return new Vector();
}
// Vector of preconfigured DAs. Read only after initialized.
private Vector preconfiguredDAs = null;
// Return a vector of DA addresses.
Vector getPreconfiguredDAs() {
return (Vector)preconfiguredDAs.clone();
}
// Initialize preconfigured DA list.
private Vector initializePreconfiguredDAs() {
String sDAList = System.getProperty("net.slp.DAAddresses", "");
Vector ret = new Vector();
sDAList.trim();
if (sDAList.length() <= 0) {
return ret;
}
try {
ret = SrvLocHeader.parseCommaSeparatedListIn(sDAList, true);
} catch (ServiceLocationException ex) {
writeLog("syntax_error_prop",
new Object[] {"net.slp.DAAddress", sDAList});
return ret;
}
// Convert to InetAddress objects.
int i;
for (i = 0; i < ret.size(); i++) {
String da = "";
try {
da = ((String)ret.elementAt(i)).trim();
InetAddress daAddr = InetAddress.getByName(da);
ret.setElementAt(daAddr, i);
} catch (UnknownHostException ex) {
writeLog("resolve_failed",
new Object[] {da});
/*
* Must decrement the index 'i' otherwise the next iteration
* around the loop will miss the element immediately after
* the element removed.
*
* WARNING: Do not use 'i' again until the loop has
* iterated as it may, after decrementing,
* be negative.
*/
ret.removeElementAt(i);
i--;
continue;
}
}
return ret;
}
// ------------------------------------------------------------
// SLPv1 Support Switches
//
boolean getSLPv1NotSupported() {// not official!
return Boolean.getBoolean("sun.net.slp.SLPv1NotSupported");
}
boolean getAcceptSLPv1UnscopedRegs() {// not official!
if (!getSLPv1NotSupported()) {
return Boolean.getBoolean("sun.net.slp.acceptSLPv1UnscopedRegs");
}
return false;
}
// ------------------------------------------------------------
// Accessor for SLPConfig object
//
protected static SLPConfig theSLPConfig = null;
static SLPConfig getSLPConfig() {
if (theSLPConfig == null) {
theSLPConfig = new SLPConfig();
}
return theSLPConfig;
}
/**
* @return Maximum number of messages/objects to return.
*/
int getMaximumResults() {
int i = Integer.getInteger("net.slp.maxResults",
Defaults.iMaximumResults).intValue();
if (i == -1) {
i = Integer.MAX_VALUE;
}
if (OKBound(i, 1, Integer.MAX_VALUE)) {
return i;
} else {
writeLog("bad_prop_tag",
new Object[] {
"net.slp.maxResults"});
return Defaults.iMaximumResults;
}
}
/**
* Convert a language tag into a locale.
*/
static Locale langTagToLocale(String ltag) {
// We treat the first part as the ISO 639 language and the
// second part as the ISO 3166 country tag, even though RFC
// 1766 doesn't necessarily require that. We should probably
// use a lookup table here to determine if they are correct.
StringTokenizer tk = new StringTokenizer(ltag, "-");
String lang = "";
String country = "";
if (tk.hasMoreTokens()) {
lang = tk.nextToken();
if (tk.hasMoreTokens()) {
country = tk.nextToken("");
// country name may have "-" in it...
}
}
return new Locale(lang, country);
}
/**
* Convert a Locale object into a language tag for output.
*
* @param locale The Locale.
* @return String with the language tag encoded.
*/
static String localeToLangTag(Locale locale) {
// Construct the language tag.
String ltag = locale.getCountry();
ltag = locale.getLanguage() + (ltag.length() <= 0 ? "" : ("-" + ltag));
return ltag;
}
/**
* @return the language requests will be made in.
*/
static Locale getLocale() {
String s = System.getProperty("net.slp.locale");
if (s != null && s.length() > 0) {
return langTagToLocale(s);
} else {
// Return the Java default if the SLP property is not set.
return Locale.getDefault();
}
}
/**
* @return the InetAddress of the broadcast interface.
*/
static private InetAddress broadcastAddress;
static InetAddress getBroadcastAddress() {
if (broadcastAddress == null) {
try {
broadcastAddress =
InetAddress.getByName(Defaults.sBroadcast);
} catch (UnknownHostException uhe) {
Assert.slpassert(false,
"cast_address_failure",
new Object[] {Defaults.sBroadcast});
}
}
return broadcastAddress;
}
/**
* @return the InetAddress of the multicast group.
*/
static private InetAddress multicastAddress;
static InetAddress getMulticastAddress() {
if (multicastAddress == null) {
try {
multicastAddress =
InetAddress.getByName(Defaults.sGeneralSLPMCAddress);
} catch (UnknownHostException uhe) {
Assert.slpassert(false,
"cast_address_failure",
new Object[] {Defaults.sGeneralSLPMCAddress});
}
}
return multicastAddress;
}
/**
* @return the interfaces on which SLP should listen and transmit.
*/
private static Vector interfaces = null;
Vector getInterfaces() {
if (interfaces == null) {
InetAddress iaLocal = null;
// Get local host.
try {
iaLocal = InetAddress.getLocalHost();
} catch (UnknownHostException ex) {
Assert.slpassert(false,
"resolve_failed",
new Object[] {"localhost"});
}
String mcastI = System.getProperty("net.slp.interfaces");
interfaces = new Vector();
// Only add local host if nothing else is given.
if (mcastI == null || mcastI.length() <= 0) {
interfaces.addElement(iaLocal);
return interfaces;
}
Vector nintr;
try {
nintr = SrvLocHeader.parseCommaSeparatedListIn(mcastI, true);
} catch (ServiceLocationException ex) {
writeLog("syntax_error_prop",
new Object[] {
"net.slp.multicastInterfaces",
mcastI});
// Add local host.
interfaces.addElement(iaLocal);
return interfaces;
}
// See if they are really there.
int i, n = nintr.size();
for (i = 0; i < n; i++) {
InetAddress ia;
String host = (String)nintr.elementAt(i);
try {
ia = InetAddress.getByName(host);
} catch (UnknownHostException ex) {
writeLog("unknown_interface",
new Object[] {host,
"net.slp.multicastInterfaces"});
continue;
}
if (!interfaces.contains(ia)) {
// Add default at beginning.
if (ia.equals(iaLocal)) {
interfaces.insertElementAt(ia, 0);
} else {
interfaces.addElement(ia);
}
}
}
}
return interfaces;
}
/**
* @return An InetAddress object representing 127.0.0.1
*/
InetAddress getLoopback() {
InetAddress iaLoopback = null;
try {
iaLoopback = InetAddress.getByName(Defaults.LOOPBACK_ADDRESS);
} catch (UnknownHostException ex) {
Assert.slpassert(false,
"resolve_failed",
new Object[] {"localhost loopback"});
}
return iaLoopback;
}
/**
* @return The default interface, which should be the first in the
* interfaces vector Vector.
*/
InetAddress getLocalHost() {
Vector inter = getInterfaces();
return (InetAddress)inter.elementAt(0);
}
// Return true if the address is one of the local interfaces.
boolean isLocalHostSource(InetAddress addr) {
// First check loopback
if (addr.equals(getLoopback())) {
return true;
}
return interfaces.contains(addr);
}
// -----------------
// Timeouts
//
// Return the maximum wait for multicast convergence.
final static private int iMultiMin = 1000; // one second
final static private int iMultiMax = 60000; // one minute
int getMulticastMaximumWait() {
return getIntProperty("net.slp.multicastMaximumWait",
Defaults.iMulticastMaxWait,
iMultiMin,
iMultiMax);
}
/*
* @return Vector of timeouts for multicast convergence.
*/
int[] getMulticastTimeouts() {
int[] timeouts = parseTimeouts("net.slp.multicastTimeouts",
Defaults.a_iConvergeTimeout);
timeouts = capTimeouts("net.slp.multicastTimeouts",
timeouts,
false,
0,
0);
return timeouts;
}
/**
* @return Vector of timeouts to try for datagram transmission.
*/
int[] getDatagramTimeouts() {
int[] timeouts = parseTimeouts("net.slp.datagramTimeouts",
Defaults.a_iDatagramTimeout);
timeouts = capTimeouts("net.slp.datagramTimeouts",
timeouts,
true,
iMultiMin,
iMultiMax);
return timeouts;
}
/**
* @return Vector of timeouts for DA discovery multicast.
*/
int[] getDADiscoveryTimeouts() {
int[] timeouts = parseTimeouts("net.slp.DADiscoveryTimeouts",
Defaults.a_iDADiscoveryTimeout);
timeouts = capTimeouts("net.slp.DADiscoveryTimeouts",
timeouts,
false,
0,
0);
return timeouts;
}
/**
* This method ensures that all the timeouts are within valid ranges.
* The sum of all timeouts for the given property name must not
* exceed the value returned by <i>getMulticastMaximumWait()</i>. If
* the sum of all timeouts does exceed the maximum wait period the
* timeouts are averaged out so that the sum equals the maximum wait
* period.
* <br>
* Additional range checking is also performed when <i>rangeCheck</i>
* is true. Then the sum of all timeouts must also be between <i>min</i>
* and <i>max</i>. If the sum of all timeouts is not within the range
* the average is taken from the closest range boundary.
*
* @param property
* Name of timeout property being capped. This is only present for
* reporting purposes and no actual manipulation of the property
* is made within this method.
* @param timeouts
* Array of timeout values.
* @param rangeCheck
* Indicator of whether additional range checking is required. When
* false <i>min</i> and <i>max</i> are ignored.
* @param min
* Additional range checking lower boundary.
* @param max
* Additional range checking upper boundary.
* @return
* Array of capped timeouts. Note this may be the same array as
* passed in (<i>timeouts</i>).
*/
private int[] capTimeouts(String property,
int[] timeouts,
boolean rangeCheck,
int min,
int max) {
int averagedTimeout;
int totalWait = 0;
for (int index = 0; index < timeouts.length; index++) {
totalWait += timeouts[index];
}
if (rangeCheck) {
// If sum of timeouts within limits then finished.
if (totalWait >= min && totalWait <= max) {
return timeouts;
}
// Average out the timeouts so the sum is equal to the closest
// range boundary.
if (totalWait < min) {
averagedTimeout = min / timeouts.length;
} else {
averagedTimeout = max / timeouts.length;
}
writeLog("capped_range_timeout_prop",
new Object[] {property,
String.valueOf(totalWait),
String.valueOf(min),
String.valueOf(max),
String.valueOf(timeouts.length),
String.valueOf(averagedTimeout)});
} else {
// Sum of all timeouts must not exceed this value.
int maximumWait = getMulticastMaximumWait();
// If sum of timeouts within limits then finished.
if (totalWait <= maximumWait) {
return timeouts;
}
// Average out the timeouts so the sum is equal to the maximum
// timeout.
averagedTimeout = maximumWait / timeouts.length;
writeLog("capped_timeout_prop",
new Object[] {property,
String.valueOf(totalWait),
String.valueOf(maximumWait),
String.valueOf(timeouts.length),
String.valueOf(averagedTimeout)});
}
for (int index = 0; index < timeouts.length; index++) {
timeouts[index] = averagedTimeout;
}
return timeouts;
}
private int[] parseTimeouts(String property, int[] defaults) {
String sTimeouts = System.getProperty(property);
if (sTimeouts == null || sTimeouts.length() <= 0) {
return defaults;
}
Vector timeouts = null;
try {
timeouts = SrvLocHeader.parseCommaSeparatedListIn(sTimeouts, true);
} catch (ServiceLocationException ex) {
writeLog("syntax_error_prop",
new Object[] {property, sTimeouts});
return defaults;
}
int iCount = 0;
int[] iTOs = new int[timeouts.size()];
for (Enumeration en = timeouts.elements(); en.hasMoreElements(); ) {
String s1 = (String)en.nextElement();
try {
iTOs[iCount] = Integer.parseInt(s1);
} catch (NumberFormatException nfe) {
writeLog("syntax_error_prop",
new Object[] {property, sTimeouts});
return defaults;
}
if (iTOs[iCount] < 0) {
writeLog("invalid_timeout_prop",
new Object[] {property, String.valueOf(iTOs[iCount])});
return defaults;
}
iCount++;
}
return iTOs;
}
// -----------------------------
// SLP Time Calculation
//
/**
* Returns the number of seconds since 00:00 Universal Coordinated
* Time, January 1, 1970.
*
* Java returns the number of milliseconds, so all the method does is
* divide by 1000.
*
* This implementation still will have a problem when the Java time
* values wraps, but there isn't much we can do now.
*/
static long currentSLPTime() {
return (System.currentTimeMillis() / 1000);
}
/* security */
// Indicates whether security class is available.
boolean getSecurityEnabled() {
return securityEnabled;
}
private static boolean securityEnabled;
// Indicates whether the securityEnabled property is true
boolean getHasSecurity() {
return securityEnabled &&
(new Boolean(System.getProperty("net.slp.securityEnabled",
"false")).booleanValue());
}
// I18N Support.
private static final String BASE_BUNDLE_NAME = "com/sun/slp/ClientLib";
ResourceBundle getMessageBundle(Locale locale) {
ResourceBundle msgBundle = null;
// First try the Solaris Java locale area
try {
URL[] urls = new URL[] {new URL("file:/usr/share/lib/locale/")};
URLClassLoader ld = new URLClassLoader(urls);
msgBundle = ResourceBundle.getBundle(BASE_BUNDLE_NAME, locale, ld);
return msgBundle;
} catch (MalformedURLException e) { // shouldn't get here
} catch (MissingResourceException ex) {
System.err.println("Missing resource bundle ``"+
"/usr/share/lib/locale/" + BASE_BUNDLE_NAME +
"'' for locale ``" +
locale + "''; trying default...");
}
try {
msgBundle = ResourceBundle.getBundle(BASE_BUNDLE_NAME, locale);
} catch (MissingResourceException ex) { // can't localize this one!
// We can't print out to the log, because we may be in the
// process of trying to.
System.err.println("Missing resource bundle ``"+
BASE_BUNDLE_NAME+
"'' for locale ``"+
locale+
"''");
// Hosed if the default locale is missing.
if (locale.equals(Defaults.locale)) {
System.err.println("Exiting...");
System.exit(1);
}
// Otherwise, return the default locale.
System.err.println("Using SLP default locale ``" +
Defaults.locale +
"''");
msgBundle = getMessageBundle(Defaults.locale);
}
return msgBundle;
}
String formatMessage(String msgTag, Object[] params) {
ResourceBundle bundle = getMessageBundle(getLocale());
return formatMessageInternal(msgTag, params, bundle);
}
// MessageFormat is picky about types. Convert the params into strings.
static void convertToString(Object[] params) {
int i, n = params.length;
for (i = 0; i < n; i++) {
if (params[i] != null) {
params[i] = params[i].toString();
} else {
params[i] = "<null>";
}
}
}
static String
formatMessageInternal(String msgTag,
Object[] params,
ResourceBundle bundle) {
String pattern = "";
try {
pattern = bundle.getString(msgTag);
} catch (MissingResourceException ex) {
// Attempt to report error. Can't use Assert here because it
// calls back into SLPConfig.
String msg = "Can''t find message ``{0}''''.";
try {
pattern = bundle.getString("cant_find_resource");
msg = MessageFormat.format(pattern, new Object[] {msgTag});
} catch (MissingResourceException exx) {
}
System.err.println(msg);
System.exit(-1);
}
convertToString(params);
return MessageFormat.format(pattern, params);
}
// logging.
// Protected so slpd can replace it.
protected Writer log;
// Synchronized so writes from multiple threads don't get interleaved.
void writeLog(String msgTag, Object[] params) {
// MessageFormat is picky about types. Convert the params into strings.
convertToString(params);
try {
synchronized (log) {
log.write(formatMessage(msgTag, params));
log.flush();
}
} catch (IOException ex) {}
}
void writeLogLine(String msgTag, Object[] params) {
try {
String pattern = getMessageBundle(getLocale()).getString(msgTag);
synchronized (log) {
log.write(formatMessage(msgTag, params));
log.write("\n");
log.flush();
}
} catch (IOException ex) {}
}
static String getDateString() {
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
DateFormat.DEFAULT,
getLocale());
Calendar calendar = Calendar.getInstance(getLocale());
return df.format(calendar.getTime());
}
// On load, check whether the signature class is available, and turn
// security off if not.
static {
securityEnabled = true;
try {
Class c = Class.forName("com.sun.slp.AuthBlock");
} catch (ClassNotFoundException e) {
securityEnabled = false;
}
}
}