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 ServiceTable()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 getServiceTable()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 createServiceTable()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 deserializeTable()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 serializeServiceStore(String URL)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 getStoreFromURL(String serializedURL)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 registerStore(ServiceStore serStore)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 run()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 ageStore()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 register(SSrvReg req)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 deregister(SSrvDereg req)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 findServiceTypes(SSrvTypeMsg req)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 findServices(SSrvMsg req)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 findAttributes(SAttrMsg req)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 getServiceRecord(ServiceURL URL, Locale locale)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 checkForRestrictedType(ServiceType type)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 trackRegisteredServiceTypes()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 areSupportedScopes(Vector scopes)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 getSleepIncrement(ServiceURL url)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 makeDAAdvert(SSrvMsg rqst, InetAddress daAddr, SLPConfig conf)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 makeDAAdvert(SrvLocHeader hdr, InetAddress daAddr, short xid, Vector scopes, SLPConfig config)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 makeSAAdvert(SSrvMsg rqst, InetAddress interfac, SLPConfig conf)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 reportFatalException(Exception ex)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 reportNonfatalException(Exception ex, Vector args, ServiceStore store)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 reportException(boolean isFatal, Exception ex, Vector args)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