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 getServerDATable()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 recordNewDA(ServiceURL url, Vector scopes, long timestamp, int version, Vector attrs, String spis)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 returnMatchingDAs(String query)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 forwardSAMessage(SrvLocMsg msg, InetAddress source)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 makeKey(ServiceURL url, Locale locale)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 handleAdvertIn(CDAAdvert advert)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 forwardRegistrations(ServiceURL url, Vector scopes, long timestamp, int version)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 forwardRegOrDereg(InetAddress addr, SrvLocMsg rqst)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