xref: /illumos-gate/usr/src/lib/libslp/javalib/com/sun/slp/ServerDATable.java (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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) 1999 by Sun Microsystems, Inc.
23  * All rights reserved.
24  *
25  */
26 
27 //  ServerDATable.java: Abstract class for DA Table in the DA/SA server.
28 //  Author:           James Kempf
29 //  Created On:       Wed May 20 08:30:46 1998
30 //  Last Modified By: James Kempf
31 //  Last Modified On: Tue Mar  9 12:36:37 1999
32 //  Update Count:     124
33 //
34 
35 package com.sun.slp;
36 
37 import java.util.*;
38 import java.net.*;
39 
40 /**
41  * ServerDATable is an abstract class that provides the interface for DA
42  * and scope discovery, storage of DA information from incoming DAAdverts,
43  * and forwarding of registrations and deregistration to DAs having
44  * the same scopes. A variety of implementations are possible.
45  * The getServerDATable() method creates the right one from a subclass.
46  * We keep separate track of the superclass DA table and the server
47  * DA table so that these two classes can co-exist in the same JVM.
48  * Note that the code for forwarding registrations must keep track of
49  * only those registrations that were done on this host. It does this
50  * by saving the registrations as they come in. The forwarding code
51  * is optimized so that forwarding of a new message is fast, while
52  * forwarding of a message due to discovery of a new DA is somewhat
53  * slower. This helps assure that SA clients get good service.
54  *
55  * The ServerDATable also does active discovery for the SA server/DA,
56  * in a separate thread.
57  *
58  * @author James Kempf
59  */
60 
61 abstract class ServerDATable extends DATable {
62 
63     // The active discovery object.
64 
65     protected static ActiveDiscoverer activeDiscoverer = null;
66 
67     // The table of regs to forward. Keys are the reg URL and locale, values
68     //  are the SSrvReg objects.
69 
70     protected Hashtable forwardRegs = new Hashtable();
71 
72     // This acts as a guard protecting an non-initialized DA table:
73     //  If the DA Table hasn't been populated by active discovery yet,
74     //  other threads accessing the DA table will block on readyLock.
75     private static Object readyLock = new Object();
76 
77     // Keeps track of which DAs support which SPIs. The actual mapping
78     //  is DA InetAddress to LinkedList of SPI Strings. recordNewDA
79     //  populates this.
80     protected Hashtable daSPIsHash = new Hashtable();
81 
82     /**
83      * Get the right server DA table from the subclass.
84      *
85      * @return Table for handling DAs in the server.
86      */
87 
88     static ServerDATable getServerDATable()
89 	throws ServiceLocationException {
90 
91 	ServerDATable table = null;
92 
93 	synchronized (readyLock) {
94 
95 	    // Note that we are expecting this subclass. We will get a
96 	    //  cast exception if somebody instantiated with a
97 	    //  DATable subclass.
98 
99 	    if (daTable != null) {
100 		return (ServerDATable)daTable;
101 
102 	    }
103 
104 	    conf = SLPConfig.getSLPConfig();
105 
106 	    // Call the superclass method to link it.
107 
108 	    daTable = linkAndInstantiateFromProp();
109 
110 	    table = (ServerDATable)daTable;
111 
112 	    // Advertise for *all* scopes. This is because we need to
113 	    //  be able to support clients that do user scoping.
114 
115 	    Vector useScopes = new Vector();
116 
117 	    activeDiscoverer =
118 		new ActiveDiscoverer(Defaults.version,
119 				     table,
120 				     useScopes,
121 				     conf.getMulticastAddress());
122 
123 	    activeDiscoverer.start();
124 
125 	}	// readyLock
126 
127 	return table;
128 
129     }
130 
131     /**
132      * Record a new DA.
133      *
134      * @param URL The DAAdvert URL.
135      * @param scopes The scopes.
136      * @param version DA version number.
137      * @param attrs Attributes of DA.
138      * @param spis SPIs supported by DA
139      * @return True if recorded, false if not.
140      */
141 
142     abstract long
143 	recordNewDA(ServiceURL url,
144 		    Vector scopes,
145 		    long timestamp,
146 		    int version,
147 		    Vector attrs,
148 		    String spis);
149 
150     /**
151      * Return a hashtable in ServiceTable.findServices() format (e.g.
152      * URL's as keys, scopes as values) for DAs matching the query.
153      *
154      * @param query Query for DA attributes.
155      */
156 
157     abstract Hashtable returnMatchingDAs(String query)
158 	throws ServiceLocationException;
159 
160     /**
161      * Forward a registration or deregistration to all DAs that have matching
162      * scopes.
163      *
164      * @param msg Registration or deregistration message, server side.
165      * @param source The address of the source.
166      */
167 
168     void forwardSAMessage(SrvLocMsg msg, InetAddress source)
169 	throws ServiceLocationException {
170 
171 	SrvLocHeader hdr = msg.getHeader();
172 
173 	// If the message is not from this host (on any interface)
174 	//  then don't forward it.
175 
176 	if (!conf.isLocalHostSource(source)) {
177 	    return;
178 
179 	}
180 
181 	// Record it so we can forward to a new DA.
182 
183 	if (msg instanceof SSrvReg || msg instanceof CSrvReg) {
184 	    ServiceURL url;
185 	    if (msg instanceof SSrvReg) {
186 		url = ((SSrvReg)msg).URL;
187 	    } else {
188 		url = ((CSrvReg)msg).URL;
189 	    }
190 
191 	    String key = makeKey(url, hdr.locale); // need locale also...
192 	    forwardRegs.put(key, msg);  // fresh doesn't matter.
193 
194 	} else {
195 	    SSrvDereg smsg = (SSrvDereg)msg;
196 
197 	    // Only remove if tags are null. Otherwise, the updated record
198 	    //  will be sought.
199 
200 	    if (smsg.tags == null) {
201 		String key = makeKey(smsg.URL, hdr.locale);
202 		forwardRegs.remove(key);
203 
204 	    }
205 	}
206 
207 	// We only forward registrations to v2 DAs because we are
208 	//  acting as an SA server here. There is no requirement
209 	//  for v2 SAs to communicate with v1 DAs.
210 
211 	// Get a hashtable of DAs that match the scopes in the message.
212 
213 	Hashtable daScopeRec = findDAScopes(hdr.scopes);
214 
215 	// We are only concerned with the unicast key, since it contains
216 	//  the DAs to which forwarding is required.
217 
218 	Vector daRecs = (Vector)daScopeRec.get(UNICAST_KEY);
219 
220 	// If there are no daRecs, then simply return.
221 
222 	if (daRecs == null) {
223 	    return;
224 
225 	}
226 
227 	// Otherwise, forward the registration to all DAs in the vector.
228 
229 	int i, n = daRecs.size();
230 
231 	for (i = 0; i < n; i++) {
232 	    DARecord rec = (DARecord)daRecs.elementAt(i);
233 	    Vector daAddresses = rec.daAddresses;
234 
235 	    int j, m = daAddresses.size();
236 
237 	    for (j = 0; j < m; j++) {
238 		InetAddress addr = (InetAddress)daAddresses.elementAt(j);
239 
240 		// Don't forward if it's the host from which the registration
241 		//  came. Otherwise, we're hosed.
242 
243 		if (!source.equals(addr)) {
244 		    forwardRegOrDereg(addr, msg);
245 
246 		}
247 	    }
248 	}
249     }
250 
251     // Make a key for the service agent message table.
252 
253     private String makeKey(ServiceURL url, Locale locale) {
254 
255 	return url.toString() + "/" + locale.toString();
256 
257     }
258 
259 
260     /**
261      * Handle an incoming DAAdvert. Presence must be recorded in the
262      * implementation specific server DA table and any registrations need
263      * to be forwarded if the boot timestamp is different from the
264      * old boot timestamp.
265      *
266      * @param advert Incoming DAAdvert.
267      */
268 
269     void handleAdvertIn(CDAAdvert advert) {
270 
271 	SrvLocHeader hdr = advert.getHeader();
272 
273 	// Remove if DA is going down.
274 
275 	if (advert.isGoingDown()) {
276 
277 	    InetAddress addr = null;
278 
279 	    try {
280 
281 		addr = InetAddress.getByName(advert.URL.getHost());
282 
283 	    } catch (UnknownHostException ex) {
284 		conf.writeLog("unknown_da_address",
285 			      new Object[] {advert.URL.getHost()});
286 
287 		return;
288 	    }
289 
290 	    if (removeDA(addr, hdr.scopes)) {
291 
292 		if (conf.traceDATraffic()) {
293 		    conf.writeLog("sdat_delete_da",
294 				  new Object[] {
295 			advert.URL,
296 			    hdr.scopes});
297 		}
298 	    }
299 
300 	} else {
301 
302 	    // verify the DAAdvert
303 	    if (advert.authBlock != null) {
304 		try {
305 		    AuthBlock.verifyAll(advert.authBlock);
306 		} catch (ServiceLocationException e) {
307 		    if (conf.traceDrop()) {
308 			conf.writeLog("sdat_daadvert_vrfy_failed",
309 				      new Object[] {advert.URL});
310 		    }
311 		    return;
312 		}
313 	    }
314 
315 	    long timestamp =
316 		recordNewDA(advert.URL,
317 			    hdr.scopes,
318 			    advert.timestamp,
319 			    hdr.version,
320 			    advert.attrs,
321 			    advert.spis);
322 
323 	    // Don't forward if the advert was rejected, or if the
324 	    //  old timestamp greater than or equal to the new timestamp.
325 	    //  If the old timestamp is greater than or equal to the new,
326 	    //  it means that we have already forwarded to this DA.
327 	    //  IF the old timestamp is less, it means that
328 	    //  the DA has crashed and come up again since we last saw
329 	    //  it, so we may have missed forwarding something to it.
330 
331 	    if (timestamp >= advert.timestamp) {
332 
333 		if (conf.traceDATraffic()) {
334 		    conf.writeLog("sdat_add_da_no_forward",
335 				  new Object[] {
336 			advert.URL,
337 			    hdr.scopes,
338 			    new Long(timestamp)});
339 		}
340 
341 		return;
342 
343 	    }
344 
345 	    if (conf.traceDATraffic()) {
346 		conf.writeLog("sdat_add_da",
347 			      new Object[] {
348 		    advert.URL,
349 			hdr.scopes,
350 			new Long(advert.timestamp)});
351 	    }
352 
353 	    // Forward existing registrations to the new advert.
354 
355 	    forwardRegistrations(advert.URL, hdr.scopes,
356 				 advert.timestamp, hdr.version);
357 	}
358     }
359 
360     //
361     // Private methods.
362     //
363 
364     private void
365 	forwardRegistrations(ServiceURL url,
366 			     Vector scopes,
367 			     long timestamp,
368 			     int version) {
369 
370 	// Wait a random amount of time before forwarding.
371 
372 	try {
373 
374 	    Thread.currentThread().sleep(conf.getRandomWait());
375 
376 	} catch (InterruptedException ex) {
377 
378 	}
379 
380 	// Get the registrations to forward.
381 
382 	Enumeration regs = forwardRegs.elements();
383 
384 	// Get the address of the DA.
385 
386 	InetAddress addr = null;
387 	String host = url.getHost();
388 
389 	try {
390 	    addr = InetAddress.getByName(host);
391 
392 	} catch (UnknownHostException ex) {
393 	    if (conf.traceDrop() || conf.traceDATraffic()) {
394 		conf.writeLog("sdat_drop_fwd",
395 			      new Object[] {
396 		    host});
397 
398 	    }
399 
400 	    return;
401 	}
402 
403 	ServiceTable serviceTable = null;
404 
405 	try {
406 
407 	    serviceTable = ServiceTable.getServiceTable();
408 
409 	} catch (ServiceLocationException ex) {
410 
411 	    // By this time, we should have it.
412 
413 	}
414 
415 	// Forward the registrations. Keep track of any deleted elements.
416 
417 	Vector deleted = new Vector();
418 
419 	while (regs.hasMoreElements()) {
420 	    SrvLocMsg reg = (SrvLocMsg)regs.nextElement();
421 
422 	    ServiceURL regurl;
423 	    if (reg instanceof SSrvReg) {
424 		regurl = ((SSrvReg)reg).URL;
425 	    } else {
426 		regurl = ((CSrvReg)reg).URL;
427 	    }
428 
429 	    SrvLocHeader hdr = reg.getHeader();
430 
431 	    // Get the record and modify the reg to reflect the
432 	    //  record. We must do this because the SA may have
433 	    //  changed the record since it was first registred
434 	    //  and we do not keep track of the changes here.
435 
436 	    ServiceStore.ServiceRecord rec =
437 		serviceTable.getServiceRecord(regurl, hdr.locale);
438 
439 	    // If the record is null, it means that the entry was
440 	    //  aged out.
441 
442 	    if (rec == null) {
443 		deleted.addElement(reg);
444 
445 	    } else {
446 
447 		// Check that the scopes match.
448 
449 		Vector sscopes = (Vector)hdr.scopes.clone();
450 
451 		DATable.filterScopes(sscopes, scopes, false);
452 
453 		if (sscopes.size() <= 0) {
454 		    continue;
455 
456 		}
457 
458 		if (reg instanceof SSrvReg) {
459 		    SSrvReg sreg = (SSrvReg)reg;
460 
461 		    hdr.scopes = (Vector)hdr.scopes.clone();
462 		    sreg.attrList = (Vector)rec.getAttrList().clone();
463 		    sreg.URLSignature = rec.getURLSignature();
464 		    sreg.attrSignature = rec.getAttrSignature();
465 		}
466 
467 		forwardRegOrDereg(addr, reg);
468 	    }
469 
470 	}
471 
472 	// Remove any deleted elements from the hashtable.
473 	//  We do this in a separate loop because enumerations
474 	//  aren't synchronized.
475 
476 	int i, n = deleted.size();
477 
478 	for (i = 0; i < n; i++) {
479 	    SrvLocMsg reg = (SrvLocMsg)deleted.elementAt(i);
480 	    SrvLocHeader hdr = reg.getHeader();
481 	    ServiceURL regurl;
482 	    if (reg instanceof SSrvReg) {
483 		regurl = ((SSrvReg)reg).URL;
484 	    } else {
485 		regurl = ((CSrvReg)reg).URL;
486 	    }
487 
488 	    String key = makeKey(regurl, hdr.locale);
489 
490 	    forwardRegs.remove(key);
491 
492 	}
493 
494     }
495 
496 
497     // Forward the registration or deregistration to the URL.
498 
499     private void forwardRegOrDereg(InetAddress addr, SrvLocMsg rqst) {
500 	SrvLocHeader hdr = rqst.getHeader();
501 
502 	// Don't forward to myself! Otherwise, nasty recursion happens.
503 
504 	if (conf.isLocalHostSource(addr)) {
505 	    return;
506 
507 	}
508 
509 	// If security is on, only forward if this DA can verify the authblocks
510 	if (conf.getHasSecurity()) {
511 	    LinkedList spis = (LinkedList)daSPIsHash.get(addr);
512 	    if (spis == null) {
513 		// internal error; skip this DA to be safe
514 		return;
515 	    }
516 
517 	    Hashtable auths = null;
518 	    if (rqst instanceof SSrvReg) {
519 		auths = ((SSrvReg)rqst).URLSignature;
520 	    } else if (rqst instanceof SSrvDereg) {
521 		auths = ((SSrvDereg)rqst).URLSignature;
522 	    } else {
523 		// shouldn't even be forwarding this!
524 		return;
525 	    }
526 
527 	    // If each authblock is equiv to at least one SPI, forward the reg
528 	    Enumeration abs = auths.elements();
529 	    while (abs.hasMoreElements()) {
530 		AuthBlock ab = (AuthBlock)abs.nextElement();
531 
532 		// check each DA SPI
533 		boolean daSPImatch = false;
534 		for (int SPIi = 0; SPIi < spis.size(); SPIi++) {
535 		    if (AuthBlock.checkEquiv((String)spis.get(SPIi), ab)) {
536 			daSPImatch = true;
537 			break;
538 		    }
539 		}
540 
541 		if (!daSPImatch) {
542 		    return;
543 		}
544 	    }
545 	}
546 
547 	if (conf.traceDATraffic()) {
548 	    conf.writeLog("sdat_forward",
549 			  new Object[] {
550 		Integer.toHexString(hdr.xid),
551 		    addr});
552 
553 	}
554 
555 
556 	// Send it via TCP. DAs should understand TCP, and it's reliable.
557 
558 	SrvLocMsg rply = null;
559 
560 	try {
561 
562 	    // Construct the client side message, for outgoing.
563 
564 	    if (rqst instanceof SSrvReg) {
565 		SSrvReg rrqst = (SSrvReg)rqst;
566 		CSrvReg msg = new CSrvReg(hdr.fresh,
567 					  hdr.locale,
568 					  rrqst.URL,
569 					  hdr.scopes,
570 					  rrqst.attrList,
571 					  rrqst.URLSignature,
572 					  rrqst.attrSignature);
573 		rply = msg;
574 
575 	    } else if (rqst instanceof SSrvDereg) {
576 		SSrvDereg drqst = (SSrvDereg)rqst;
577 		CSrvDereg msg = new CSrvDereg(hdr.locale,
578 					      drqst.URL,
579 					      hdr.scopes,
580 					      drqst.tags,
581 					      drqst.URLSignature);
582 		rply = msg;
583 
584 	    } else if (rqst instanceof CSrvReg) {
585 		rply = rqst;
586 
587 	    }
588 
589 	    rply = Transact.transactTCPMsg(addr, rply, false);
590 
591 	} catch (ServiceLocationException ex) {
592 
593 	    if (conf.traceDATraffic()) {
594 		conf.writeLog("sdat_forward_exception",
595 			      new Object[] {
596 		    Integer.toHexString(hdr.xid),
597 			addr,
598 			new Integer(ex.getErrorCode()),
599 			ex.getMessage()});
600 
601 	    }
602 	}
603 
604 	// Report any errors.
605 
606 	if (rply == null ||
607 	    rply.getErrorCode() != ServiceLocationException.OK) {
608 	    if (conf.traceDATraffic()) {
609 		conf.writeLog("sdat_forward_err",
610 			      new Object[] {
611 		    Integer.toHexString(hdr.xid),
612 			addr,
613 			(rply == null ? "<null>":
614 			 Integer.toString(rply.getErrorCode()))});
615 
616 	    }
617 	}
618     }
619 }
620