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 // ServiceTable.java: Storage of all services. 28 // Author: James Kempf 29 // Created On: Fri Oct 10 14:23:25 1997 30 // Last Modified By: James Kempf 31 // Last Modified On: Thu Apr 1 10:33:46 1999 32 // Update Count: 461 33 // 34 35 package com.sun.slp; 36 37 import java.util.*; 38 import java.io.*; 39 import java.security.*; 40 import java.net.*; 41 42 /** 43 * The ServiceTable object records all service registrations. Note 44 * that any exceptions internal to the service table are processed 45 * and either returned as SrvRply objects or are reported. 46 * 47 * @author James Kempf 48 */ 49 50 class ServiceTable extends Object { 51 52 // Key for SDAAdvert class. 53 54 static final String SDAADVERT = "com.sun.slp.SDAAdvert"; 55 56 private static final String locationMsg = "Service table"; 57 58 // 59 // Instance variables. 60 // 61 62 // The service store. 63 64 protected ServiceStore store = null; 65 66 // 67 // Class variables. 68 69 // System properties. 70 71 static protected SLPConfig conf = null; 72 73 // Singleton objects for the service tables. 74 75 static protected ServiceTable table = null; 76 77 // The ager thread. 78 79 static protected AgerThread thrAger = null; 80 81 // Time to sleep. Adjusted depending on incoming URLs. 82 83 private static long sleepyTime = Defaults.lMaxSleepTime; 84 85 // 86 // Creation of singleton. 87 // 88 89 // Protected constructor. 90 91 protected ServiceTable() { 92 93 if (thrAger != null) { 94 return; 95 96 } 97 98 // Create the ager thread. 99 100 thrAger = new AgerThread(); 101 102 // Set the priority low, so other things (like active discovery) 103 // take priority. 104 105 thrAger.setPriority(Thread.MIN_PRIORITY); 106 107 thrAger.start(); 108 109 } 110 111 /** 112 * Return an SA service store. 113 * 114 * @return The distinguished table object. 115 */ 116 117 static ServiceTable getServiceTable() 118 throws ServiceLocationException { 119 120 if (conf == null) { 121 conf = SLPConfig.getSLPConfig(); 122 123 } 124 125 if (table == null) { 126 127 table = createServiceTable(); 128 129 } 130 131 return table; 132 } 133 134 /** 135 * Return a service table object. 136 * 137 * @return The service table object. 138 */ 139 140 private static ServiceTable createServiceTable() 141 throws ServiceLocationException { 142 143 ServiceTable table = new ServiceTable(); 144 145 table.store = ServiceStoreFactory.createServiceStore(); 146 147 return table; 148 } 149 150 // 151 // Support for serializated registrations. 152 // 153 154 /** 155 * If any serialized registrations are pending, then unserialize 156 * and register. 157 */ 158 159 public void deserializeTable() { 160 161 // If there are any serialized registrations, then get 162 // them and perform registrations. 163 164 String serializedURL = conf.getSerializedRegURL(); 165 166 if (serializedURL != null) { 167 168 ServiceStore serStore = getStoreFromURL(serializedURL); 169 170 if (serStore != null) { 171 registerStore(serStore); 172 } 173 } 174 } 175 176 /** 177 * Serialize the table to the URL. 178 * 179 * @param URL String giving the URL to which the store should be 180 * serialized. 181 */ 182 183 void serializeServiceStore(String URL) { 184 185 // Open an object output stream for the URL, serialize through 186 // the factory. 187 188 try { 189 190 URL url = new URL(URL); 191 URLConnection urlConn = url.openConnection(); 192 OutputStream os = urlConn.getOutputStream(); 193 BufferedWriter di = 194 new BufferedWriter(new OutputStreamWriter(os)); 195 196 // Serialize the store. 197 198 ServiceStoreFactory.serialize(di, store); 199 200 } catch (MalformedURLException ex) { 201 202 conf.writeLog("st_serialized_malform", 203 new Object[] {URL}); 204 205 } catch (UnsupportedEncodingException ex) { 206 207 conf.writeLog("st_unsupported_encoding", 208 new Object[] {URL}); 209 210 } catch (IOException ex) { 211 212 conf.writeLog("st_serialized_ioexception", 213 new Object[] {URL, ex}); 214 215 } catch (ServiceLocationException ex) { 216 217 conf.writeLog("st_serialized_sle", 218 new Object[] {URL, ex.getMessage()}); 219 220 } 221 222 } 223 224 // Read proxy registrations from the URL. 225 226 private ServiceStore getStoreFromURL(String serializedURL) { 227 228 ServiceStore serStore = null; 229 230 // Open an object input stream for the URL, deserialize through 231 // the factory. 232 233 try { 234 235 URL url = new URL(serializedURL); 236 InputStream is = url.openStream(); 237 BufferedReader di = new BufferedReader(new InputStreamReader(is)); 238 239 // Deserialize the objects. 240 241 serStore = 242 ServiceStoreFactory.deserializeServiceStore(di); 243 244 } catch (MalformedURLException ex) { 245 246 conf.writeLog("st_serialized_malform", 247 new Object[] {serializedURL}); 248 249 } catch (UnsupportedEncodingException ex) { 250 251 conf.writeLog("st_unsupported_encoding", 252 new Object[] {serializedURL}); 253 254 } catch (IOException ex) { 255 256 conf.writeLog("st_serialized_ioexception", 257 new Object[] { 258 serializedURL, 259 ex.getMessage()}); 260 261 } catch (ServiceLocationException ex) { 262 263 conf.writeLog("st_serialized_sle", 264 new Object[] { 265 serializedURL, 266 ex.getMessage()}); 267 268 } 269 270 return serStore; 271 } 272 273 // Walk the table, performing actual registrations on all records. 274 275 private void registerStore(ServiceStore serStore) { 276 277 // Walk the table. 278 279 Enumeration en = serStore.getServiceRecordsByScope(null); 280 boolean hasURLSig = conf.getHasSecurity(); 281 boolean hasAttrSig = conf.getHasSecurity(); 282 PermSARegTable pregTable = SARequester.getPermSARegTable(); 283 284 while (en.hasMoreElements()) { 285 ServiceStore.ServiceRecord rec = 286 (ServiceStore.ServiceRecord)en.nextElement(); 287 ServiceURL surl = rec.getServiceURL(); 288 Vector scopes = rec.getScopes(); 289 Vector attrs = rec.getAttrList(); 290 Locale locale = rec.getLocale(); 291 Hashtable urlSig = null; 292 Hashtable attrSig = null; 293 294 // Note that we can't use the Advertiser to register here, 295 // because we may not be listening yet for registrations. 296 // We need to do this all by hand. 297 298 try { 299 300 // Create a registration message for refreshing. 301 302 CSrvReg creg = new CSrvReg(false, 303 locale, 304 surl, 305 scopes, 306 attrs, 307 null, 308 null); 309 310 // We externalize to a server side message if authentication 311 // is needed. This creates the auth blocks for the scopes. 312 // Doing this in any other way is alot more complicated, 313 // although doing it this way seems kludgy. 314 315 if (hasURLSig || hasAttrSig) { 316 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 317 318 creg.getHeader().externalize(baos, false, true); 319 320 ByteArrayInputStream bais = 321 new ByteArrayInputStream(baos.toByteArray()); 322 bais.read(); // pop off version and function code... 323 bais.read(); 324 DataInputStream dis = new DataInputStream(bais); 325 SLPHeaderV2 hdr = new SLPHeaderV2(); 326 hdr.parseHeader(SrvLocHeader.SrvReg, dis); 327 SSrvReg sreg = new SSrvReg(hdr, dis); 328 329 // Now we've got it, after much effort. Get the auths. 330 331 urlSig = sreg.URLSignature; 332 attrSig = sreg.attrSignature; 333 334 } 335 336 store.register(surl, attrs, scopes, locale, urlSig, attrSig); 337 338 // Now we've got to put the registration into the 339 // PermSARegTable. Again, we do everything by hand 340 // because we can't use Advertiser. 341 342 if (surl.getIsPermanent()) { 343 pregTable.reg(surl, creg); 344 345 } 346 347 // Report registration. 348 349 if (conf.regTest()) { 350 conf.writeLog("st_reg_add", 351 new Object[] { 352 locationMsg, 353 locale, 354 surl.getServiceType(), 355 surl, 356 attrs, 357 scopes}); 358 359 } 360 } catch (ServiceLocationException ex) { 361 362 String msg = ex.getMessage(); 363 364 conf.writeLog("st_serialized_seex", 365 new Object[] { 366 new Integer(ex.getErrorCode()), 367 surl, 368 (msg == null ? "<no message>":msg)}); 369 370 } catch (Exception ex) { 371 372 String msg = ex.getMessage(); 373 374 conf.writeLog("st_serialized_seex", 375 new Object[] { 376 surl, 377 (msg == null ? "<no message>":msg)}); 378 } 379 } 380 } 381 382 // 383 // Record aging. 384 // 385 386 // 387 // Run the thread that ages out records. 388 // 389 390 private class AgerThread extends Thread { 391 392 public void run() { 393 394 setName("SLP Service Table Age-out"); 395 long alarmTime = sleepyTime; // when to wake up next 396 long wentToSleep = 0; // what time we went to bed 397 398 while (true) { 399 400 try { 401 402 // Record when we went to sleep. 403 404 wentToSleep = System.currentTimeMillis(); 405 406 // Sleep for the minimum amount of time needed before we 407 // must wake up and check. 408 409 sleep(alarmTime); 410 411 } catch (InterruptedException ie) { 412 413 // A new registration came in. Calculate how much time 414 // remains until we would have woken up. If this is 415 // less than the new sleepyTime, then we set the alarm 416 // for this time. If it is more, then we set the alarm 417 // for the new sleepyTime. 418 419 long remainingSleepTime = 420 (wentToSleep + alarmTime) - System.currentTimeMillis(); 421 422 remainingSleepTime = // just in case... 423 ((remainingSleepTime <= 0) ? 0 : remainingSleepTime); 424 425 alarmTime = sleepyTime; 426 427 if (remainingSleepTime < alarmTime) { 428 alarmTime = remainingSleepTime; 429 430 } 431 432 continue; // we don't have to walk yet... 433 434 } 435 436 // Walk the table, get the new alarm and sleepy times. 437 438 if (table != null) { 439 table.ageStore(); 440 441 alarmTime = sleepyTime; 442 443 } 444 } 445 } 446 447 } 448 449 /** 450 * Age the service store. 451 */ 452 453 // this method cannot be private... due to compiler weakness 454 void ageStore() { 455 456 try { 457 458 // We synchronize in case somebody registers and tries to 459 // change sleepy time. 460 461 synchronized (store) { 462 Vector deleted = new Vector(); 463 464 sleepyTime = store.ageOut(deleted); 465 466 // Track unregistered services. 467 468 int i, n = deleted.size(); 469 470 for (i = 0; i < n; i++) { 471 ServiceStore.ServiceRecord rec = 472 (ServiceStore.ServiceRecord)deleted.elementAt(i); 473 ServiceURL surl = rec.getServiceURL(); 474 475 trackRegisteredServiceTypes(); // it's deleted... 476 477 } 478 479 } 480 481 } catch (RuntimeException ex) { 482 483 reportNonfatalException(ex, new Vector(), store); 484 485 } catch (ServiceLocationException ex) { 486 487 reportNonfatalException(ex, new Vector(), store); 488 489 } 490 491 } 492 493 // 494 // SLP Service Table operations (register, deregister, etc.) 495 // 496 497 /** 498 * Process the registration and record if no errors found. 499 * 500 * @param req Service registration request message. 501 * @return SrvLocMsg A service registration acknowledgement. 502 */ 503 504 SrvLocMsg register(SSrvReg req) { 505 506 SrvLocHeader hdr = req.getHeader(); 507 Locale locale = hdr.locale; 508 boolean fresh = hdr.fresh; 509 Vector scopes = hdr.scopes; 510 ServiceURL surl = req.URL; 511 String serviceType = req.serviceType; 512 Vector attrList = req.attrList; 513 Hashtable urlSig = req.URLSignature; 514 Hashtable attrSig = req.attrSignature; 515 short errorCode = 516 (fresh ? ServiceLocationException.INVALID_REGISTRATION : 517 ServiceLocationException.INVALID_UPDATE); 518 519 try { 520 521 // If a sig block came in, verify it. 522 523 if (urlSig != null) { 524 525 AuthBlock.verifyAll(urlSig); 526 } 527 528 if (attrSig != null) { 529 530 AuthBlock.verifyAll(attrSig); 531 532 } 533 534 // Check whether the URL has a zero lifetime. If so, it 535 // isn't cached. 536 537 if (surl.getLifetime() <= 0) { 538 throw 539 new ServiceLocationException(errorCode, 540 "st_zero", 541 new Object[0]); 542 543 } 544 545 // Check if the service type is restricted. If so, nobody outside 546 // this process is allowed to register it. 547 548 checkForRestrictedType(surl.getServiceType()); 549 550 // Check that attribute signature bit on implies URL signature 551 // bit on. 552 553 if (attrSig != null && urlSig == null) { 554 throw 555 new ServiceLocationException(errorCode, 556 "st_attr_sig", 557 new Object[0]); 558 559 } 560 561 // If a signature and the fresh bit was not set, error since signed 562 // registrations don't allow updating. 563 564 if (urlSig != null && !fresh) { 565 throw 566 new ServiceLocationException( 567 ServiceLocationException.INVALID_UPDATE, 568 "st_prot_update", 569 new Object[0]); 570 } 571 572 // Check if scopes are supported. 573 574 if (!areSupportedScopes(scopes)) { 575 throw 576 new ServiceLocationException( 577 ServiceLocationException.SCOPE_NOT_SUPPORTED, 578 "st_scope_unsup", 579 new Object[0]); 580 } 581 582 // Check if the reg is signed and auth is off or vice versa. 583 // Check is really simple. If security is on, then all regs 584 // to this DA/SA server must be signed, so toss out any regs 585 // that aren't, and vice versa. 586 587 if (conf.getHasSecurity() && (urlSig == null || attrSig == null)) { 588 throw 589 new ServiceLocationException( 590 ServiceLocationException.AUTHENTICATION_FAILED, 591 "st_unprot_non_reg", 592 new Object[0]); 593 594 } else if (!conf.getHasSecurity() && 595 (urlSig != null || attrSig != null)) { 596 throw 597 new ServiceLocationException( 598 ServiceLocationException.INVALID_REGISTRATION, 599 "st_prot_non_reg", 600 new Object[0]); 601 602 } 603 604 // Merge any duplicates. 605 606 Vector attrs = new Vector(); 607 Hashtable attrHash = new Hashtable(); 608 int i, n = attrList.size(); 609 610 for (i = 0; i < n; i++) { 611 ServiceLocationAttribute attr = 612 (ServiceLocationAttribute)attrList.elementAt(i); 613 614 ServiceLocationAttribute.mergeDuplicateAttributes( 615 attr, 616 attrHash, 617 attrs, 618 false); 619 } 620 621 // Store register or update. 622 623 boolean existing = false; 624 625 if (fresh) { 626 existing = store.register(surl, 627 attrs, 628 scopes, 629 locale, 630 urlSig, 631 attrSig); 632 633 // Track registred service types in case we get a 634 // SAAdvert solicatation. 635 636 trackRegisteredServiceTypes(); 637 638 } else { 639 store.updateRegistration(surl, attrs, scopes, locale); 640 641 } 642 643 // Create the reply. 644 645 SrvLocMsg ack = req.makeReply(existing); 646 647 if (conf.regTest()) { 648 conf.writeLog((fresh ? "st_reg_add":"st_reg_update"), 649 new Object[] { 650 locationMsg, 651 locale, 652 serviceType, 653 surl, 654 attrs, 655 scopes}); 656 657 } 658 659 if (conf.traceAll()) { 660 conf.writeLog("st_dump", new Object[] {locationMsg}); 661 store.dumpServiceStore(); 662 663 } 664 665 // Calculate time increment until next update. This is used 666 // to adjust the sleep interval in the ager thread. 667 668 long sTime = getSleepIncrement(surl); 669 670 // We synchronize in case the ager thread is in the middle 671 // of trying to set the time. 672 673 synchronized (store) { 674 675 // If we need to wake up sooner, adjust the sleep time. 676 677 if (sTime < sleepyTime) { 678 679 sleepyTime = sTime; 680 681 // Interrupt the thread so we go back to 682 // sleep for the right amount of time. 683 684 thrAger.interrupt(); 685 } 686 } 687 688 return ack; 689 690 } catch (ServiceLocationException ex) { 691 692 if (conf.traceDrop()) { 693 conf.writeLog("st_reg_drop", 694 new Object[] { 695 locationMsg, 696 ex.getMessage()+"("+ex.getErrorCode()+")", 697 locale, 698 serviceType, 699 surl, 700 attrList, 701 scopes}); 702 } 703 704 return hdr.makeErrorReply(ex); 705 706 } catch (RuntimeException ex) { 707 708 // These exceptions are not declared in throws but can occur 709 // anywhere. 710 711 Vector args = new Vector(); 712 713 args.addElement(req); 714 715 reportNonfatalException(ex, args, store); 716 717 return hdr.makeErrorReply(ex); 718 719 } 720 } 721 722 /** 723 * Process the deregistration and return the result in a reply. 724 * 725 * @param req Service deregistration request message. 726 * @return SrvLocMsg A service registration acknowledgement. 727 */ 728 729 SrvLocMsg deregister(SSrvDereg req) { 730 731 // We need to determine whether this is an attribute deregistration 732 // or a deregistration of the entire URL. 733 734 SrvLocHeader hdr = req.getHeader(); 735 Locale locale = hdr.locale; 736 Vector scopes = hdr.scopes; 737 ServiceURL surl = req.URL; 738 Hashtable urlSig = req.URLSignature; 739 Vector tags = req.tags; 740 short errorCode = ServiceLocationException.OK; 741 742 try { 743 744 // Verify if signature is nonnull. 745 746 if (urlSig != null) { 747 AuthBlock.verifyAll(urlSig); 748 749 } 750 751 // Check if the service type is restricted. If so, nobody outside 752 // this process is allowed to register it. 753 754 checkForRestrictedType(surl.getServiceType()); 755 756 // Error if there's a signature and attempt at deleting attributes. 757 758 if ((urlSig != null) && (tags != null)) { 759 throw 760 new ServiceLocationException( 761 ServiceLocationException.AUTHENTICATION_FAILED, 762 "st_prot_attr_dereg", 763 new Object[0]); 764 } 765 766 // Check if scope is protected and auth is off or vice versa. 767 // Check is really simple. If security is on, then all scopes 768 // in this DA/SA server are protected, so toss out any regs 769 // that aren't, and vice versa. 770 771 if (conf.getHasSecurity() && urlSig == null) { 772 throw 773 new ServiceLocationException( 774 ServiceLocationException.AUTHENTICATION_FAILED, 775 "st_unprot_non_dereg", 776 new Object[0]); 777 778 } else if (!conf.getHasSecurity() && urlSig != null) { 779 throw 780 new ServiceLocationException( 781 ServiceLocationException.INVALID_REGISTRATION, 782 "st_prot_non_dereg", 783 new Object[0]); 784 785 } 786 787 // If it's a service URL, then deregister the URL. 788 789 if (tags == null) { 790 store.deregister(surl, scopes, urlSig); 791 792 // Track registred service types in case we get a 793 // SAAdvert solicatation. 794 795 trackRegisteredServiceTypes(); 796 797 } else { 798 799 // Just delete the attributes. 800 801 store.deleteAttributes(surl, scopes, tags, locale); 802 803 } 804 805 // Create the reply. 806 807 SrvLocMsg ack = req.makeReply(); 808 809 if (conf.regTest()) { 810 conf.writeLog((tags == null ? "st_dereg":"st_delattr"), 811 new Object[] { 812 locationMsg, 813 locale, 814 surl.getServiceType(), 815 surl, 816 tags}); 817 818 } 819 820 if (conf.traceAll()) { 821 conf.writeLog("st_dump", 822 new Object[] {locationMsg}); 823 store.dumpServiceStore(); 824 825 } 826 827 return ack; 828 829 } catch (ServiceLocationException ex) { 830 831 if (conf.traceDrop()) { 832 conf.writeLog((tags == null ? 833 "st_dereg_drop" : "st_dereg_attr_drop"), 834 new Object[] { 835 locationMsg, 836 ex.getMessage()+"("+ex.getErrorCode()+")", 837 locale, 838 surl.getServiceType(), 839 surl, 840 tags}); 841 } 842 843 return hdr.makeErrorReply(ex); 844 845 } catch (RuntimeException ex) { 846 847 // These exceptions are not declared in throws but can occur 848 // anywhere. 849 850 Vector args = new Vector(); 851 852 args.addElement(req); 853 854 reportNonfatalException(ex, args, store); 855 856 return hdr.makeErrorReply(ex); 857 858 } 859 } 860 861 /** 862 * Process the service type request and return the result in a reply. 863 * 864 * @param req Service type request message. 865 * @return SrvTypeRply A service type reply. 866 */ 867 868 SrvLocMsg findServiceTypes(SSrvTypeMsg req) { 869 870 SrvLocHeader hdr = req.getHeader(); 871 Vector scopes = hdr.scopes; 872 String namingAuthority = req.namingAuthority; 873 short errorCode = ServiceLocationException.OK; 874 875 try { 876 877 // Check whether the scope is supported. 878 879 if (!areSupportedScopes(scopes)) { 880 throw 881 new ServiceLocationException( 882 ServiceLocationException.SCOPE_NOT_SUPPORTED, 883 "st_scope_unsup", 884 new Object[0]); 885 886 } 887 888 // Get the vector of service types in the store, independent 889 // of language. 890 891 Vector types = store.findServiceTypes(namingAuthority, scopes); 892 893 // Create the reply. 894 895 SrvLocMsg ack = req.makeReply(types); 896 897 if (conf.traceAll()) { 898 conf.writeLog("st_stypes", 899 new Object[] { 900 locationMsg, 901 namingAuthority, 902 scopes, 903 types}); 904 } 905 906 return ack; 907 908 } catch (ServiceLocationException ex) { 909 910 if (conf.traceDrop()) { 911 conf.writeLog("st_stypes_drop", 912 new Object[] { 913 locationMsg, 914 ex.getMessage()+"("+ex.getErrorCode()+")", 915 namingAuthority, 916 scopes, 917 hdr.locale}); 918 } 919 920 return hdr.makeErrorReply(ex); 921 922 } catch (RuntimeException ex) { 923 924 // These exceptions are not declared in throws but can occur 925 // anywhere. 926 927 Vector args = new Vector(); 928 929 args.addElement(req); 930 931 reportNonfatalException(ex, args, store); 932 933 return hdr.makeErrorReply(ex); 934 935 } 936 } 937 938 /** 939 * Process the service request and return the result in a reply. 940 * 941 * @param req Service request message. 942 * @return SrvRply A service reply. 943 */ 944 945 SrvLocMsg findServices(SSrvMsg req) { 946 947 SrvLocHeader hdr = req.getHeader(); 948 Locale locale = hdr.locale; 949 Vector scopes = hdr.scopes; 950 String serviceType = req.serviceType; 951 String query = req.query; 952 short errorCode = ServiceLocationException.OK; 953 954 try { 955 956 // Check whether the scope is supported. 957 958 if (!areSupportedScopes(scopes)) { 959 throw 960 new ServiceLocationException( 961 ServiceLocationException.SCOPE_NOT_SUPPORTED, 962 "st_scope_unsup", 963 new Object[0]); 964 } 965 966 // Get the hashtable of returns. 967 968 Hashtable returns = 969 store.findServices(serviceType, 970 scopes, 971 query, 972 locale); 973 974 // Get the hashtable of services v.s. scopes, and signatures, if 975 // any. 976 977 Hashtable services = 978 (Hashtable)returns.get(ServiceStore.FS_SERVICES); 979 Hashtable signatures = 980 (Hashtable)returns.get(ServiceStore.FS_SIGTABLE); 981 boolean hasSignatures = (signatures != null); 982 983 // for each candidate URL, make sure it has the requested SPI 984 // (if any) 985 if (hasSignatures && !req.spi.equals("")) { 986 Enumeration allSurls = services.keys(); 987 while (allSurls.hasMoreElements()) { 988 Object aSurl = allSurls.nextElement(); 989 Hashtable auths = (Hashtable) signatures.get(aSurl); 990 AuthBlock auth = 991 AuthBlock.getEquivalentAuth(req.spi, auths); 992 if (auth == null) { 993 // doesn't have the requested SPI 994 services.remove(aSurl); 995 } 996 } 997 } 998 999 // Create return message. 1000 1001 SrvLocMsg ack = req.makeReply(services, signatures); 1002 1003 if (conf.traceAll()) { 1004 conf.writeLog("st_sreq", 1005 new Object[] { 1006 locationMsg, 1007 serviceType, 1008 scopes, 1009 query, 1010 locale, 1011 services, 1012 signatures}); 1013 } 1014 1015 return ack; 1016 1017 } catch (ServiceLocationException ex) { 1018 1019 if (conf.traceDrop()) { 1020 conf.writeLog("st_sreq_drop", 1021 new Object[] { 1022 locationMsg, 1023 ex.getMessage()+"("+ex.getErrorCode()+")", 1024 serviceType, 1025 scopes, 1026 query, 1027 locale}); 1028 } 1029 1030 return hdr.makeErrorReply(ex); 1031 1032 } catch (RuntimeException ex) { 1033 1034 // These exceptions are not declared in throws but can occur 1035 // anywhere. 1036 1037 Vector args = new Vector(); 1038 1039 args.addElement(req); 1040 1041 reportNonfatalException(ex, args, store); 1042 1043 return hdr.makeErrorReply(ex); 1044 1045 } 1046 } 1047 1048 /** 1049 * Process the attribute request and return the result in a reply. 1050 * 1051 * @param req Attribute request message. 1052 * @return AttrRply An attribute reply. 1053 */ 1054 1055 SrvLocMsg findAttributes(SAttrMsg req) { 1056 1057 // We need to determine whether this is a request for attributes 1058 // on a specific URL or for an entire service type. 1059 1060 SrvLocHeader hdr = req.getHeader(); 1061 Vector scopes = hdr.scopes; 1062 Locale locale = hdr.locale; 1063 ServiceURL surl = req.URL; 1064 String serviceType = req.serviceType; 1065 Vector tags = req.tags; 1066 short errorCode = ServiceLocationException.OK; 1067 1068 try { 1069 1070 // Check whether the scope is supported. 1071 1072 if (!areSupportedScopes(scopes)) { 1073 throw 1074 new ServiceLocationException( 1075 ServiceLocationException.SCOPE_NOT_SUPPORTED, 1076 "st_scope_unsup", 1077 new Object[0]); 1078 } 1079 1080 Vector attributes = null; 1081 Hashtable sig = null; 1082 1083 // If it's a service URL, then get the attributes just for 1084 // that URL. 1085 1086 if (serviceType == null) { 1087 1088 // If the attrs are signed, then error if any tags, since 1089 // we must ask for *all* attributes in for a signed reg 1090 1091 if (!req.spi.equals("") && tags.size() > 0) { 1092 throw 1093 new ServiceLocationException( 1094 ServiceLocationException.AUTHENTICATION_FAILED, 1095 "st_par_attr", 1096 new Object[0]); 1097 1098 } 1099 1100 Hashtable ht = 1101 store.findAttributes(surl, scopes, tags, locale); 1102 1103 // Get the attributes and signatures. 1104 1105 attributes = (Vector)ht.get(ServiceStore.FA_ATTRIBUTES); 1106 1107 sig = (Hashtable)ht.get(ServiceStore.FA_SIG); 1108 1109 // make sure the attr has the requested SPI (if any) 1110 if (sig != null && !req.spi.equals("")) { 1111 AuthBlock auth = AuthBlock.getEquivalentAuth(req.spi, sig); 1112 if (auth == null) { 1113 // return empty 1114 attributes = new Vector(); 1115 } 1116 } 1117 1118 } else { 1119 1120 if (!req.spi.equals("")) { 1121 throw 1122 new ServiceLocationException( 1123 ServiceLocationException.AUTHENTICATION_FAILED, 1124 "st_par_attr", 1125 new Object[0]); 1126 } 1127 1128 // Otherwise find the attributes for all service types. 1129 1130 attributes = 1131 store.findAttributes(serviceType, scopes, tags, locale); 1132 1133 } 1134 1135 ServiceType type = 1136 (serviceType == null ? surl.getServiceType(): 1137 new ServiceType(serviceType)); 1138 1139 1140 // Create the reply. 1141 1142 SrvLocMsg ack = req.makeReply(attributes, sig); 1143 1144 if (conf.traceAll()) { 1145 conf.writeLog((serviceType != null ? 1146 "st_st_attr" : "st_url_attr"), 1147 new Object[] { 1148 locationMsg, 1149 (serviceType != null ? serviceType.toString() : 1150 surl.toString()), 1151 scopes, 1152 tags, 1153 locale, 1154 attributes}); 1155 } 1156 1157 return ack; 1158 1159 } catch (ServiceLocationException ex) { 1160 1161 if (conf.traceDrop()) { 1162 conf.writeLog((serviceType != null ? "st_st_attr_drop": 1163 "st_url_attr_drop"), 1164 new Object[] { 1165 locationMsg, 1166 ex.getMessage()+"("+ex.getErrorCode()+")", 1167 (serviceType != null ? serviceType.toString() : 1168 surl.toString()), 1169 scopes, 1170 tags, 1171 locale}); 1172 } 1173 1174 return hdr.makeErrorReply(ex); 1175 1176 } catch (RuntimeException ex) { 1177 1178 // These exceptions are not declared in throws but can occur 1179 // anywhere. 1180 1181 Vector args = new Vector(); 1182 1183 args.addElement(req); 1184 1185 reportNonfatalException(ex, args, store); 1186 1187 return hdr.makeErrorReply(ex); 1188 1189 } 1190 } 1191 1192 // Return the service record corresponding to the URL. 1193 1194 ServiceStore.ServiceRecord getServiceRecord(ServiceURL URL, 1195 Locale locale) { 1196 return store.getServiceRecord(URL, locale); 1197 1198 } 1199 1200 // 1201 // Utility methods. 1202 // 1203 1204 // 1205 // Protected/private methods. 1206 // 1207 1208 // Check whether the type is restricted, through an exception if so. 1209 1210 private void checkForRestrictedType(ServiceType type) 1211 throws ServiceLocationException { 1212 1213 if (Defaults.restrictedTypes.contains(type)) { 1214 throw 1215 new ServiceLocationException( 1216 ServiceLocationException.INVALID_REGISTRATION, 1217 "st_restricted_type", 1218 new Object[] {type}); 1219 } 1220 } 1221 1222 // Insert a record for type "service-agent" with attributes having 1223 // the types currently supported, if the new URL is not on the 1224 // list of supported types. This allows us to perform queries 1225 // for supported service types. 1226 1227 private void trackRegisteredServiceTypes() 1228 throws ServiceLocationException { 1229 1230 // First find the types. 1231 1232 Vector types = store.findServiceTypes(Defaults.ALL_AUTHORITIES, 1233 conf.getSAConfiguredScopes()); 1234 1235 // Get preconfigured attributes. 1236 1237 Vector attrs = conf.getSAAttributes(); 1238 1239 // Make an attribute with the service types. 1240 1241 ServiceLocationAttribute attr = 1242 new ServiceLocationAttribute(Defaults.SERVICE_TYPE_ATTR_ID, 1243 types); 1244 1245 attrs.addElement(attr); 1246 1247 // Construct URL to use on all interfaces. 1248 1249 Vector interfaces = conf.getInterfaces(); 1250 int i, n = interfaces.size(); 1251 1252 for (i = 0; i < n; i++) { 1253 InetAddress addr = (InetAddress)interfaces.elementAt(i); 1254 ServiceURL url = 1255 new ServiceURL(Defaults.SUN_SA_SERVICE_TYPE + "://" + 1256 addr.getHostAddress(), 1257 ServiceURL.LIFETIME_MAXIMUM); 1258 1259 Vector scopes = conf.getSAOnlyScopes(); 1260 1261 Locale locale = Defaults.locale; 1262 1263 // Make a new registration for this SA. 1264 1265 store.register(url, 1266 attrs, 1267 scopes, 1268 locale, 1269 null, 1270 null); // we could sign, but we do that later... 1271 } 1272 1273 // Note that we don't need a refresh on the URLs because they 1274 // will get refreshed when the service URLs that they track 1275 // are refreshed. If the tracked URLs aren't refreshed, then 1276 // these will get updated when the tracked URLs age out. 1277 } 1278 1279 // Return true if the scopes in the vector are supported by the DA 1280 // or SA server. 1281 1282 final private boolean areSupportedScopes(Vector scopes) { 1283 1284 Vector configuredScopes = conf.getSAConfiguredScopes(); 1285 Vector saOnlyScopes = conf.getSAOnlyScopes(); 1286 int i = 0; 1287 1288 while (i < scopes.size()) { 1289 Object o = scopes.elementAt(i); 1290 1291 // Remove it if we don't support it. 1292 1293 if (!configuredScopes.contains(o) && !saOnlyScopes.contains(o)) { 1294 // This will shift the Vector's elements down one, so 1295 // don't increment i 1296 scopes.removeElementAt(i); 1297 } else { 1298 i++; 1299 } 1300 } 1301 1302 if (scopes.size() <= 0) { 1303 return false; 1304 1305 } 1306 1307 return true; 1308 } 1309 1310 /** 1311 * Return the sleep increment from the URL lifetime. Used by the 1312 * ServiceStore to calculate the new sleep interval in addition 1313 * to this class, when a new URL comes in. The algorithm 1314 * subtracts x% of the lifetime from the lifetime and schedules the 1315 * timeout at that time. 1316 * 1317 * @param url The URL to use for calculation. 1318 * @return The sleep interval. 1319 */ 1320 1321 private long getSleepIncrement(ServiceURL url) { 1322 long urlLifetime = (long)(url.getLifetime() * 1000); 1323 long increment = 1324 (long)((float)urlLifetime * Defaults.fRefreshGranularity); 1325 long sTime = urlLifetime - increment; 1326 1327 // If URL lives only one second, update every half second. 1328 1329 if (sTime <= 0) { 1330 sTime = 500; 1331 1332 } 1333 1334 return sTime; 1335 } 1336 1337 // Make a DAADvert for the DA service request. This only applies 1338 // to DAs, not to SA servers. 1339 1340 SrvLocMsg 1341 makeDAAdvert(SSrvMsg rqst, 1342 InetAddress daAddr, 1343 SLPConfig conf) { 1344 1345 SrvLocHeader hdr = rqst.getHeader(); 1346 Vector scopes = hdr.scopes; 1347 short xid = hdr.xid; 1348 String query = rqst.query; 1349 1350 try { 1351 1352 // If security is on, proceed only if we can sign as rqst.spi 1353 if (conf.getHasSecurity() && !AuthBlock.canSignAs(rqst.spi)) { 1354 throw new ServiceLocationException( 1355 ServiceLocationException.AUTHENTICATION_UNKNOWN, 1356 "st_cant_sign_as", 1357 new Object[] {rqst.spi}); 1358 } 1359 1360 // Get the hashtable of service URLs v.s. scopes. 1361 1362 Hashtable services = 1363 ServerDATable.getServerDATable().returnMatchingDAs(query); 1364 1365 // Go through the table checking whether the IP address came back. 1366 1367 Enumeration urls = services.keys(); 1368 boolean foundIt = false; 1369 String strDAAddr = daAddr.getHostAddress(); 1370 1371 while (urls.hasMoreElements()) { 1372 ServiceURL url = (ServiceURL)urls.nextElement(); 1373 1374 if (url.getHost().equals(strDAAddr)) { 1375 foundIt = true; 1376 break; 1377 1378 } 1379 } 1380 1381 // If we didn't find anything, make a null service reply. 1382 1383 if (!foundIt) { 1384 return rqst.makeReply(new Hashtable(), new Hashtable()); 1385 1386 } 1387 1388 return makeDAAdvert(hdr, daAddr, xid, scopes, conf); 1389 1390 1391 } catch (ServiceLocationException ex) { 1392 1393 return hdr.makeErrorReply(ex); 1394 1395 } 1396 1397 } 1398 1399 // Make a DAAdvert from the input arguments. 1400 SrvLocMsg 1401 makeDAAdvert(SrvLocHeader hdr, 1402 InetAddress daAddr, 1403 short xid, 1404 Vector scopes, 1405 SLPConfig config) 1406 throws ServiceLocationException { 1407 1408 // If this is a request for a V1 Advert, truncate the scopes vector 1409 // since DA solicitations in V1 are always unscoped 1410 1411 if (hdr.version == 1) { 1412 scopes = new Vector(); 1413 1414 } 1415 1416 // Check if we support scopes first. If not, return an 1417 // error reply unless the scope vector is zero. Upper layers 1418 // must sort out whether this is a unicast or multicast. 1419 1420 if (scopes.size() > 0 && !areSupportedScopes(scopes)) { 1421 throw 1422 new ServiceLocationException( 1423 ServiceLocationException.SCOPE_NOT_SUPPORTED, 1424 "st_scope_unsup", 1425 new Object[0]); 1426 1427 } 1428 1429 // Get the service store's timestamp. This must be the 1430 // time since last stateless reboot for a stateful store, 1431 // or the current time. 1432 1433 long timestamp = store.getStateTimestamp(); 1434 1435 ServiceURL url = 1436 new ServiceURL(Defaults.DA_SERVICE_TYPE + "://" + 1437 daAddr.getHostAddress(), 1438 ServiceURL.LIFETIME_DEFAULT); 1439 1440 SDAAdvert advert = 1441 hdr.getDAAdvert(xid, 1442 timestamp, 1443 url, 1444 scopes, 1445 conf.getDAAttributes()); 1446 1447 return advert; 1448 } 1449 1450 // Make a SAADvert for the SA service request. This only applies 1451 // to SA servers, not DA's. Note that we only advertise the "public" 1452 // scopes, not the private ones. 1453 1454 SSAAdvert 1455 makeSAAdvert(SSrvMsg rqst, 1456 InetAddress interfac, 1457 SLPConfig conf) 1458 throws ServiceLocationException { 1459 SrvLocHeader hdr = rqst.getHeader(); 1460 int version = hdr.version; 1461 short xid = hdr.xid; 1462 Locale locale = hdr.locale; 1463 Vector scopes = hdr.scopes; 1464 String query = rqst.query; 1465 String serviceType = rqst.serviceType; 1466 Vector saOnlyScopes = conf.getSAOnlyScopes(); 1467 1468 // If security is on, proceed only if we can sign as rqst.spi 1469 if (conf.getHasSecurity() && !AuthBlock.canSignAs(rqst.spi)) { 1470 throw new ServiceLocationException( 1471 ServiceLocationException.AUTHENTICATION_UNKNOWN, 1472 "st_cant_sign_as", 1473 new Object[] {rqst.spi}); 1474 } 1475 1476 1477 // Check if we support scopes first. Note that this may allow 1478 // someone to get at the SA only scopes off machine, but that's 1479 // OK. Since the SAAdvert is only ever multicast, this is OK. 1480 1481 if (!areSupportedScopes(scopes) && !(scopes.size() <= 0)) { 1482 return null; 1483 1484 } 1485 1486 // If the scopes vector is null, then use all configured scopes. 1487 1488 if (scopes.size() <= 0) { 1489 scopes = (Vector)conf.getSAConfiguredScopes().clone(); 1490 1491 } 1492 1493 // Check to be sure the query matches. 1494 // If it doesn't, we don't need to return anything. 1495 1496 Hashtable returns = 1497 store.findServices(Defaults.SUN_SA_SERVICE_TYPE.toString(), 1498 saOnlyScopes, 1499 query, 1500 Defaults.locale); 1501 Hashtable services = 1502 (Hashtable)returns.get(ServiceStore.FS_SERVICES); 1503 Enumeration en = services.keys(); 1504 1505 // Indicates we don't support the service type. 1506 1507 if (!en.hasMoreElements()) { 1508 return null; 1509 1510 } 1511 1512 // Find the URL to use. The interface on which the message came in 1513 // needs to match one of the registered URLs. 1514 1515 ServiceURL url = null; 1516 ServiceURL surl = null; 1517 String addr = interfac.getHostAddress(); 1518 1519 while (en.hasMoreElements()) { 1520 surl = (ServiceURL)en.nextElement(); 1521 1522 if (addr.equals(surl.getHost())) { 1523 url = new ServiceURL(Defaults.SA_SERVICE_TYPE + "://" + 1524 addr, 1525 ServiceURL.LIFETIME_DEFAULT); 1526 break; 1527 } 1528 } 1529 1530 // If none of the URLs matched this interface, then return null. 1531 1532 if (url == null) { 1533 return null; 1534 1535 } 1536 1537 // Find the SA's attributes. 1538 1539 Hashtable ht = 1540 store.findAttributes(surl, 1541 saOnlyScopes, 1542 new Vector(), 1543 Defaults.locale); 1544 1545 Vector attrs = (Vector)ht.get(ServiceStore.FA_ATTRIBUTES); 1546 1547 // Construct return. 1548 1549 return 1550 new SSAAdvert(version, 1551 xid, 1552 locale, 1553 url, 1554 conf.getSAConfiguredScopes(), // report all scopes... 1555 attrs); 1556 } 1557 1558 /** 1559 * Report a fatal exception to the log. 1560 * 1561 * @param ex The exception to report. 1562 */ 1563 1564 protected static void reportFatalException(Exception ex) { 1565 1566 reportException(true, ex, new Vector()); 1567 1568 if (table != null) { 1569 table.store.dumpServiceStore(); 1570 } 1571 1572 conf.writeLog("exiting_msg", new Object[0]); 1573 1574 System.exit(1); 1575 1576 } 1577 1578 /** 1579 * Report a nonfatal exception to the log. 1580 * 1581 * @param ex The exception to report. 1582 * @param args The method arguments. 1583 * @param store The service store being processed. 1584 */ 1585 1586 protected static void reportNonfatalException(Exception ex, 1587 Vector args, 1588 ServiceStore store) { 1589 1590 reportException(false, ex, args); 1591 1592 if (conf.traceAll()) { 1593 store.dumpServiceStore(); 1594 } 1595 1596 } 1597 1598 /** 1599 * Report an exception to the log. 1600 * 1601 * @param isFatal Indicates whether the exception is fatal or not. 1602 * @param ex The exception to report. 1603 * @param args A potentially null vector of arguments to the 1604 * method where the exception was caught. 1605 */ 1606 1607 private static void 1608 reportException(boolean isFatal, Exception ex, Vector args) { 1609 1610 StringWriter sw = new StringWriter(); 1611 PrintWriter writer = new PrintWriter(sw); 1612 1613 // Get the backtrace. 1614 1615 ex.printStackTrace(writer); 1616 1617 String severity = (isFatal ? "fatal_error":"nonfatal_error"); 1618 String msg = ex.getMessage(); 1619 1620 if (msg == null) { 1621 msg = conf.formatMessage("no_message", new Object[0]); 1622 1623 } else if (ex instanceof ServiceLocationException) { 1624 msg = msg + 1625 "(" + ((ServiceLocationException)ex).getErrorCode() + ")"; 1626 1627 } 1628 1629 StringBuffer argMsg = new StringBuffer(); 1630 1631 int i, n = args.size(); 1632 1633 for (i = 0; i < n; i++) { 1634 argMsg.append("\n (" + Integer.toString(i) + "):" + 1635 args.elementAt(i).toString()); 1636 } 1637 1638 conf.writeLog(severity, 1639 new Object[] { 1640 ex.getClass().getName(), 1641 msg, 1642 argMsg, 1643 sw.toString()}); 1644 1645 } 1646 } 1647