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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2001 by Sun Microsystems, Inc.
23  * All rights reserved.
24  *
25  */
26 
27 //  SunServerDATable.java: Server DA Table for Sun's client/SA server SLP.
28 //  Author:           James Kempf
29 //  Created On:       Wed May 20 09:58:46 1998
30 //  Last Modified By: James Kempf
31 //  Last Modified On: Mon Mar  8 14:30:29 1999
32 //  Update Count:     79
33 //
34 
35 package com.sun.slp;
36 
37 import java.util.*;
38 import java.net.*;
39 import java.io.*;
40 
41 /**
42  * SunServerDATable is a subclass class that provides the
43  * implementation for DA storage on Solaris. As described in
44  * the header for SunDATable, DA information is stored in the server's
45  * SA table as the service type "directory-agent.sun"  with a
46  * attribute, scopes. The attribute contains a list of scopes supported
47  * by the DA. The service: URL of the registration contains the
48  * DA address as the host, followed by the list of scopes as an attribute
49  * in the URL part. An example is:
50  *
51  *   service:directory-agent.sun:// 199.200.200.5/scopes=eng, corp, freeb
52  *
53  * The scopes of the registration are the scopes provided as the Sun-specific
54  * system property "sun.net.slp.SAOnlyScopes". By convention, this is
55  * initialized to be the local machine name, but it may also include other
56  * names.
57  *
58  * @author James Kempf
59  */
60 
61 class SunServerDATable extends ServerDATable {
62 
63     // DA boot timestamp.
64 
65     static final private String TIMESTAMP_ID =
66 	"424242SUN-TABLE-TIMESTAMP424242";
67 
68     // Address. Makes deletion easier.
69 
70     static final private String ADDRESS_ID = "424242SUN-TABLE-ADDRESS424242";
71 
72     private ServiceTable serviceTable = null;  // SA table for regs.
73     private Vector saOnlyScopes = null;	       // Scopes for SA only.
74 
SunServerDATable()75     SunServerDATable() {
76 
77 	// Get the service table.
78 
79 	try {
80 
81 	    serviceTable = ServiceTable.getServiceTable();
82 
83 	} catch (ServiceLocationException ex) {
84 
85 	}
86 
87 	// Get the vector of SA scopes.
88 
89 	saOnlyScopes = conf.getSAOnlyScopes();
90 
91 	Assert.slpassert(saOnlyScopes.size() > 0,
92 		      "no_sa_scopes",
93 		      new Object[0]);
94 
95     }
96 
97     /**
98      * Record a new DA in the service table.
99      *
100      * @param URL The DAAdvert URL.
101      * @param scopes The scopes.
102      * @param version DA version number.
103      * @param spis SPIs this DA can support
104      * @return The boot timestamp in the previous registration. Used
105      *         to determine if registration is necessary. If an error occurs,
106      *	       the returned value is negative. If the DA is new, the return
107      *         value is the maximum long value. This will cause all
108      *         registrations to be forwarded, because it is larger than any
109      *         current time.
110      */
111 
112     public synchronized long
recordNewDA(ServiceURL url, Vector scopes, long timestamp, int version, Vector attrs, String spis)113 	recordNewDA(ServiceURL url,
114 		    Vector scopes,
115 		    long timestamp,
116 		    int version,
117 		    Vector attrs,
118 		    String spis) {
119 
120 	String addr = url.getHost();
121 	long formerTimestamp = -1L;
122 
123 	// We record all DAs regardless of whether we support them or not,
124 	//  because a UA client may be using the user selectable scoping
125 	//  model and therefore may want to see them.
126 
127 	Vector v = (Vector)scopes.clone();
128 
129 	// Add the Sun attributes.
130 
131 	ServiceLocationAttribute attr =
132 	    new ServiceLocationAttribute(SunDATable.SCOPES_ID, scopes);
133 	attrs.addElement(attr);
134 
135 	Vector vals = new Vector();
136 	vals.addElement(Long.toString(timestamp));
137 	attr =
138 	    new ServiceLocationAttribute(SunServerDATable.TIMESTAMP_ID, vals);
139 	attrs.addElement(attr);
140 
141 	vals = new Vector();
142 	vals.addElement(new Integer(version));
143 	attr = new ServiceLocationAttribute(SunDATable.VERSION_ID, vals);
144 	attrs.addElement(attr);
145 
146 	vals = new Vector();
147 	vals.addElement(url.getHost());
148 	attr = new ServiceLocationAttribute(SunServerDATable.ADDRESS_ID, vals);
149 	attrs.addElement(attr);
150 
151 	// Form the URL for the DA.
152 
153 	ServiceURL adURL = formServiceTableDAURL(url, attrs);
154 
155 	// Reach *around* the service table for registration, because
156 	//  we don't need a message. The service table abstraction
157 	//  is basically for decoding message objects, and we already
158 	//  have things in the internal form needed by the service store.
159 
160 	ServiceStore store = serviceTable.store;
161 
162 	try {
163 
164 	    // First, get the boot time stamp if there.
165 
166 	    Vector tags = new Vector();
167 	    tags.addElement(SunServerDATable.TIMESTAMP_ID);
168 
169 	    Hashtable attrRec =
170 		store.findAttributes(adURL,
171 				     saOnlyScopes,
172 				     tags,
173 				     Defaults.locale);
174 
175 	    Vector formerAttrs =
176 		(Vector)attrRec.get(ServiceStore.FA_ATTRIBUTES);
177 
178 	    // If there, then get the old timestamp.
179 
180 	    if (formerAttrs != null && !(formerAttrs.size() <= 0)) {
181 
182 		// Get the timestamp into a long.
183 
184 		attr = (ServiceLocationAttribute)formerAttrs.elementAt(0);
185 		vals = attr.getValues();
186 		String stamp = (String)vals.elementAt(0);
187 
188 		try {
189 
190 		    formerTimestamp = Long.parseLong(stamp.trim());
191 
192 		} catch (NumberFormatException ex) {
193 
194 		    Assert.slpassert(false,
195 				  "ssdat_number_format",
196 				  new Object[0]);
197 
198 		}
199 	    }
200 
201 	    // Now register the URL.
202 
203 	    store.register(adURL,
204 			   attrs,
205 			   saOnlyScopes,
206 			   Defaults.locale,
207 			   null,
208 			   null);
209 
210 	    // Keep track of this DAs supported SPIs
211 	    LinkedList spiList =
212 		AuthBlock.commaSeparatedListToLinkedList(spis);
213 
214 	    // convert addr to an InetAddress for hashing
215 	    InetAddress inetAddr = null;
216 	    try {
217 		inetAddr = InetAddress.getByName(addr);
218 	    } catch (UnknownHostException e) {}
219 
220 	    // If we didn't get the InetAddress, this DA will never be used
221 	    // anyway
222 	    if (addr != null) {
223 		daSPIsHash.put(inetAddr, spiList);
224 	    }
225 
226 	} catch (ServiceLocationException ex) {
227 	    conf.writeLog("ssdat_register_error",
228 			  new Object[] {
229 		ex.getMessage(),
230 		    adURL,
231 		    saOnlyScopes});
232 	}
233 
234 	return formerTimestamp;
235     }
236 
237     /**
238      * Remove a DA. The Sun-specific convention is used to deregister
239      * the URL.
240      *
241      * @param address The host address of the DA, from its service URL.
242      * @param scopes The scopes.
243      * @return True if removed, false if not.
244      */
245 
removeDA(InetAddress address, Vector scopes)246     public synchronized boolean removeDA(InetAddress address, Vector scopes) {
247 
248 	// Find URLs corresponding to this address.
249 
250 	String query = "(" + ADDRESS_ID + "=" + address.getHostAddress() + ")";
251 
252 	// Reach *around* the service table for dregistration, because
253 	//  we don't need a message. The service table abstraction
254 	//  is basically for decoding message objects, and we already
255 	//  have things in the internal form needed by the service store.
256 
257 	ServiceStore store = serviceTable.store;
258 
259 	try {
260 
261 	    Hashtable das = returnMatchingDAs(query);
262 
263 	    Enumeration daURLs = das.keys();
264 
265 	    while (daURLs.hasMoreElements()) {
266 		ServiceURL adURL = (ServiceURL)daURLs.nextElement();
267 		store.deregister(adURL, saOnlyScopes, null);
268 
269 	    }
270 
271 	} catch (ServiceLocationException ex) {
272 	    conf.writeLog("ssdat_deregister_error",
273 			  new Object[] {
274 		ex.getMessage(),
275 		    address,
276 		    saOnlyScopes});
277 
278 	    return false;
279 	}
280 
281 	return true;
282 
283     }
284 
285     /**
286      * Return a hashtable in ServiceTable.findServices() format (e.g.
287      * URL's as keys, scopes as values) for DAs matching the query.
288      *
289      * @param query Query for DA attributes.
290      */
291 
returnMatchingDAs(String query)292     public synchronized Hashtable returnMatchingDAs(String query)
293 	throws ServiceLocationException {
294 	ServiceStore store = ServiceTable.getServiceTable().store;
295 
296 	// Get DA records matching the query.
297 
298 	Vector saOnlyScopes = conf.getSAOnlyScopes();
299 
300 	Hashtable returns =
301 	    store.findServices(Defaults.SUN_DA_SERVICE_TYPE.toString(),
302 			       saOnlyScopes,
303 			       query,
304 			       Defaults.locale);
305 
306 	// Return the hashtable of services v.s. scopes.
307 
308 	return (Hashtable)returns.get(ServiceStore.FS_SERVICES);
309     }
310 
311     /**
312      * Return a hashtable of DA equivalence classes and multicast
313      * scopes. Multicast scopes are stored in the special hashtable
314      * key MULTICAST_KEY. Unicast DA equivalence classes are stored
315      * under the key UNICAST_KEY. This implementation goes directly
316      * to the service table in the SA server for the DA addresses.
317      *
318      * @param scopes Scope list for DAs needed.
319      * @return Hashtable with DA addresses as keys and scopes to contact
320      *         them with as values. Any scopes not associated with a
321      *         DA come back stored under the key MULTICAST_KEY.
322      *         Unicast DA equivalence classes are stored
323      * 	     under the key UNICAST_KEY.
324      */
325 
findDAScopes(Vector scopes)326     public synchronized Hashtable findDAScopes(Vector scopes)
327 	throws ServiceLocationException {
328 
329 	// Formulate a query for the DAs.
330 
331 	int i, n = scopes.size();
332 	StringBuffer buf = new StringBuffer();
333 
334 	for (i = 0; i < n; i++) {
335 	    buf.append("(");
336 	    buf.append(SunDATable.SCOPES_ID);
337 	    buf.append("=");
338 	    buf.append((String)scopes.elementAt(i));
339 	    buf.append(")");
340 	}
341 
342 	// Add logical disjunction if more than one element.
343 
344 	if (i > 1) {
345 	    buf.insert(0, "(|");
346 	    buf.append(")");
347 
348 	}
349 
350 	// Add version number.
351 
352 	if (i > 0) {
353 	    buf.insert(0, "(&");
354 
355 	}
356 
357 	buf.append("(");
358 	buf.append(SunDATable.VERSION_ID);
359 	buf.append("=");
360 	buf.append((new Integer(Defaults.version)).toString());
361 	buf.append(")");
362 
363 	// Add closing paren if there were any scopes.
364 
365 	if (i > 0) {
366 	    buf.append(")");
367 
368 	}
369 
370 	ServiceStore store = serviceTable.store;
371 
372 	Hashtable returns =
373 	    store.findServices(Defaults.SUN_DA_SERVICE_TYPE.toString(),
374 			       saOnlyScopes,
375 			       buf.toString(),
376 			       Defaults.locale);
377 
378 	Hashtable retRec = (Hashtable)returns.get(ServiceStore.FS_SERVICES);
379 
380 	// Convert to a vector. Keys are the service: URLs.
381 
382 	Enumeration en = retRec.keys();
383 	Vector ret = new Vector();
384 	Vector multiScopes = (Vector)scopes.clone();
385 	Vector attrTags = new Vector();
386 
387 	attrTags.addElement(SunDATable.SCOPES_ID);
388 
389 	while (en.hasMoreElements()) {
390 	    ServiceURL url = (ServiceURL)en.nextElement();
391 	    Vector urlScopes = (Vector)retRec.get(url);
392 
393 	    // Get the scope attributes for this URL.
394 
395 	    Hashtable attrRec =
396 		store.findAttributes(url,
397 				     urlScopes,
398 				     attrTags,
399 				     Defaults.locale);
400 
401 	    Vector retAttrs = (Vector)attrRec.get(ServiceStore.FA_ATTRIBUTES);
402 	    String host = url.getHost();
403 	    Vector retScopes = null;
404 	    n = retAttrs.size();
405 
406 	    for (i = 0; i < n; i++) {
407 		ServiceLocationAttribute attr =
408 		    (ServiceLocationAttribute)retAttrs.elementAt(i);
409 
410 		// Distinguish based on type. We assume the attributes are
411 		// prescreened when the URL was formed to make sure they're OK
412 
413 		String id = attr.getId();
414 		Vector vals = attr.getValues();
415 
416 		if (id.equals(SunDATable.SCOPES_ID)) {
417 		    retScopes = vals;
418 
419 		}
420 	    }
421 
422 	    // Add to equivalence class.
423 
424 	    DATable.addToEquivClass(host, retScopes, ret);
425 
426 	    // Filter scopes for any that might be multicast.
427 
428 	    DATable.filterScopes(multiScopes, retScopes, false);
429 
430 	}
431 
432 	// Format the return.
433 
434 	retRec.clear();
435 
436 	if (multiScopes.size() > 0) {
437 	    retRec.put(DATable.MULTICAST_KEY, multiScopes);
438 
439 	}
440 
441 	if (ret.size() > 0) {
442 	    retRec.put(DATable.UNICAST_KEY, ret);
443 
444 	}
445 
446 	return retRec;
447     }
448 
449     // Form a URL for the service table, from the DA URL and attributes.
450     //  Attributes and scope have been prechecked for correctness.
451 
formServiceTableDAURL(ServiceURL url, Vector attrs)452     private ServiceURL formServiceTableDAURL(ServiceURL url, Vector attrs) {
453 
454 	// Form up the URL part.
455 
456 	StringBuffer buf = new StringBuffer();
457 
458 	int i, n = attrs.size();
459 
460 	for (i = 0; i < n; i++) {
461 	    ServiceLocationAttribute attr =
462 		(ServiceLocationAttribute)attrs.elementAt(i);
463 
464 	    // If this is a URL attribute, then externalize and
465 	    //  put into URL.
466 
467 	    String id = attr.getId();
468 
469 	    if (id.equals(SunDATable.SCOPES_ID)) {
470 		String rep = "";
471 
472 		try {
473 		    rep = attr.externalize();
474 
475 		} catch (ServiceLocationException ex) {
476 		    conf.writeLog("ssdat_inter_attr_err",
477 				  new Object[] {attr, ex.getMessage()});
478 		    continue;
479 
480 		}
481 
482 		// Add semi if something already there.
483 
484 		if (buf.length() > 0) {
485 		    buf.append(";");
486 
487 		}
488 
489 		// Remove parens before inserting.
490 
491 		buf.append(rep.substring(1, rep.length()-1));
492 
493 	    }
494 	}
495 
496 	// Create the URL.
497 
498 	ServiceURL daURL =
499 	    new ServiceURL(Defaults.SUN_DA_SERVICE_TYPE+
500 			   "://"+
501 			   url.getHost()+
502 			   "/"+
503 			   buf.toString(),
504 			   url.getLifetime());
505 	return daURL;
506 
507     }
508 }
509