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