/* * 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 (c) 1999 by Sun Microsystems, Inc. * All rights reserved. * */ // ServiceTable.java: Storage of all services. // Author: James Kempf // Created On: Fri Oct 10 14:23:25 1997 // Last Modified By: James Kempf // Last Modified On: Thu Apr 1 10:33:46 1999 // Update Count: 461 // package com.sun.slp; import java.util.*; import java.io.*; import java.security.*; import java.net.*; /** * The ServiceTable object records all service registrations. Note * that any exceptions internal to the service table are processed * and either returned as SrvRply objects or are reported. * * @author James Kempf */ class ServiceTable extends Object { // Key for SDAAdvert class. static final String SDAADVERT = "com.sun.slp.SDAAdvert"; private static final String locationMsg = "Service table"; // // Instance variables. // // The service store. protected ServiceStore store = null; // // Class variables. // System properties. static protected SLPConfig conf = null; // Singleton objects for the service tables. static protected ServiceTable table = null; // The ager thread. static protected AgerThread thrAger = null; // Time to sleep. Adjusted depending on incoming URLs. private static long sleepyTime = Defaults.lMaxSleepTime; // // Creation of singleton. // // Protected constructor. protected ServiceTable() { if (thrAger != null) { return; } // Create the ager thread. thrAger = new AgerThread(); // Set the priority low, so other things (like active discovery) // take priority. thrAger.setPriority(Thread.MIN_PRIORITY); thrAger.start(); } /** * Return an SA service store. * * @return The distinguished table object. */ static ServiceTable getServiceTable() throws ServiceLocationException { if (conf == null) { conf = SLPConfig.getSLPConfig(); } if (table == null) { table = createServiceTable(); } return table; } /** * Return a service table object. * * @return The service table object. */ private static ServiceTable createServiceTable() throws ServiceLocationException { ServiceTable table = new ServiceTable(); table.store = ServiceStoreFactory.createServiceStore(); return table; } // // Support for serializated registrations. // /** * If any serialized registrations are pending, then unserialize * and register. */ public void deserializeTable() { // If there are any serialized registrations, then get // them and perform registrations. String serializedURL = conf.getSerializedRegURL(); if (serializedURL != null) { ServiceStore serStore = getStoreFromURL(serializedURL); if (serStore != null) { registerStore(serStore); } } } /** * Serialize the table to the URL. * * @param URL String giving the URL to which the store should be * serialized. */ void serializeServiceStore(String URL) { // Open an object output stream for the URL, serialize through // the factory. try { URL url = new URL(URL); URLConnection urlConn = url.openConnection(); OutputStream os = urlConn.getOutputStream(); BufferedWriter di = new BufferedWriter(new OutputStreamWriter(os)); // Serialize the store. ServiceStoreFactory.serialize(di, store); } catch (MalformedURLException ex) { conf.writeLog("st_serialized_malform", new Object[] {URL}); } catch (UnsupportedEncodingException ex) { conf.writeLog("st_unsupported_encoding", new Object[] {URL}); } catch (IOException ex) { conf.writeLog("st_serialized_ioexception", new Object[] {URL, ex}); } catch (ServiceLocationException ex) { conf.writeLog("st_serialized_sle", new Object[] {URL, ex.getMessage()}); } } // Read proxy registrations from the URL. private ServiceStore getStoreFromURL(String serializedURL) { ServiceStore serStore = null; // Open an object input stream for the URL, deserialize through // the factory. try { URL url = new URL(serializedURL); InputStream is = url.openStream(); BufferedReader di = new BufferedReader(new InputStreamReader(is)); // Deserialize the objects. serStore = ServiceStoreFactory.deserializeServiceStore(di); } catch (MalformedURLException ex) { conf.writeLog("st_serialized_malform", new Object[] {serializedURL}); } catch (UnsupportedEncodingException ex) { conf.writeLog("st_unsupported_encoding", new Object[] {serializedURL}); } catch (IOException ex) { conf.writeLog("st_serialized_ioexception", new Object[] { serializedURL, ex.getMessage()}); } catch (ServiceLocationException ex) { conf.writeLog("st_serialized_sle", new Object[] { serializedURL, ex.getMessage()}); } return serStore; } // Walk the table, performing actual registrations on all records. private void registerStore(ServiceStore serStore) { // Walk the table. Enumeration en = serStore.getServiceRecordsByScope(null); boolean hasURLSig = conf.getHasSecurity(); boolean hasAttrSig = conf.getHasSecurity(); PermSARegTable pregTable = SARequester.getPermSARegTable(); while (en.hasMoreElements()) { ServiceStore.ServiceRecord rec = (ServiceStore.ServiceRecord)en.nextElement(); ServiceURL surl = rec.getServiceURL(); Vector scopes = rec.getScopes(); Vector attrs = rec.getAttrList(); Locale locale = rec.getLocale(); Hashtable urlSig = null; Hashtable attrSig = null; // Note that we can't use the Advertiser to register here, // because we may not be listening yet for registrations. // We need to do this all by hand. try { // Create a registration message for refreshing. CSrvReg creg = new CSrvReg(false, locale, surl, scopes, attrs, null, null); // We externalize to a server side message if authentication // is needed. This creates the auth blocks for the scopes. // Doing this in any other way is alot more complicated, // although doing it this way seems kludgy. if (hasURLSig || hasAttrSig) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); creg.getHeader().externalize(baos, false, true); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); bais.read(); // pop off version and function code... bais.read(); DataInputStream dis = new DataInputStream(bais); SLPHeaderV2 hdr = new SLPHeaderV2(); hdr.parseHeader(SrvLocHeader.SrvReg, dis); SSrvReg sreg = new SSrvReg(hdr, dis); // Now we've got it, after much effort. Get the auths. urlSig = sreg.URLSignature; attrSig = sreg.attrSignature; } store.register(surl, attrs, scopes, locale, urlSig, attrSig); // Now we've got to put the registration into the // PermSARegTable. Again, we do everything by hand // because we can't use Advertiser. if (surl.getIsPermanent()) { pregTable.reg(surl, creg); } // Report registration. if (conf.regTest()) { conf.writeLog("st_reg_add", new Object[] { locationMsg, locale, surl.getServiceType(), surl, attrs, scopes}); } } catch (ServiceLocationException ex) { String msg = ex.getMessage(); conf.writeLog("st_serialized_seex", new Object[] { new Integer(ex.getErrorCode()), surl, (msg == null ? "":msg)}); } catch (Exception ex) { String msg = ex.getMessage(); conf.writeLog("st_serialized_seex", new Object[] { surl, (msg == null ? "":msg)}); } } } // // Record aging. // // // Run the thread that ages out records. // private class AgerThread extends Thread { public void run() { setName("SLP Service Table Age-out"); long alarmTime = sleepyTime; // when to wake up next long wentToSleep = 0; // what time we went to bed while (true) { try { // Record when we went to sleep. wentToSleep = System.currentTimeMillis(); // Sleep for the minimum amount of time needed before we // must wake up and check. sleep(alarmTime); } catch (InterruptedException ie) { // A new registration came in. Calculate how much time // remains until we would have woken up. If this is // less than the new sleepyTime, then we set the alarm // for this time. If it is more, then we set the alarm // for the new sleepyTime. long remainingSleepTime = (wentToSleep + alarmTime) - System.currentTimeMillis(); remainingSleepTime = // just in case... ((remainingSleepTime <= 0) ? 0 : remainingSleepTime); alarmTime = sleepyTime; if (remainingSleepTime < alarmTime) { alarmTime = remainingSleepTime; } continue; // we don't have to walk yet... } // Walk the table, get the new alarm and sleepy times. if (table != null) { table.ageStore(); alarmTime = sleepyTime; } } } } /** * Age the service store. */ // this method cannot be private... due to compiler weakness void ageStore() { try { // We synchronize in case somebody registers and tries to // change sleepy time. synchronized (store) { Vector deleted = new Vector(); sleepyTime = store.ageOut(deleted); // Track unregistered services. int i, n = deleted.size(); for (i = 0; i < n; i++) { ServiceStore.ServiceRecord rec = (ServiceStore.ServiceRecord)deleted.elementAt(i); ServiceURL surl = rec.getServiceURL(); trackRegisteredServiceTypes(); // it's deleted... } } } catch (RuntimeException ex) { reportNonfatalException(ex, new Vector(), store); } catch (ServiceLocationException ex) { reportNonfatalException(ex, new Vector(), store); } } // // SLP Service Table operations (register, deregister, etc.) // /** * Process the registration and record if no errors found. * * @param req Service registration request message. * @return SrvLocMsg A service registration acknowledgement. */ SrvLocMsg register(SSrvReg req) { SrvLocHeader hdr = req.getHeader(); Locale locale = hdr.locale; boolean fresh = hdr.fresh; Vector scopes = hdr.scopes; ServiceURL surl = req.URL; String serviceType = req.serviceType; Vector attrList = req.attrList; Hashtable urlSig = req.URLSignature; Hashtable attrSig = req.attrSignature; short errorCode = (fresh ? ServiceLocationException.INVALID_REGISTRATION : ServiceLocationException.INVALID_UPDATE); try { // If a sig block came in, verify it. if (urlSig != null) { AuthBlock.verifyAll(urlSig); } if (attrSig != null) { AuthBlock.verifyAll(attrSig); } // Check whether the URL has a zero lifetime. If so, it // isn't cached. if (surl.getLifetime() <= 0) { throw new ServiceLocationException(errorCode, "st_zero", new Object[0]); } // Check if the service type is restricted. If so, nobody outside // this process is allowed to register it. checkForRestrictedType(surl.getServiceType()); // Check that attribute signature bit on implies URL signature // bit on. if (attrSig != null && urlSig == null) { throw new ServiceLocationException(errorCode, "st_attr_sig", new Object[0]); } // If a signature and the fresh bit was not set, error since signed // registrations don't allow updating. if (urlSig != null && !fresh) { throw new ServiceLocationException( ServiceLocationException.INVALID_UPDATE, "st_prot_update", new Object[0]); } // Check if scopes are supported. if (!areSupportedScopes(scopes)) { throw new ServiceLocationException( ServiceLocationException.SCOPE_NOT_SUPPORTED, "st_scope_unsup", new Object[0]); } // Check if the reg is signed and auth is off or vice versa. // Check is really simple. If security is on, then all regs // to this DA/SA server must be signed, so toss out any regs // that aren't, and vice versa. if (conf.getHasSecurity() && (urlSig == null || attrSig == null)) { throw new ServiceLocationException( ServiceLocationException.AUTHENTICATION_FAILED, "st_unprot_non_reg", new Object[0]); } else if (!conf.getHasSecurity() && (urlSig != null || attrSig != null)) { throw new ServiceLocationException( ServiceLocationException.INVALID_REGISTRATION, "st_prot_non_reg", new Object[0]); } // Merge any duplicates. Vector attrs = new Vector(); Hashtable attrHash = new Hashtable(); int i, n = attrList.size(); for (i = 0; i < n; i++) { ServiceLocationAttribute attr = (ServiceLocationAttribute)attrList.elementAt(i); ServiceLocationAttribute.mergeDuplicateAttributes( attr, attrHash, attrs, false); } // Store register or update. boolean existing = false; if (fresh) { existing = store.register(surl, attrs, scopes, locale, urlSig, attrSig); // Track registred service types in case we get a // SAAdvert solicatation. trackRegisteredServiceTypes(); } else { store.updateRegistration(surl, attrs, scopes, locale); } // Create the reply. SrvLocMsg ack = req.makeReply(existing); if (conf.regTest()) { conf.writeLog((fresh ? "st_reg_add":"st_reg_update"), new Object[] { locationMsg, locale, serviceType, surl, attrs, scopes}); } if (conf.traceAll()) { conf.writeLog("st_dump", new Object[] {locationMsg}); store.dumpServiceStore(); } // Calculate time increment until next update. This is used // to adjust the sleep interval in the ager thread. long sTime = getSleepIncrement(surl); // We synchronize in case the ager thread is in the middle // of trying to set the time. synchronized (store) { // If we need to wake up sooner, adjust the sleep time. if (sTime < sleepyTime) { sleepyTime = sTime; // Interrupt the thread so we go back to // sleep for the right amount of time. thrAger.interrupt(); } } return ack; } catch (ServiceLocationException ex) { if (conf.traceDrop()) { conf.writeLog("st_reg_drop", new Object[] { locationMsg, ex.getMessage()+"("+ex.getErrorCode()+")", locale, serviceType, surl, attrList, scopes}); } return hdr.makeErrorReply(ex); } catch (RuntimeException ex) { // These exceptions are not declared in throws but can occur // anywhere. Vector args = new Vector(); args.addElement(req); reportNonfatalException(ex, args, store); return hdr.makeErrorReply(ex); } } /** * Process the deregistration and return the result in a reply. * * @param req Service deregistration request message. * @return SrvLocMsg A service registration acknowledgement. */ SrvLocMsg deregister(SSrvDereg req) { // We need to determine whether this is an attribute deregistration // or a deregistration of the entire URL. SrvLocHeader hdr = req.getHeader(); Locale locale = hdr.locale; Vector scopes = hdr.scopes; ServiceURL surl = req.URL; Hashtable urlSig = req.URLSignature; Vector tags = req.tags; short errorCode = ServiceLocationException.OK; try { // Verify if signature is nonnull. if (urlSig != null) { AuthBlock.verifyAll(urlSig); } // Check if the service type is restricted. If so, nobody outside // this process is allowed to register it. checkForRestrictedType(surl.getServiceType()); // Error if there's a signature and attempt at deleting attributes. if ((urlSig != null) && (tags != null)) { throw new ServiceLocationException( ServiceLocationException.AUTHENTICATION_FAILED, "st_prot_attr_dereg", new Object[0]); } // Check if scope is protected and auth is off or vice versa. // Check is really simple. If security is on, then all scopes // in this DA/SA server are protected, so toss out any regs // that aren't, and vice versa. if (conf.getHasSecurity() && urlSig == null) { throw new ServiceLocationException( ServiceLocationException.AUTHENTICATION_FAILED, "st_unprot_non_dereg", new Object[0]); } else if (!conf.getHasSecurity() && urlSig != null) { throw new ServiceLocationException( ServiceLocationException.INVALID_REGISTRATION, "st_prot_non_dereg", new Object[0]); } // If it's a service URL, then deregister the URL. if (tags == null) { store.deregister(surl, scopes, urlSig); // Track registred service types in case we get a // SAAdvert solicatation. trackRegisteredServiceTypes(); } else { // Just delete the attributes. store.deleteAttributes(surl, scopes, tags, locale); } // Create the reply. SrvLocMsg ack = req.makeReply(); if (conf.regTest()) { conf.writeLog((tags == null ? "st_dereg":"st_delattr"), new Object[] { locationMsg, locale, surl.getServiceType(), surl, tags}); } if (conf.traceAll()) { conf.writeLog("st_dump", new Object[] {locationMsg}); store.dumpServiceStore(); } return ack; } catch (ServiceLocationException ex) { if (conf.traceDrop()) { conf.writeLog((tags == null ? "st_dereg_drop" : "st_dereg_attr_drop"), new Object[] { locationMsg, ex.getMessage()+"("+ex.getErrorCode()+")", locale, surl.getServiceType(), surl, tags}); } return hdr.makeErrorReply(ex); } catch (RuntimeException ex) { // These exceptions are not declared in throws but can occur // anywhere. Vector args = new Vector(); args.addElement(req); reportNonfatalException(ex, args, store); return hdr.makeErrorReply(ex); } } /** * Process the service type request and return the result in a reply. * * @param req Service type request message. * @return SrvTypeRply A service type reply. */ SrvLocMsg findServiceTypes(SSrvTypeMsg req) { SrvLocHeader hdr = req.getHeader(); Vector scopes = hdr.scopes; String namingAuthority = req.namingAuthority; short errorCode = ServiceLocationException.OK; try { // Check whether the scope is supported. if (!areSupportedScopes(scopes)) { throw new ServiceLocationException( ServiceLocationException.SCOPE_NOT_SUPPORTED, "st_scope_unsup", new Object[0]); } // Get the vector of service types in the store, independent // of language. Vector types = store.findServiceTypes(namingAuthority, scopes); // Create the reply. SrvLocMsg ack = req.makeReply(types); if (conf.traceAll()) { conf.writeLog("st_stypes", new Object[] { locationMsg, namingAuthority, scopes, types}); } return ack; } catch (ServiceLocationException ex) { if (conf.traceDrop()) { conf.writeLog("st_stypes_drop", new Object[] { locationMsg, ex.getMessage()+"("+ex.getErrorCode()+")", namingAuthority, scopes, hdr.locale}); } return hdr.makeErrorReply(ex); } catch (RuntimeException ex) { // These exceptions are not declared in throws but can occur // anywhere. Vector args = new Vector(); args.addElement(req); reportNonfatalException(ex, args, store); return hdr.makeErrorReply(ex); } } /** * Process the service request and return the result in a reply. * * @param req Service request message. * @return SrvRply A service reply. */ SrvLocMsg findServices(SSrvMsg req) { SrvLocHeader hdr = req.getHeader(); Locale locale = hdr.locale; Vector scopes = hdr.scopes; String serviceType = req.serviceType; String query = req.query; short errorCode = ServiceLocationException.OK; try { // Check whether the scope is supported. if (!areSupportedScopes(scopes)) { throw new ServiceLocationException( ServiceLocationException.SCOPE_NOT_SUPPORTED, "st_scope_unsup", new Object[0]); } // Get the hashtable of returns. Hashtable returns = store.findServices(serviceType, scopes, query, locale); // Get the hashtable of services v.s. scopes, and signatures, if // any. Hashtable services = (Hashtable)returns.get(ServiceStore.FS_SERVICES); Hashtable signatures = (Hashtable)returns.get(ServiceStore.FS_SIGTABLE); boolean hasSignatures = (signatures != null); // for each candidate URL, make sure it has the requested SPI // (if any) if (hasSignatures && !req.spi.equals("")) { Enumeration allSurls = services.keys(); while (allSurls.hasMoreElements()) { Object aSurl = allSurls.nextElement(); Hashtable auths = (Hashtable) signatures.get(aSurl); AuthBlock auth = AuthBlock.getEquivalentAuth(req.spi, auths); if (auth == null) { // doesn't have the requested SPI services.remove(aSurl); } } } // Create return message. SrvLocMsg ack = req.makeReply(services, signatures); if (conf.traceAll()) { conf.writeLog("st_sreq", new Object[] { locationMsg, serviceType, scopes, query, locale, services, signatures}); } return ack; } catch (ServiceLocationException ex) { if (conf.traceDrop()) { conf.writeLog("st_sreq_drop", new Object[] { locationMsg, ex.getMessage()+"("+ex.getErrorCode()+")", serviceType, scopes, query, locale}); } return hdr.makeErrorReply(ex); } catch (RuntimeException ex) { // These exceptions are not declared in throws but can occur // anywhere. Vector args = new Vector(); args.addElement(req); reportNonfatalException(ex, args, store); return hdr.makeErrorReply(ex); } } /** * Process the attribute request and return the result in a reply. * * @param req Attribute request message. * @return AttrRply An attribute reply. */ SrvLocMsg findAttributes(SAttrMsg req) { // We need to determine whether this is a request for attributes // on a specific URL or for an entire service type. SrvLocHeader hdr = req.getHeader(); Vector scopes = hdr.scopes; Locale locale = hdr.locale; ServiceURL surl = req.URL; String serviceType = req.serviceType; Vector tags = req.tags; short errorCode = ServiceLocationException.OK; try { // Check whether the scope is supported. if (!areSupportedScopes(scopes)) { throw new ServiceLocationException( ServiceLocationException.SCOPE_NOT_SUPPORTED, "st_scope_unsup", new Object[0]); } Vector attributes = null; Hashtable sig = null; // If it's a service URL, then get the attributes just for // that URL. if (serviceType == null) { // If the attrs are signed, then error if any tags, since // we must ask for *all* attributes in for a signed reg if (!req.spi.equals("") && tags.size() > 0) { throw new ServiceLocationException( ServiceLocationException.AUTHENTICATION_FAILED, "st_par_attr", new Object[0]); } Hashtable ht = store.findAttributes(surl, scopes, tags, locale); // Get the attributes and signatures. attributes = (Vector)ht.get(ServiceStore.FA_ATTRIBUTES); sig = (Hashtable)ht.get(ServiceStore.FA_SIG); // make sure the attr has the requested SPI (if any) if (sig != null && !req.spi.equals("")) { AuthBlock auth = AuthBlock.getEquivalentAuth(req.spi, sig); if (auth == null) { // return empty attributes = new Vector(); } } } else { if (!req.spi.equals("")) { throw new ServiceLocationException( ServiceLocationException.AUTHENTICATION_FAILED, "st_par_attr", new Object[0]); } // Otherwise find the attributes for all service types. attributes = store.findAttributes(serviceType, scopes, tags, locale); } ServiceType type = (serviceType == null ? surl.getServiceType(): new ServiceType(serviceType)); // Create the reply. SrvLocMsg ack = req.makeReply(attributes, sig); if (conf.traceAll()) { conf.writeLog((serviceType != null ? "st_st_attr" : "st_url_attr"), new Object[] { locationMsg, (serviceType != null ? serviceType.toString() : surl.toString()), scopes, tags, locale, attributes}); } return ack; } catch (ServiceLocationException ex) { if (conf.traceDrop()) { conf.writeLog((serviceType != null ? "st_st_attr_drop": "st_url_attr_drop"), new Object[] { locationMsg, ex.getMessage()+"("+ex.getErrorCode()+")", (serviceType != null ? serviceType.toString() : surl.toString()), scopes, tags, locale}); } return hdr.makeErrorReply(ex); } catch (RuntimeException ex) { // These exceptions are not declared in throws but can occur // anywhere. Vector args = new Vector(); args.addElement(req); reportNonfatalException(ex, args, store); return hdr.makeErrorReply(ex); } } // Return the service record corresponding to the URL. ServiceStore.ServiceRecord getServiceRecord(ServiceURL URL, Locale locale) { return store.getServiceRecord(URL, locale); } // // Utility methods. // // // Protected/private methods. // // Check whether the type is restricted, through an exception if so. private void checkForRestrictedType(ServiceType type) throws ServiceLocationException { if (Defaults.restrictedTypes.contains(type)) { throw new ServiceLocationException( ServiceLocationException.INVALID_REGISTRATION, "st_restricted_type", new Object[] {type}); } } // Insert a record for type "service-agent" with attributes having // the types currently supported, if the new URL is not on the // list of supported types. This allows us to perform queries // for supported service types. private void trackRegisteredServiceTypes() throws ServiceLocationException { // First find the types. Vector types = store.findServiceTypes(Defaults.ALL_AUTHORITIES, conf.getSAConfiguredScopes()); // Get preconfigured attributes. Vector attrs = conf.getSAAttributes(); // Make an attribute with the service types. ServiceLocationAttribute attr = new ServiceLocationAttribute(Defaults.SERVICE_TYPE_ATTR_ID, types); attrs.addElement(attr); // Construct URL to use on all interfaces. Vector interfaces = conf.getInterfaces(); int i, n = interfaces.size(); for (i = 0; i < n; i++) { InetAddress addr = (InetAddress)interfaces.elementAt(i); ServiceURL url = new ServiceURL(Defaults.SUN_SA_SERVICE_TYPE + "://" + addr.getHostAddress(), ServiceURL.LIFETIME_MAXIMUM); Vector scopes = conf.getSAOnlyScopes(); Locale locale = Defaults.locale; // Make a new registration for this SA. store.register(url, attrs, scopes, locale, null, null); // we could sign, but we do that later... } // Note that we don't need a refresh on the URLs because they // will get refreshed when the service URLs that they track // are refreshed. If the tracked URLs aren't refreshed, then // these will get updated when the tracked URLs age out. } // Return true if the scopes in the vector are supported by the DA // or SA server. final private boolean areSupportedScopes(Vector scopes) { Vector configuredScopes = conf.getSAConfiguredScopes(); Vector saOnlyScopes = conf.getSAOnlyScopes(); int i = 0; while (i < scopes.size()) { Object o = scopes.elementAt(i); // Remove it if we don't support it. if (!configuredScopes.contains(o) && !saOnlyScopes.contains(o)) { // This will shift the Vector's elements down one, so // don't increment i scopes.removeElementAt(i); } else { i++; } } if (scopes.size() <= 0) { return false; } return true; } /** * Return the sleep increment from the URL lifetime. Used by the * ServiceStore to calculate the new sleep interval in addition * to this class, when a new URL comes in. The algorithm * subtracts x% of the lifetime from the lifetime and schedules the * timeout at that time. * * @param url The URL to use for calculation. * @return The sleep interval. */ private long getSleepIncrement(ServiceURL url) { long urlLifetime = (long)(url.getLifetime() * 1000); long increment = (long)((float)urlLifetime * Defaults.fRefreshGranularity); long sTime = urlLifetime - increment; // If URL lives only one second, update every half second. if (sTime <= 0) { sTime = 500; } return sTime; } // Make a DAADvert for the DA service request. This only applies // to DAs, not to SA servers. SrvLocMsg makeDAAdvert(SSrvMsg rqst, InetAddress daAddr, SLPConfig conf) { SrvLocHeader hdr = rqst.getHeader(); Vector scopes = hdr.scopes; short xid = hdr.xid; String query = rqst.query; try { // If security is on, proceed only if we can sign as rqst.spi if (conf.getHasSecurity() && !AuthBlock.canSignAs(rqst.spi)) { throw new ServiceLocationException( ServiceLocationException.AUTHENTICATION_UNKNOWN, "st_cant_sign_as", new Object[] {rqst.spi}); } // Get the hashtable of service URLs v.s. scopes. Hashtable services = ServerDATable.getServerDATable().returnMatchingDAs(query); // Go through the table checking whether the IP address came back. Enumeration urls = services.keys(); boolean foundIt = false; String strDAAddr = daAddr.getHostAddress(); while (urls.hasMoreElements()) { ServiceURL url = (ServiceURL)urls.nextElement(); if (url.getHost().equals(strDAAddr)) { foundIt = true; break; } } // If we didn't find anything, make a null service reply. if (!foundIt) { return rqst.makeReply(new Hashtable(), new Hashtable()); } return makeDAAdvert(hdr, daAddr, xid, scopes, conf); } catch (ServiceLocationException ex) { return hdr.makeErrorReply(ex); } } // Make a DAAdvert from the input arguments. SrvLocMsg makeDAAdvert(SrvLocHeader hdr, InetAddress daAddr, short xid, Vector scopes, SLPConfig config) throws ServiceLocationException { // If this is a request for a V1 Advert, truncate the scopes vector // since DA solicitations in V1 are always unscoped if (hdr.version == 1) { scopes = new Vector(); } // Check if we support scopes first. If not, return an // error reply unless the scope vector is zero. Upper layers // must sort out whether this is a unicast or multicast. if (scopes.size() > 0 && !areSupportedScopes(scopes)) { throw new ServiceLocationException( ServiceLocationException.SCOPE_NOT_SUPPORTED, "st_scope_unsup", new Object[0]); } // Get the service store's timestamp. This must be the // time since last stateless reboot for a stateful store, // or the current time. long timestamp = store.getStateTimestamp(); ServiceURL url = new ServiceURL(Defaults.DA_SERVICE_TYPE + "://" + daAddr.getHostAddress(), ServiceURL.LIFETIME_DEFAULT); SDAAdvert advert = hdr.getDAAdvert(xid, timestamp, url, scopes, conf.getDAAttributes()); return advert; } // Make a SAADvert for the SA service request. This only applies // to SA servers, not DA's. Note that we only advertise the "public" // scopes, not the private ones. SSAAdvert makeSAAdvert(SSrvMsg rqst, InetAddress interfac, SLPConfig conf) throws ServiceLocationException { SrvLocHeader hdr = rqst.getHeader(); int version = hdr.version; short xid = hdr.xid; Locale locale = hdr.locale; Vector scopes = hdr.scopes; String query = rqst.query; String serviceType = rqst.serviceType; Vector saOnlyScopes = conf.getSAOnlyScopes(); // If security is on, proceed only if we can sign as rqst.spi if (conf.getHasSecurity() && !AuthBlock.canSignAs(rqst.spi)) { throw new ServiceLocationException( ServiceLocationException.AUTHENTICATION_UNKNOWN, "st_cant_sign_as", new Object[] {rqst.spi}); } // Check if we support scopes first. Note that this may allow // someone to get at the SA only scopes off machine, but that's // OK. Since the SAAdvert is only ever multicast, this is OK. if (!areSupportedScopes(scopes) && !(scopes.size() <= 0)) { return null; } // If the scopes vector is null, then use all configured scopes. if (scopes.size() <= 0) { scopes = (Vector)conf.getSAConfiguredScopes().clone(); } // Check to be sure the query matches. // If it doesn't, we don't need to return anything. Hashtable returns = store.findServices(Defaults.SUN_SA_SERVICE_TYPE.toString(), saOnlyScopes, query, Defaults.locale); Hashtable services = (Hashtable)returns.get(ServiceStore.FS_SERVICES); Enumeration en = services.keys(); // Indicates we don't support the service type. if (!en.hasMoreElements()) { return null; } // Find the URL to use. The interface on which the message came in // needs to match one of the registered URLs. ServiceURL url = null; ServiceURL surl = null; String addr = interfac.getHostAddress(); while (en.hasMoreElements()) { surl = (ServiceURL)en.nextElement(); if (addr.equals(surl.getHost())) { url = new ServiceURL(Defaults.SA_SERVICE_TYPE + "://" + addr, ServiceURL.LIFETIME_DEFAULT); break; } } // If none of the URLs matched this interface, then return null. if (url == null) { return null; } // Find the SA's attributes. Hashtable ht = store.findAttributes(surl, saOnlyScopes, new Vector(), Defaults.locale); Vector attrs = (Vector)ht.get(ServiceStore.FA_ATTRIBUTES); // Construct return. return new SSAAdvert(version, xid, locale, url, conf.getSAConfiguredScopes(), // report all scopes... attrs); } /** * Report a fatal exception to the log. * * @param ex The exception to report. */ protected static void reportFatalException(Exception ex) { reportException(true, ex, new Vector()); if (table != null) { table.store.dumpServiceStore(); } conf.writeLog("exiting_msg", new Object[0]); System.exit(1); } /** * Report a nonfatal exception to the log. * * @param ex The exception to report. * @param args The method arguments. * @param store The service store being processed. */ protected static void reportNonfatalException(Exception ex, Vector args, ServiceStore store) { reportException(false, ex, args); if (conf.traceAll()) { store.dumpServiceStore(); } } /** * Report an exception to the log. * * @param isFatal Indicates whether the exception is fatal or not. * @param ex The exception to report. * @param args A potentially null vector of arguments to the * method where the exception was caught. */ private static void reportException(boolean isFatal, Exception ex, Vector args) { StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); // Get the backtrace. ex.printStackTrace(writer); String severity = (isFatal ? "fatal_error":"nonfatal_error"); String msg = ex.getMessage(); if (msg == null) { msg = conf.formatMessage("no_message", new Object[0]); } else if (ex instanceof ServiceLocationException) { msg = msg + "(" + ((ServiceLocationException)ex).getErrorCode() + ")"; } StringBuffer argMsg = new StringBuffer(); int i, n = args.size(); for (i = 0; i < n; i++) { argMsg.append("\n (" + Integer.toString(i) + "):" + args.elementAt(i).toString()); } conf.writeLog(severity, new Object[] { ex.getClass().getName(), msg, argMsg, sw.toString()}); } }