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