xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/ServiceTable.java (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * ident	"%Z%%M%	%I%	%E% SMI"
24  *
25  * Copyright (c) 1999 by Sun Microsystems, Inc.
26  * All rights reserved.
27  *
28  */
29 
30 //  SCCS Status:      @(#)ServiceTable.java	2.7	11/20/97
31 //  ServiceTable.java: Storage of all services.
32 //  Author:           James Kempf
33 //  Created On:       Fri Oct 10 14:23:25 1997
34 //  Last Modified By: James Kempf
35 //  Last Modified On: Thu Apr  1 10:33:46 1999
36 //  Update Count:     461
37 //
38 
39 package com.sun.slp;
40 
41 import java.util.*;
42 import java.io.*;
43 import java.security.*;
44 import java.net.*;
45 
46 /**
47  * The ServiceTable object records all service registrations. Note
48  * that any exceptions internal to the service table are processed
49  * and either returned as SrvRply objects or are reported.
50  *
51  * @version 2.7 97/11/20
52  * @author James Kempf
53  */
54 
55 class ServiceTable extends Object {
56 
57     // Key for SDAAdvert class.
58 
59     static final String SDAADVERT = "com.sun.slp.SDAAdvert";
60 
61     private static final String locationMsg = "Service table";
62 
63     //
64     // Instance variables.
65     //
66 
67     // The service store.
68 
69     protected ServiceStore store = null;
70 
71     //
72     // Class variables.
73 
74     // System properties.
75 
76     static protected SLPConfig conf = null;
77 
78     // Singleton objects for the service tables.
79 
80     static protected ServiceTable table = null;
81 
82     // The ager thread.
83 
84     static protected AgerThread thrAger = null;
85 
86     // Time to sleep. Adjusted depending on incoming URLs.
87 
88     private static long sleepyTime = Defaults.lMaxSleepTime;
89 
90     //
91     // Creation of singleton.
92     //
93 
94     // Protected constructor.
95 
96     protected ServiceTable() {
97 
98 	if (thrAger != null) {
99 	    return;
100 
101 	}
102 
103 	// Create the ager thread.
104 
105 	thrAger = new AgerThread();
106 
107 	// Set the priority low, so other things (like active discovery)
108 	//  take priority.
109 
110 	thrAger.setPriority(Thread.MIN_PRIORITY);
111 
112 	thrAger.start();
113 
114     }
115 
116     /**
117      * Return an SA service store.
118      *
119      * @return The distinguished table object.
120      */
121 
122     static ServiceTable getServiceTable()
123 	throws ServiceLocationException {
124 
125 	if (conf == null) {
126 	    conf = SLPConfig.getSLPConfig();
127 
128 	}
129 
130 	if (table == null) {
131 
132 	    table = createServiceTable();
133 
134 	}
135 
136 	return table;
137     }
138 
139     /**
140      * Return a service table object.
141      *
142      * @return The service table object.
143      */
144 
145     private static ServiceTable createServiceTable()
146 	throws ServiceLocationException {
147 
148 	ServiceTable table = new ServiceTable();
149 
150 	table.store = ServiceStoreFactory.createServiceStore();
151 
152 	return table;
153     }
154 
155     //
156     // Support for serializated registrations.
157     //
158 
159     /**
160      * If any serialized registrations are pending, then unserialize
161      * and register.
162      */
163 
164     public void deserializeTable() {
165 
166 	// If there are any serialized registrations, then get
167 	//  them and perform registrations.
168 
169 	String serializedURL = conf.getSerializedRegURL();
170 
171 	if (serializedURL != null) {
172 
173 	    ServiceStore serStore = getStoreFromURL(serializedURL);
174 
175 	    if (serStore != null) {
176 		registerStore(serStore);
177 	    }
178 	}
179     }
180 
181     /**
182      * Serialize the table to the URL.
183      *
184      * @param URL String giving the URL to which the store should be
185      * serialized.
186      */
187 
188     void serializeServiceStore(String URL) {
189 
190 	// Open an object output stream for the URL, serialize through
191 	//  the factory.
192 
193 	try {
194 
195 	    URL url = new URL(URL);
196 	    URLConnection urlConn = url.openConnection();
197 	    OutputStream os = urlConn.getOutputStream();
198 	    BufferedWriter di =
199 		new BufferedWriter(new OutputStreamWriter(os));
200 
201 	    // Serialize the store.
202 
203 	    ServiceStoreFactory.serialize(di, store);
204 
205 	} catch (MalformedURLException ex) {
206 
207 	    conf.writeLog("st_serialized_malform",
208 			  new Object[] {URL});
209 
210 	} catch (UnsupportedEncodingException ex) {
211 
212 	    conf.writeLog("st_unsupported_encoding",
213 			  new Object[] {URL});
214 
215 	} catch (IOException ex) {
216 
217 	    conf.writeLog("st_serialized_ioexception",
218 			  new Object[] {URL, ex});
219 
220 	} catch (ServiceLocationException ex) {
221 
222 	    conf.writeLog("st_serialized_sle",
223 			  new Object[] {URL, ex.getMessage()});
224 
225 	}
226 
227     }
228 
229     // Read proxy registrations from the URL.
230 
231     private ServiceStore getStoreFromURL(String serializedURL) {
232 
233 	ServiceStore serStore = null;
234 
235 	// Open an object input stream for the URL, deserialize through
236 	//  the factory.
237 
238 	try {
239 
240 	    URL url = new URL(serializedURL);
241 	    InputStream is = url.openStream();
242 	    BufferedReader di = new BufferedReader(new InputStreamReader(is));
243 
244 	    // Deserialize the objects.
245 
246 	    serStore =
247 		ServiceStoreFactory.deserializeServiceStore(di);
248 
249 	} catch (MalformedURLException ex) {
250 
251 	    conf.writeLog("st_serialized_malform",
252 			  new Object[] {serializedURL});
253 
254 	} catch (UnsupportedEncodingException ex) {
255 
256 	    conf.writeLog("st_unsupported_encoding",
257 			  new Object[] {serializedURL});
258 
259 	} catch (IOException ex) {
260 
261 	    conf.writeLog("st_serialized_ioexception",
262 			  new Object[] {
263 		serializedURL,
264 		    ex.getMessage()});
265 
266 	} catch (ServiceLocationException ex) {
267 
268 	    conf.writeLog("st_serialized_sle",
269 			  new Object[] {
270 		serializedURL,
271 		    ex.getMessage()});
272 
273 	}
274 
275 	return serStore;
276     }
277 
278     // Walk the table, performing actual registrations on all records.
279 
280     private void registerStore(ServiceStore serStore) {
281 
282 	// Walk the table.
283 
284 	Enumeration en = serStore.getServiceRecordsByScope(null);
285 	boolean hasURLSig = conf.getHasSecurity();
286 	boolean hasAttrSig = conf.getHasSecurity();
287 	PermSARegTable pregTable = 	SARequester.getPermSARegTable();
288 
289 	while (en.hasMoreElements()) {
290 	    ServiceStore.ServiceRecord rec =
291 		(ServiceStore.ServiceRecord)en.nextElement();
292 	    ServiceURL surl = rec.getServiceURL();
293 	    Vector scopes = rec.getScopes();
294 	    Vector attrs = rec.getAttrList();
295 	    Locale locale = rec.getLocale();
296 	    Hashtable urlSig = null;
297 	    Hashtable attrSig = null;
298 
299 	    // Note that we can't use the Advertiser to register here,
300 	    //  because we may not be listening yet for registrations.
301 	    //  We need to do this all by hand.
302 
303 	    try {
304 
305 		// Create a registration message for refreshing.
306 
307 		CSrvReg creg = new CSrvReg(false,
308 					   locale,
309 					   surl,
310 					   scopes,
311 					   attrs,
312 					   null,
313 					   null);
314 
315 		// We externalize to a server side message if authentication
316 		//  is needed. This creates the auth blocks for the scopes.
317 		//  Doing this in any other way is alot more complicated,
318 		//  although doing it this way seems kludgy.
319 
320 		if (hasURLSig || hasAttrSig) {
321 		    ByteArrayOutputStream baos = new ByteArrayOutputStream();
322 
323 		    creg.getHeader().externalize(baos, false, true);
324 
325 		    ByteArrayInputStream bais =
326 			new ByteArrayInputStream(baos.toByteArray());
327 		    bais.read();	// pop off version and function code...
328 		    bais.read();
329 		    DataInputStream dis = new DataInputStream(bais);
330 		    SLPHeaderV2 hdr = new SLPHeaderV2();
331 		    hdr.parseHeader(SrvLocHeader.SrvReg, dis);
332 		    SSrvReg sreg = new SSrvReg(hdr, dis);
333 
334 		    // Now we've got it, after much effort. Get the auths.
335 
336 		    urlSig = sreg.URLSignature;
337 		    attrSig = sreg.attrSignature;
338 
339 		}
340 
341 		store.register(surl, attrs, scopes, locale, urlSig, attrSig);
342 
343 		// Now we've got to put the registration into the
344 		//  PermSARegTable. Again, we do everything by hand
345 		//  because we can't use Advertiser.
346 
347 		if (surl.getIsPermanent()) {
348 		    pregTable.reg(surl, creg);
349 
350 		}
351 
352 		// Report registration.
353 
354 		if (conf.regTest()) {
355 		    conf.writeLog("st_reg_add",
356 				  new Object[] {
357 			locationMsg,
358 			    locale,
359 			    surl.getServiceType(),
360 			    surl,
361 			    attrs,
362 			    scopes});
363 
364 		}
365 	    } catch (ServiceLocationException ex) {
366 
367 		String msg = ex.getMessage();
368 
369 		conf.writeLog("st_serialized_seex",
370 			      new Object[] {
371 		    new Integer(ex.getErrorCode()),
372 			surl,
373 			(msg == null ? "<no message>":msg)});
374 
375 	    } catch (Exception ex) {
376 
377 		String msg = ex.getMessage();
378 
379 		conf.writeLog("st_serialized_seex",
380 			      new Object[] {
381 		    surl,
382 			(msg == null ? "<no message>":msg)});
383 	    }
384 	}
385     }
386 
387     //
388     // Record aging.
389     //
390 
391     //
392     // Run the thread that ages out records.
393     //
394 
395     private class AgerThread extends Thread {
396 
397 	public void run() {
398 
399 	    setName("SLP Service Table Age-out");
400 	    long alarmTime = sleepyTime;  // when to wake up next
401 	    long wentToSleep = 0;	    // what time we went to bed
402 
403 	    while (true) {
404 
405 		try {
406 
407 		    // Record when we went to sleep.
408 
409 		    wentToSleep = System.currentTimeMillis();
410 
411 		    // Sleep for the minimum amount of time needed before we
412 		    //  must wake up and check.
413 
414 		    sleep(alarmTime);
415 
416 		} catch (InterruptedException ie) {
417 
418 		    // A new registration came in. Calculate how much time
419 		    //  remains until we would have woken up. If this is
420 		    //  less than the new sleepyTime, then we set the alarm
421 		    //  for this time. If it is more, then we set the alarm
422 		    //  for the new sleepyTime.
423 
424 		    long remainingSleepTime =
425 			(wentToSleep + alarmTime) - System.currentTimeMillis();
426 
427 		    remainingSleepTime =		// just in case...
428 			((remainingSleepTime <= 0) ? 0 : remainingSleepTime);
429 
430 		    alarmTime = sleepyTime;
431 
432 		    if (remainingSleepTime < alarmTime) {
433 			alarmTime = remainingSleepTime;
434 
435 		    }
436 
437 		    continue;  // we don't have to walk yet...
438 
439 		}
440 
441 		// Walk the table, get the new alarm and sleepy times.
442 
443 		if (table != null) {
444 		    table.ageStore();
445 
446 		    alarmTime = sleepyTime;
447 
448 		}
449 	    }
450 	}
451 
452     }
453 
454     /**
455      * Age the service store.
456      */
457 
458     // this method cannot be private... due to compiler weakness
459     void ageStore() {
460 
461 	try {
462 
463 	    // We synchronize in case somebody registers and tries to
464 	    //  change sleepy time.
465 
466 	    synchronized (store) {
467 		Vector deleted = new Vector();
468 
469 		sleepyTime = store.ageOut(deleted);
470 
471 		// Track unregistered services.
472 
473 		int i, n = deleted.size();
474 
475 		for (i = 0; i < n; i++) {
476 		    ServiceStore.ServiceRecord rec =
477 			(ServiceStore.ServiceRecord)deleted.elementAt(i);
478 		    ServiceURL surl = rec.getServiceURL();
479 
480 		    trackRegisteredServiceTypes(); // it's deleted...
481 
482 		}
483 
484 	    }
485 
486 	} catch (RuntimeException ex) {
487 
488 	    reportNonfatalException(ex, new Vector(), store);
489 
490 	} catch (ServiceLocationException ex) {
491 
492 	    reportNonfatalException(ex, new Vector(), store);
493 
494 	}
495 
496     }
497 
498     //
499     // SLP Service Table operations (register, deregister, etc.)
500     //
501 
502     /**
503      * Process the registration and record if no errors found.
504      *
505      * @param req Service registration request message.
506      * @return SrvLocMsg A service registration acknowledgement.
507      */
508 
509     SrvLocMsg register(SSrvReg req) {
510 
511 	SrvLocHeader hdr = req.getHeader();
512 	Locale locale = hdr.locale;
513 	boolean fresh = hdr.fresh;
514 	Vector scopes = hdr.scopes;
515 	ServiceURL surl = req.URL;
516 	String serviceType = req.serviceType;
517 	Vector attrList = req.attrList;
518 	Hashtable urlSig = req.URLSignature;
519 	Hashtable attrSig = req.attrSignature;
520 	short errorCode =
521 	    (fresh ? ServiceLocationException.INVALID_REGISTRATION :
522 	    ServiceLocationException.INVALID_UPDATE);
523 
524 	try {
525 
526 	    // If a sig block came in, verify it.
527 
528 	    if (urlSig != null) {
529 
530 		AuthBlock.verifyAll(urlSig);
531 	    }
532 
533 	    if (attrSig != null) {
534 
535 		AuthBlock.verifyAll(attrSig);
536 
537 	    }
538 
539 	    // Check whether the URL has a zero lifetime. If so, it
540 	    // isn't cached.
541 
542 	    if (surl.getLifetime() <= 0) {
543 		throw
544 		    new ServiceLocationException(errorCode,
545 						 "st_zero",
546 						 new Object[0]);
547 
548 	    }
549 
550 	    // Check if the service type is restricted. If so, nobody outside
551 	    //  this process is allowed to register it.
552 
553 	    checkForRestrictedType(surl.getServiceType());
554 
555 	    // Check that attribute signature bit on implies URL signature
556 	    //  bit on.
557 
558 	    if (attrSig != null && urlSig == null) {
559 		throw
560 		    new ServiceLocationException(errorCode,
561 						 "st_attr_sig",
562 						 new Object[0]);
563 
564 	    }
565 
566 	    // If a signature and the fresh bit was not set, error since signed
567 	    //  registrations don't allow updating.
568 
569 	    if (urlSig != null && !fresh) {
570 		throw
571 		    new ServiceLocationException(
572 				ServiceLocationException.INVALID_UPDATE,
573 				"st_prot_update",
574 				new Object[0]);
575 	    }
576 
577 	    // Check if scopes are supported.
578 
579 	    if (!areSupportedScopes(scopes)) {
580 		throw
581 		    new ServiceLocationException(
582 				ServiceLocationException.SCOPE_NOT_SUPPORTED,
583 				"st_scope_unsup",
584 				new Object[0]);
585 	    }
586 
587 	    // Check if the reg is signed and auth is off or vice versa.
588 	    //  Check is really simple. If security is on, then all regs
589 	    //  to this DA/SA server must be signed, so toss out any regs
590 	    //  that aren't, and vice versa.
591 
592 	    if (conf.getHasSecurity() && (urlSig == null || attrSig == null)) {
593 		throw
594 		    new ServiceLocationException(
595 				ServiceLocationException.AUTHENTICATION_FAILED,
596 				"st_unprot_non_reg",
597 				new Object[0]);
598 
599 	    } else if (!conf.getHasSecurity() &&
600 		       (urlSig != null || attrSig != null)) {
601 		throw
602 		    new ServiceLocationException(
603 				ServiceLocationException.INVALID_REGISTRATION,
604 				"st_prot_non_reg",
605 				new Object[0]);
606 
607 	    }
608 
609 	    // Merge any duplicates.
610 
611 	    Vector attrs = new Vector();
612 	    Hashtable attrHash = new Hashtable();
613 	    int i, n = attrList.size();
614 
615 	    for (i = 0; i < n; i++) {
616 		ServiceLocationAttribute attr =
617 		    (ServiceLocationAttribute)attrList.elementAt(i);
618 
619 		ServiceLocationAttribute.mergeDuplicateAttributes(
620 								  attr,
621 								  attrHash,
622 								  attrs,
623 								  false);
624 	    }
625 
626 	    // Store register or update.
627 
628 	    boolean existing = false;
629 
630 	    if (fresh) {
631 		existing = store.register(surl,
632 					  attrs,
633 					  scopes,
634 					  locale,
635 					  urlSig,
636 					  attrSig);
637 
638 		// Track registred service types in case we get a
639 		// SAAdvert solicatation.
640 
641 		trackRegisteredServiceTypes();
642 
643 	    } else {
644 		store.updateRegistration(surl, attrs, scopes, locale);
645 
646 	    }
647 
648 	    // Create the reply.
649 
650 	    SrvLocMsg ack = req.makeReply(existing);
651 
652 	    if (conf.regTest()) {
653 		conf.writeLog((fresh ? "st_reg_add":"st_reg_update"),
654 			      new Object[] {
655 		    locationMsg,
656 			locale,
657 			serviceType,
658 			surl,
659 			attrs,
660 			scopes});
661 
662 	    }
663 
664 	    if (conf.traceAll()) {
665 		conf.writeLog("st_dump", new Object[] {locationMsg});
666 		store.dumpServiceStore();
667 
668 	    }
669 
670 	    // Calculate time increment until next update. This is used
671 	    //  to adjust the sleep interval in the ager thread.
672 
673 	    long sTime = getSleepIncrement(surl);
674 
675 	    // We synchronize in case the ager thread is in the middle
676 	    //  of trying to set the time.
677 
678 	    synchronized (store) {
679 
680 		// If we need to wake up sooner, adjust the sleep time.
681 
682 		if (sTime < sleepyTime) {
683 
684 		    sleepyTime = sTime;
685 
686 		    // Interrupt the thread so we go back to
687 		    //  sleep for the right amount of time.
688 
689 		    thrAger.interrupt();
690 		}
691 	    }
692 
693 	    return ack;
694 
695 	} catch (ServiceLocationException ex) {
696 
697 	    if (conf.traceDrop()) {
698 		conf.writeLog("st_reg_drop",
699 			      new Object[] {
700 		    locationMsg,
701 			ex.getMessage()+"("+ex.getErrorCode()+")",
702 			locale,
703 			serviceType,
704 			surl,
705 			attrList,
706 			scopes});
707 	    }
708 
709 	    return hdr.makeErrorReply(ex);
710 
711 	} catch (RuntimeException ex) {
712 
713 	    // These exceptions are not declared in throws but can occur
714 	    //  anywhere.
715 
716 	    Vector args = new Vector();
717 
718 	    args.addElement(req);
719 
720 	    reportNonfatalException(ex, args, store);
721 
722 	    return hdr.makeErrorReply(ex);
723 
724 	}
725     }
726 
727     /**
728      * Process the deregistration and return the result in a reply.
729      *
730      * @param req Service deregistration request message.
731      * @return SrvLocMsg A service registration acknowledgement.
732      */
733 
734     SrvLocMsg deregister(SSrvDereg req) {
735 
736 	// We need to determine whether this is an attribute deregistration
737 	//  or a deregistration of the entire URL.
738 
739 	SrvLocHeader hdr = req.getHeader();
740 	Locale locale = hdr.locale;
741 	Vector scopes = hdr.scopes;
742 	ServiceURL surl = req.URL;
743 	Hashtable urlSig = req.URLSignature;
744 	Vector tags = req.tags;
745 	short errorCode = ServiceLocationException.OK;
746 
747 	try {
748 
749 	    // Verify if signature is nonnull.
750 
751 	    if (urlSig != null) {
752 		AuthBlock.verifyAll(urlSig);
753 
754 	    }
755 
756 	    // Check if the service type is restricted. If so, nobody outside
757 	    //  this process is allowed to register it.
758 
759 	    checkForRestrictedType(surl.getServiceType());
760 
761 	    // Error if there's a signature and attempt at deleting attributes.
762 
763 	    if ((urlSig != null) && (tags != null)) {
764 		throw
765 		    new ServiceLocationException(
766 				ServiceLocationException.AUTHENTICATION_FAILED,
767 				"st_prot_attr_dereg",
768 				new Object[0]);
769 	    }
770 
771 	    // Check if scope is protected and auth is off or vice versa.
772 	    //  Check is really simple. If security is on, then all scopes
773 	    //  in this DA/SA server are protected, so toss out any regs
774 	    //  that aren't, and vice versa.
775 
776 	    if (conf.getHasSecurity() && urlSig == null) {
777 		throw
778 		    new ServiceLocationException(
779 				ServiceLocationException.AUTHENTICATION_FAILED,
780 				"st_unprot_non_dereg",
781 				new Object[0]);
782 
783 	    } else if (!conf.getHasSecurity() && urlSig != null) {
784 		throw
785 		    new ServiceLocationException(
786 				ServiceLocationException.INVALID_REGISTRATION,
787 				"st_prot_non_dereg",
788 				new Object[0]);
789 
790 	    }
791 
792 	    // If it's a service URL, then deregister the URL.
793 
794 	    if (tags == null) {
795 		store.deregister(surl, scopes, urlSig);
796 
797 		// Track registred service types in case we get a
798 		// SAAdvert solicatation.
799 
800 		trackRegisteredServiceTypes();
801 
802 	    } else {
803 
804 		// Just delete the attributes.
805 
806 		store.deleteAttributes(surl, scopes, tags, locale);
807 
808 	    }
809 
810 	    // Create the reply.
811 
812 	    SrvLocMsg ack = req.makeReply();
813 
814 	    if (conf.regTest()) {
815 		conf.writeLog((tags == null ? "st_dereg":"st_delattr"),
816 			      new Object[] {
817 		    locationMsg,
818 	                locale,
819 			surl.getServiceType(),
820 			surl,
821 			tags});
822 
823 	    }
824 
825 	    if (conf.traceAll()) {
826 		conf.writeLog("st_dump",
827 			      new Object[] {locationMsg});
828 		store.dumpServiceStore();
829 
830 	    }
831 
832 	    return ack;
833 
834 	} catch (ServiceLocationException ex) {
835 
836 	    if (conf.traceDrop()) {
837 		conf.writeLog((tags == null ?
838 			       "st_dereg_drop" : "st_dereg_attr_drop"),
839 			      new Object[] {
840 		    locationMsg,
841 			ex.getMessage()+"("+ex.getErrorCode()+")",
842 			locale,
843 			surl.getServiceType(),
844 			surl,
845 			tags});
846 	    }
847 
848 	    return hdr.makeErrorReply(ex);
849 
850 	} catch (RuntimeException ex) {
851 
852 	    // These exceptions are not declared in throws but can occur
853 	    //  anywhere.
854 
855 	    Vector args = new Vector();
856 
857 	    args.addElement(req);
858 
859 	    reportNonfatalException(ex, args, store);
860 
861 	    return hdr.makeErrorReply(ex);
862 
863 	}
864     }
865 
866     /**
867      * Process the service type request and return the result in a reply.
868      *
869      * @param req Service type request message.
870      * @return SrvTypeRply A service type reply.
871      */
872 
873     SrvLocMsg findServiceTypes(SSrvTypeMsg req) {
874 
875 	SrvLocHeader hdr = req.getHeader();
876 	Vector scopes = hdr.scopes;
877 	String namingAuthority = req.namingAuthority;
878 	short errorCode = ServiceLocationException.OK;
879 
880 	try {
881 
882 	    // Check whether the scope is supported.
883 
884 	    if (!areSupportedScopes(scopes)) {
885 		throw
886 		    new ServiceLocationException(
887 				ServiceLocationException.SCOPE_NOT_SUPPORTED,
888 				"st_scope_unsup",
889 				new Object[0]);
890 
891 	    }
892 
893 	    // Get the vector of service types in the store, independent
894 	    //  of language.
895 
896 	    Vector types = store.findServiceTypes(namingAuthority, scopes);
897 
898 	    // Create the reply.
899 
900 	    SrvLocMsg ack = req.makeReply(types);
901 
902 	    if (conf.traceAll()) {
903 		conf.writeLog("st_stypes",
904 			      new Object[] {
905 		    locationMsg,
906 			namingAuthority,
907 			scopes,
908 			types});
909 	    }
910 
911 	    return ack;
912 
913 	} catch (ServiceLocationException ex) {
914 
915 	    if (conf.traceDrop()) {
916 		conf.writeLog("st_stypes_drop",
917 			      new Object[] {
918 		    locationMsg,
919 			ex.getMessage()+"("+ex.getErrorCode()+")",
920 			namingAuthority,
921 			scopes,
922 			hdr.locale});
923 	    }
924 
925 	    return hdr.makeErrorReply(ex);
926 
927 	} catch (RuntimeException ex) {
928 
929 	    // These exceptions are not declared in throws but can occur
930 	    //  anywhere.
931 
932 	    Vector args = new Vector();
933 
934 	    args.addElement(req);
935 
936 	    reportNonfatalException(ex, args, store);
937 
938 	    return hdr.makeErrorReply(ex);
939 
940 	}
941     }
942 
943     /**
944      * Process the service request and return the result in a reply.
945      *
946      * @param req Service request message.
947      * @return SrvRply A service reply.
948      */
949 
950     SrvLocMsg findServices(SSrvMsg req) {
951 
952 	SrvLocHeader hdr = req.getHeader();
953 	Locale locale = hdr.locale;
954 	Vector scopes = hdr.scopes;
955 	String serviceType = req.serviceType;
956 	String query = req.query;
957 	short errorCode = ServiceLocationException.OK;
958 
959 	try {
960 
961 	    // Check whether the scope is supported.
962 
963 	    if (!areSupportedScopes(scopes)) {
964 		throw
965 		    new ServiceLocationException(
966 				ServiceLocationException.SCOPE_NOT_SUPPORTED,
967 				"st_scope_unsup",
968 				new Object[0]);
969 	    }
970 
971 	    // Get the hashtable of returns.
972 
973 	    Hashtable returns =
974 		store.findServices(serviceType,
975 				   scopes,
976 				   query,
977 				   locale);
978 
979 	    // Get the hashtable of services v.s. scopes, and signatures, if
980 	    //  any.
981 
982 	    Hashtable services =
983 		(Hashtable)returns.get(ServiceStore.FS_SERVICES);
984 	    Hashtable signatures =
985 		(Hashtable)returns.get(ServiceStore.FS_SIGTABLE);
986 	    boolean hasSignatures = (signatures != null);
987 
988 	    // for each candidate URL, make sure it has the requested SPI
989 	    // (if any)
990 	    if (hasSignatures && !req.spi.equals("")) {
991 		Enumeration allSurls = services.keys();
992 		while (allSurls.hasMoreElements()) {
993 		    Object aSurl = allSurls.nextElement();
994 		    Hashtable auths = (Hashtable) signatures.get(aSurl);
995 		    AuthBlock auth =
996 			AuthBlock.getEquivalentAuth(req.spi, auths);
997 		    if (auth == null) {
998 			// doesn't have the requested SPI
999 			services.remove(aSurl);
1000 		    }
1001 		}
1002 	    }
1003 
1004 	    // Create return message.
1005 
1006 	    SrvLocMsg ack = req.makeReply(services, signatures);
1007 
1008 	    if (conf.traceAll()) {
1009 		conf.writeLog("st_sreq",
1010 			      new Object[] {
1011 		    locationMsg,
1012 			serviceType,
1013 			scopes,
1014 			query,
1015 			locale,
1016 			services,
1017 			signatures});
1018 	    }
1019 
1020 	    return ack;
1021 
1022 	} catch (ServiceLocationException ex) {
1023 
1024 	    if (conf.traceDrop()) {
1025 		conf.writeLog("st_sreq_drop",
1026 			      new Object[] {
1027 		    locationMsg,
1028 			ex.getMessage()+"("+ex.getErrorCode()+")",
1029 			serviceType,
1030 			scopes,
1031 			query,
1032 			locale});
1033 	    }
1034 
1035 	    return hdr.makeErrorReply(ex);
1036 
1037 	} catch (RuntimeException ex) {
1038 
1039 	    // These exceptions are not declared in throws but can occur
1040 	    //  anywhere.
1041 
1042 	    Vector args = new Vector();
1043 
1044 	    args.addElement(req);
1045 
1046 	    reportNonfatalException(ex, args, store);
1047 
1048 	    return hdr.makeErrorReply(ex);
1049 
1050 	}
1051     }
1052 
1053     /**
1054      * Process the attribute request and return the result in a reply.
1055      *
1056      * @param req Attribute request message.
1057      * @return AttrRply An attribute reply.
1058      */
1059 
1060     SrvLocMsg findAttributes(SAttrMsg req) {
1061 
1062 	// We need to determine whether this is a request for attributes
1063 	//  on a specific URL or for an entire service type.
1064 
1065 	SrvLocHeader hdr = req.getHeader();
1066 	Vector scopes = hdr.scopes;
1067 	Locale locale = hdr.locale;
1068 	ServiceURL surl = req.URL;
1069 	String serviceType = req.serviceType;
1070 	Vector tags = req.tags;
1071 	short errorCode = ServiceLocationException.OK;
1072 
1073 	try {
1074 
1075 	    // Check whether the scope is supported.
1076 
1077 	    if (!areSupportedScopes(scopes)) {
1078 	throw
1079 	    new ServiceLocationException(
1080 				ServiceLocationException.SCOPE_NOT_SUPPORTED,
1081 				"st_scope_unsup",
1082 				new Object[0]);
1083 	    }
1084 
1085 	    Vector attributes = null;
1086 	    Hashtable sig = null;
1087 
1088 	    // If it's a service URL, then get the attributes just for
1089 	    // that URL.
1090 
1091 	    if (serviceType == null) {
1092 
1093 		// If the attrs are signed, then error if any tags, since
1094 		//  we must ask for *all* attributes in for a signed reg
1095 
1096 		if (!req.spi.equals("") && tags.size() > 0) {
1097 		    throw
1098 			new ServiceLocationException(
1099 				ServiceLocationException.AUTHENTICATION_FAILED,
1100 				"st_par_attr",
1101 				new Object[0]);
1102 
1103 		}
1104 
1105 		Hashtable ht =
1106 		    store.findAttributes(surl, scopes, tags, locale);
1107 
1108 		// Get the attributes and signatures.
1109 
1110 		attributes = (Vector)ht.get(ServiceStore.FA_ATTRIBUTES);
1111 
1112 		sig = (Hashtable)ht.get(ServiceStore.FA_SIG);
1113 
1114 		// make sure the attr has the requested SPI (if any)
1115 		if (sig != null && !req.spi.equals("")) {
1116 		    AuthBlock auth = AuthBlock.getEquivalentAuth(req.spi, sig);
1117 		    if (auth == null) {
1118 			// return empty
1119 			attributes = new Vector();
1120 		    }
1121 		}
1122 
1123 	    } else {
1124 
1125 		if (!req.spi.equals("")) {
1126 		    throw
1127 			new ServiceLocationException(
1128 				ServiceLocationException.AUTHENTICATION_FAILED,
1129 				"st_par_attr",
1130 				new Object[0]);
1131 		}
1132 
1133 		// Otherwise find the attributes for all service types.
1134 
1135 		attributes =
1136 		    store.findAttributes(serviceType, scopes, tags, locale);
1137 
1138 	    }
1139 
1140 	    ServiceType type =
1141 		(serviceType == null ? surl.getServiceType():
1142 		 new ServiceType(serviceType));
1143 
1144 
1145 	    // Create the reply.
1146 
1147 	    SrvLocMsg ack = req.makeReply(attributes, sig);
1148 
1149 	    if (conf.traceAll()) {
1150 		conf.writeLog((serviceType != null ?
1151 			       "st_st_attr" : "st_url_attr"),
1152 			      new Object[] {
1153 		    locationMsg,
1154 			(serviceType != null ? serviceType.toString() :
1155 			 surl.toString()),
1156 		      	scopes,
1157 			tags,
1158 			locale,
1159 			attributes});
1160 	    }
1161 
1162 	    return ack;
1163 
1164 	} catch (ServiceLocationException ex) {
1165 
1166 	    if (conf.traceDrop()) {
1167 		conf.writeLog((serviceType != null ? "st_st_attr_drop":
1168 			       "st_url_attr_drop"),
1169 			      new Object[] {
1170 		    locationMsg,
1171 			ex.getMessage()+"("+ex.getErrorCode()+")",
1172 		        (serviceType != null ? serviceType.toString() :
1173 			 surl.toString()),
1174 		        scopes,
1175 			tags,
1176 			locale});
1177 	    }
1178 
1179 	    return hdr.makeErrorReply(ex);
1180 
1181 	} catch (RuntimeException ex) {
1182 
1183 	    // These exceptions are not declared in throws but can occur
1184 	    //  anywhere.
1185 
1186 	    Vector args = new Vector();
1187 
1188 	    args.addElement(req);
1189 
1190 	    reportNonfatalException(ex, args, store);
1191 
1192 	    return hdr.makeErrorReply(ex);
1193 
1194 	}
1195     }
1196 
1197     // Return the service record corresponding to the URL.
1198 
1199     ServiceStore.ServiceRecord getServiceRecord(ServiceURL URL,
1200 						Locale locale) {
1201 	return store.getServiceRecord(URL, locale);
1202 
1203     }
1204 
1205     //
1206     // Utility methods.
1207     //
1208 
1209     //
1210     //  Protected/private methods.
1211     //
1212 
1213     // Check whether the type is restricted, through an exception if so.
1214 
1215     private void checkForRestrictedType(ServiceType type)
1216 	throws ServiceLocationException {
1217 
1218 	if (Defaults.restrictedTypes.contains(type)) {
1219 	    throw
1220 		new ServiceLocationException(
1221 				ServiceLocationException.INVALID_REGISTRATION,
1222 				"st_restricted_type",
1223 				new Object[] {type});
1224 	}
1225     }
1226 
1227     // Insert a record for type "service-agent" with attributes having
1228     //  the types currently supported, if the new URL is not on the
1229     //  list of supported types. This allows us to perform queries
1230     //  for supported service types.
1231 
1232     private void trackRegisteredServiceTypes()
1233 	throws ServiceLocationException {
1234 
1235 	// First find the types.
1236 
1237 	Vector types = store.findServiceTypes(Defaults.ALL_AUTHORITIES,
1238 					      conf.getSAConfiguredScopes());
1239 
1240 	// Get preconfigured attributes.
1241 
1242 	Vector attrs = conf.getSAAttributes();
1243 
1244 	// Make an attribute with the service types.
1245 
1246 	ServiceLocationAttribute attr =
1247 	    new ServiceLocationAttribute(Defaults.SERVICE_TYPE_ATTR_ID,
1248 					 types);
1249 
1250 	attrs.addElement(attr);
1251 
1252 	// Construct URL to use on all interfaces.
1253 
1254 	Vector interfaces = conf.getInterfaces();
1255 	int i, n = interfaces.size();
1256 
1257 	for (i = 0; i < n; i++) {
1258 	    InetAddress addr = (InetAddress)interfaces.elementAt(i);
1259 	    ServiceURL url =
1260 		new ServiceURL(Defaults.SUN_SA_SERVICE_TYPE + "://" +
1261 			       addr.getHostAddress(),
1262 			       ServiceURL.LIFETIME_MAXIMUM);
1263 
1264 	    Vector scopes = conf.getSAOnlyScopes();
1265 
1266 	    Locale locale = Defaults.locale;
1267 
1268 	    // Make a new registration for this SA.
1269 
1270 	    store.register(url,
1271 			   attrs,
1272 			   scopes,
1273 			   locale,
1274 			   null,
1275 			   null);  // we could sign, but we do that later...
1276 	}
1277 
1278 	// Note that we don't need a refresh on the URLs because they
1279 	//  will get refreshed when the service URLs that they track
1280 	//  are refreshed. If the tracked URLs aren't refreshed, then
1281 	//  these will get updated when the tracked URLs age out.
1282     }
1283 
1284     // Return true if the scopes in the vector are supported by the DA
1285     //  or SA server.
1286 
1287     final private boolean areSupportedScopes(Vector scopes) {
1288 
1289 	Vector configuredScopes = conf.getSAConfiguredScopes();
1290 	Vector saOnlyScopes = conf.getSAOnlyScopes();
1291 	int i = 0;
1292 
1293 	while (i < scopes.size()) {
1294 	    Object o = scopes.elementAt(i);
1295 
1296 	    // Remove it if we don't support it.
1297 
1298 	    if (!configuredScopes.contains(o) && !saOnlyScopes.contains(o)) {
1299 		// This will shift the Vector's elements down one, so
1300 		// don't increment i
1301 		scopes.removeElementAt(i);
1302 	    } else {
1303 		i++;
1304 	    }
1305 	}
1306 
1307 	if (scopes.size() <= 0) {
1308 	    return false;
1309 
1310 	}
1311 
1312 	return true;
1313     }
1314 
1315     /**
1316      * Return the sleep increment from the URL lifetime. Used by the
1317      * ServiceStore to calculate the new sleep interval in addition
1318      * to this class, when a new URL comes in. The algorithm
1319      * subtracts x% of the lifetime from the lifetime and schedules the
1320      * timeout at that time.
1321      *
1322      * @param url The URL to use for calculation.
1323      * @return The sleep interval.
1324      */
1325 
1326     private long getSleepIncrement(ServiceURL url) {
1327 	long urlLifetime = (long)(url.getLifetime() * 1000);
1328 	long increment =
1329 	    (long)((float)urlLifetime * Defaults.fRefreshGranularity);
1330 	long sTime = urlLifetime - increment;
1331 
1332 	// If URL lives only one second, update every half second.
1333 
1334 	if (sTime <= 0) {
1335 	    sTime = 500;
1336 
1337 	}
1338 
1339 	return sTime;
1340     }
1341 
1342     // Make a DAADvert for the DA service request. This only applies
1343     //  to DAs, not to SA servers.
1344 
1345     SrvLocMsg
1346 	makeDAAdvert(SSrvMsg rqst,
1347 		     InetAddress daAddr,
1348 		     SLPConfig conf) {
1349 
1350 	SrvLocHeader hdr = rqst.getHeader();
1351 	Vector scopes = hdr.scopes;
1352 	short xid = hdr.xid;
1353 	String query = rqst.query;
1354 
1355 	try {
1356 
1357 	    // If security is on, proceed only if we can sign as rqst.spi
1358 	    if (conf.getHasSecurity() && !AuthBlock.canSignAs(rqst.spi)) {
1359 		throw new ServiceLocationException(
1360 			ServiceLocationException.AUTHENTICATION_UNKNOWN,
1361 			"st_cant_sign_as",
1362 			new Object[] {rqst.spi});
1363 	    }
1364 
1365 	    // Get the hashtable of service URLs v.s. scopes.
1366 
1367 	    Hashtable services =
1368 		ServerDATable.getServerDATable().returnMatchingDAs(query);
1369 
1370 	    // Go through the table checking whether the IP address came back.
1371 
1372 	    Enumeration urls = services.keys();
1373 	    boolean foundIt = false;
1374 	    String strDAAddr = daAddr.getHostAddress();
1375 
1376 	    while (urls.hasMoreElements()) {
1377 		ServiceURL url = (ServiceURL)urls.nextElement();
1378 
1379 		if (url.getHost().equals(strDAAddr)) {
1380 		    foundIt = true;
1381 		    break;
1382 
1383 		}
1384 	    }
1385 
1386 	    // If we didn't find anything, make a null service reply.
1387 
1388 	    if (!foundIt) {
1389 		return rqst.makeReply(new Hashtable(), new Hashtable());
1390 
1391 	    }
1392 
1393 	    return makeDAAdvert(hdr, daAddr, xid, scopes, conf);
1394 
1395 
1396 	} catch (ServiceLocationException ex) {
1397 
1398 	    return hdr.makeErrorReply(ex);
1399 
1400 	}
1401 
1402     }
1403 
1404     // Make a DAAdvert from the input arguments.
1405     SrvLocMsg
1406 	makeDAAdvert(SrvLocHeader hdr,
1407 		     InetAddress daAddr,
1408 		     short xid,
1409 		     Vector scopes,
1410 		     SLPConfig config)
1411 	throws ServiceLocationException {
1412 
1413 	// If this is a request for a V1 Advert, truncate the scopes vector
1414 	//  since DA solicitations in V1 are always unscoped
1415 
1416 	if (hdr.version == 1) {
1417 	    scopes = new Vector();
1418 
1419 	}
1420 
1421 	// Check if we support scopes first. If not, return an
1422 	//  error reply unless the scope vector is zero. Upper layers
1423 	//  must sort out whether this is a unicast or multicast.
1424 
1425 	if (scopes.size() > 0 && !areSupportedScopes(scopes)) {
1426 	    throw
1427 		new ServiceLocationException(
1428 				ServiceLocationException.SCOPE_NOT_SUPPORTED,
1429 				"st_scope_unsup",
1430 				new Object[0]);
1431 
1432 	}
1433 
1434 	// Get the service store's timestamp. This must be the
1435 	//  time since last stateless reboot for a stateful store,
1436 	//  or the current time.
1437 
1438 	long timestamp = store.getStateTimestamp();
1439 
1440 	ServiceURL url =
1441 	    new ServiceURL(Defaults.DA_SERVICE_TYPE + "://" +
1442 			   daAddr.getHostAddress(),
1443 			   ServiceURL.LIFETIME_DEFAULT);
1444 
1445 	SDAAdvert advert =
1446 	    hdr.getDAAdvert(xid,
1447 			    timestamp,
1448 			    url,
1449 			    scopes,
1450 			    conf.getDAAttributes());
1451 
1452 	return advert;
1453     }
1454 
1455     // Make a SAADvert for the SA service request. This only applies
1456     //  to SA servers, not DA's. Note that we only advertise the "public"
1457     //  scopes, not the private ones.
1458 
1459     SSAAdvert
1460 	makeSAAdvert(SSrvMsg rqst,
1461 		     InetAddress interfac,
1462 		     SLPConfig conf)
1463 	throws ServiceLocationException {
1464 	SrvLocHeader hdr = rqst.getHeader();
1465 	int version = hdr.version;
1466 	short xid = hdr.xid;
1467 	Locale locale = hdr.locale;
1468 	Vector scopes = hdr.scopes;
1469 	String query = rqst.query;
1470 	String serviceType = rqst.serviceType;
1471 	Vector saOnlyScopes = conf.getSAOnlyScopes();
1472 
1473 	// If security is on, proceed only if we can sign as rqst.spi
1474 	if (conf.getHasSecurity() && !AuthBlock.canSignAs(rqst.spi)) {
1475 	    throw new ServiceLocationException(
1476 			ServiceLocationException.AUTHENTICATION_UNKNOWN,
1477 			"st_cant_sign_as",
1478 			new Object[] {rqst.spi});
1479 	}
1480 
1481 
1482 	// Check if we support scopes first. Note that this may allow
1483 	//  someone to get at the SA only scopes off machine, but that's
1484 	//  OK. Since the SAAdvert is only ever multicast, this is OK.
1485 
1486 	if (!areSupportedScopes(scopes) && !(scopes.size() <= 0)) {
1487 	    return null;
1488 
1489 	}
1490 
1491 	// If the scopes vector is null, then use all configured scopes.
1492 
1493 	if (scopes.size() <= 0) {
1494 	    scopes = (Vector)conf.getSAConfiguredScopes().clone();
1495 
1496 	}
1497 
1498 	// Check to be sure the query matches.
1499 	//  If it doesn't, we don't need to return anything.
1500 
1501 	Hashtable returns =
1502 	    store.findServices(Defaults.SUN_SA_SERVICE_TYPE.toString(),
1503 			       saOnlyScopes,
1504 			       query,
1505 			       Defaults.locale);
1506 	Hashtable services =
1507 	    (Hashtable)returns.get(ServiceStore.FS_SERVICES);
1508 	Enumeration en = services.keys();
1509 
1510 	// Indicates we don't support the service type.
1511 
1512 	if (!en.hasMoreElements()) {
1513 	    return null;
1514 
1515 	}
1516 
1517 	// Find the URL to use. The interface on which the message came in
1518 	//  needs to match one of the registered URLs.
1519 
1520 	ServiceURL url = null;
1521 	ServiceURL surl = null;
1522 	String addr = interfac.getHostAddress();
1523 
1524 	while (en.hasMoreElements()) {
1525 	    surl = (ServiceURL)en.nextElement();
1526 
1527 	    if (addr.equals(surl.getHost())) {
1528 		url = new ServiceURL(Defaults.SA_SERVICE_TYPE + "://" +
1529 				     addr,
1530 				     ServiceURL.LIFETIME_DEFAULT);
1531 		break;
1532 	    }
1533 	}
1534 
1535 	// If none of the URLs matched this interface, then return null.
1536 
1537 	if (url == null) {
1538 	    return null;
1539 
1540 	}
1541 
1542 	// Find the SA's attributes.
1543 
1544 	Hashtable ht =
1545 	    store.findAttributes(surl,
1546 				 saOnlyScopes,
1547 				 new Vector(),
1548 				 Defaults.locale);
1549 
1550 	Vector attrs = (Vector)ht.get(ServiceStore.FA_ATTRIBUTES);
1551 
1552 	// Construct return.
1553 
1554 	return
1555 	    new SSAAdvert(version,
1556 			  xid,
1557 			  locale,
1558 			  url,
1559 			  conf.getSAConfiguredScopes(), // report all scopes...
1560 			  attrs);
1561     }
1562 
1563     /**
1564      * Report a fatal exception to the log.
1565      *
1566      * @param ex The exception to report.
1567      */
1568 
1569     protected static void reportFatalException(Exception ex) {
1570 
1571 	reportException(true, ex, new Vector());
1572 
1573 	if (table != null) {
1574 	    table.store.dumpServiceStore();
1575 	}
1576 
1577 	conf.writeLog("exiting_msg", new Object[0]);
1578 
1579 	System.exit(1);
1580 
1581     }
1582 
1583     /**
1584      * Report a nonfatal exception to the log.
1585      *
1586      * @param ex The exception to report.
1587      * @param args The method arguments.
1588      * @param store The service store being processed.
1589      */
1590 
1591     protected static void reportNonfatalException(Exception ex,
1592 						  Vector args,
1593 						  ServiceStore store) {
1594 
1595 	reportException(false, ex, args);
1596 
1597 	if (conf.traceAll()) {
1598 	    store.dumpServiceStore();
1599 	}
1600 
1601     }
1602 
1603     /**
1604      * Report an exception to the log.
1605      *
1606      * @param isFatal Indicates whether the exception is fatal or not.
1607      * @param ex The exception to report.
1608      * @param args A potentially null vector of arguments to the
1609      * 			method where the exception was caught.
1610      */
1611 
1612     private static void
1613 	reportException(boolean isFatal, Exception ex, Vector args) {
1614 
1615 	StringWriter sw = new StringWriter();
1616 	PrintWriter writer = new PrintWriter(sw);
1617 
1618 	// Get the backtrace.
1619 
1620 	ex.printStackTrace(writer);
1621 
1622 	String severity = (isFatal ? "fatal_error":"nonfatal_error");
1623 	String msg = ex.getMessage();
1624 
1625 	if (msg == null) {
1626 	    msg = conf.formatMessage("no_message", new Object[0]);
1627 
1628 	} else if (ex instanceof ServiceLocationException) {
1629 	    msg = msg +
1630 		"(" + ((ServiceLocationException)ex).getErrorCode() + ")";
1631 
1632 	}
1633 
1634 	StringBuffer argMsg = new StringBuffer();
1635 
1636 	int i, n = args.size();
1637 
1638 	for (i = 0; i < n; i++) {
1639 	    argMsg.append("\n        (" + Integer.toString(i) + "):" +
1640 			  args.elementAt(i).toString());
1641 	}
1642 
1643 	conf.writeLog(severity,
1644 		      new Object[] {
1645 	    ex.getClass().getName(),
1646 		msg,
1647 		argMsg,
1648 		sw.toString()});
1649 
1650     }
1651 }
1652