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