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 1999-2003 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 */ 26 27 // SLPConfig.java 28 // 29 30 /** 31 * This class is a singleton - it has the configuration which 32 * is the default. It reads from a configuration file and 33 * this overrides the default. If the config file does not 34 * expressly forbid it, the ServiceLocationManager interface 35 * allows some of these configuration options to be modified. 36 * This configuration is refered to by many points of the 37 * implementation. Note that the class itself is abstract, 38 * and is extended by two classes, one that allows slpd to 39 * run as an SA server only, the other allows it to run 40 * as a DA as well. 41 * 42 * @see com.sun.slp.ServiceLocationManager 43 */ 44 45 package com.sun.slp; 46 47 import java.net.*; 48 import java.util.*; 49 import java.text.*; 50 import java.io.*; 51 52 /* 53 * This class contains all configuration information. It 54 * is hard coded to know the defaults, and will read a config 55 * file if it is present. The config file will override the 56 * default values and policy. If the config file allows it 57 * the user may reset some of these values using the 58 * ServiceLocationManager interface. 59 * 60 */ 61 class SLPConfig { 62 63 /** 64 * A Java properties file defines `\' as an escape character, which 65 * conflicts with the SLP API escape convention. Therefore, we need 66 * to subclass properties so we can parse in the file ourselves. 67 */ 68 69 public static class SLPProperties extends Properties { 70 71 SLPProperties(Properties p) { 72 super(p); 73 74 } 75 76 // Parse the SLP properties file ourselves. Groan! We don't recognize 77 // backslash as an escape. 78 79 public synchronized void load(InputStream in) throws IOException { 80 81 BufferedReader rd = new BufferedReader(new InputStreamReader(in)); 82 83 while (rd.ready()) { 84 String ln = rd.readLine(); 85 86 // Throw out anything that begins with '#' or ';'. 87 88 if (ln.startsWith("#") || 89 ln.startsWith(";") || 90 ln.length() <= 0) { 91 continue; 92 93 } 94 95 // Parse out equals sign, if any. Note that we trim any 96 // white space preceding or following data strings. 97 // Although the grammar doesn't allow it, users may 98 // enter blanks in their configuration files. 99 // NOTE: White space is not allowed in the data of 100 // property tag or values. If included, according 101 // to RFC 2614, Section 2.1 these MUST be escaped, 102 // ie. space would be represented with '\20'. 103 // Therefore, it is *completely* safe to perform 104 // these trim()s. They will catch errors resulting 105 // from sloppy data entry in slp.conf files and 106 // never corrupt or alter correctly formatted 107 // properties. 108 109 SLPTokenizer tk = new SLPTokenizer(ln, "="); 110 111 if (!tk.hasMoreTokens()) {// empty line... 112 continue; 113 114 } 115 116 String prop = tk.nextToken().trim(); 117 118 if (prop.trim().length() <= 0) {// line has just spaces... 119 continue; 120 121 } 122 123 if (!tk.hasMoreTokens()) {// line has no definition... 124 continue; 125 126 } 127 128 // Register the property. 129 String def = tk.nextToken().trim(); 130 this.setProperty(prop, def); 131 } 132 } 133 134 } 135 136 protected SLPConfig() { 137 138 // Create a temporary, default log to report any errors during 139 // configuration. 140 log = new StderrLog(); 141 142 // Initialize properties. Properties on command line override config 143 // file properties, and both override defaults. 144 145 Properties sysProps = (Properties)(System.getProperties().clone()); 146 147 // Load Defalts. 148 149 try { 150 Class.forName("com.sun.slp.Defaults"); 151 152 } catch (ClassNotFoundException ex) { 153 154 Assert.printMessageAndDie(this, 155 "no_class", 156 new Object[] {"com.sun.slp.Defaults"}); 157 } 158 159 // System properties now contain Defaults 160 Properties defaultProps = System.getProperties(); 161 162 // Load config file. 163 164 SLPProperties slpProps = new SLPProperties(new Properties()); 165 try { 166 InputStream fis = getConfigURLStream(); 167 if (fis != null) { 168 slpProps.load(fis); 169 System.setProperties(slpProps); 170 } 171 172 } catch (IOException ex) { 173 writeLog("unparsable_config_file", 174 new Object[] {ex.getMessage()}); 175 } 176 177 // Add config properties to Defaults, overwritting any pre-existing 178 // entries 179 defaultProps.putAll(slpProps); 180 181 // Now add in system props, overwritting any pre-existing entries 182 defaultProps.putAll(sysProps); 183 184 System.setProperties(defaultProps); 185 186 187 // Initialize useScopes property. This is read-only after the file 188 // has been loaded. 189 190 configuredScopes = initializeScopes("net.slp.useScopes"); 191 saConfiguredScopes = (Vector)configuredScopes.clone(); 192 193 // Add default scope to scopes for SA. 194 195 if (saConfiguredScopes.size() <= 0) { 196 saConfiguredScopes.addElement(Defaults.DEFAULT_SCOPE); 197 198 } 199 200 // Initialize SA scopes. This uses a Sun specific property for 201 // scopes only used by the SA and adds in the DA scopes. 202 203 saOnlyScopes = initializeScopes(DATable.SA_ONLY_SCOPES_PROP); 204 205 // Initialized preconfigured DAs. 206 207 preconfiguredDAs = initializePreconfiguredDAs(); 208 209 // Initialize broadcast flag. 210 211 broadcastOnly = Boolean.getBoolean("net.slp.isBroadcastOnly"); 212 213 // Initialize logging. Default is stderr, first check for alternate. 214 215 String failed = null; 216 217 try { 218 String loggerClassName = 219 System.getProperty("sun.net.slp.loggerClass"); 220 if (loggerClassName != null) { 221 Class loggerClass = Class.forName(loggerClassName); 222 // Protect against disastrous pilot error, such as trying 223 // to use com.sun.slp.SLPConfig as the log class 224 // (causes stack recursion) 225 if (Class.forName("java.io.Writer").isAssignableFrom( 226 loggerClass)) { 227 Object logger = loggerClass.newInstance(); 228 log = (Writer) logger; 229 } else { 230 failed = formatMessage( 231 "bad_log_class", 232 new Object[] { 233 loggerClass.toString()}) + "\n"; 234 } 235 } 236 237 } catch (Throwable ex) { 238 log = null; 239 failed = formatMessage( 240 "bad_log", 241 new Object[] { 242 ex.toString()}) + "\n"; 243 } 244 245 // If no alternate log, revert to minimal default 246 if (log == null) { 247 log = new StderrLog(); 248 249 // If the alternate log failed, log it through the default log 250 if (failed != null) { 251 try { 252 synchronized (log) { 253 log.write(failed); 254 log.flush(); 255 } 256 } catch (IOException giveUp) {} 257 } 258 } 259 260 } 261 262 private InputStream getConfigURLStream() { 263 264 // Open a URL onto the configuration file. 265 266 String conf = System.getProperty("sun.net.slp.configURL"); 267 268 if (conf == null) { 269 conf = Defaults.SOLARIS_CONF; 270 271 } 272 273 InputStream str = null; 274 275 try { 276 277 URL confURL = new URL(conf); 278 279 str = confURL.openStream(); 280 281 } catch (MalformedURLException ex) { 282 writeLog("url_malformed", 283 new Object[] {conf}); 284 285 } catch (IOException ex) { 286 if (conf != Defaults.SOLARIS_CONF) { 287 // don't complain if we can't find our own default 288 writeLog("unparsable_config_file", 289 new Object[] {ex.getMessage()}); 290 } 291 292 } 293 294 return str; 295 } 296 297 // ------------------------------------------------------------ 298 // Property manipulation functions 299 // 300 301 private boolean OKBound(int i, int lb, int ub) { 302 if (i < lb || i > ub) 303 return false; 304 else 305 return true; 306 } 307 308 int getIntProperty(String prop, int df, int lb, int ub) { 309 310 int i = Integer.getInteger(prop, df).intValue(); 311 312 if (OKBound(i, lb, ub)) { 313 return i; 314 315 } else { 316 writeLog("bad_prop_tag", new Object[] {prop}); 317 318 return df; 319 } 320 } 321 322 // ------------------------------------------------------------ 323 // Multicast radius 324 // 325 private int iMinMCRadius = 1; // link local scope 326 private int iMaxMCRadius = 255; // universal scope 327 328 int getMCRadius() { 329 return getIntProperty("net.slp.multicastTTL", 330 Defaults.iMulticastRadius, 331 iMinMCRadius, 332 iMaxMCRadius); 333 } 334 335 // ------------------------------------------------------------ 336 // Heartbeat interval, seconds. 337 // 338 private final int iMinHeart = 2000; // 10 minutes 339 private final int iMaxHeart = 259200000; // 3 days 340 341 int getAdvertHeartbeatTime() { 342 return getIntProperty("net.slp.DAHeartBeat", 343 Defaults.iHeartbeat, 344 iMinHeart, 345 iMaxHeart); 346 } 347 348 // ------------------------------------------------------------ 349 // Active discovery interval, seconds. 350 // 351 352 private final int iMinDisc = 300; // 5 minutes 353 private final int iMaxDisc = 10800; // 3 hours 354 355 int getActiveDiscoveryInterval() { 356 357 // We allow zero in order to turn active discovery off, but 358 // if 5 minutes is the smallest actual time. 359 360 int prop = getIntProperty("net.slp.DAActiveDiscoveryInterval", 361 Defaults.iActiveDiscoveryInterval, 362 0, 363 iMaxDisc); 364 if (prop > 0 && prop < iMinDisc) { 365 writeLog("bad_prop_tag", 366 new Object[] {"net.slp.DAActiveDiscoveryInterval"}); 367 return iMinDisc; 368 369 } 370 371 return prop; 372 } 373 374 375 // ------------------------------------------------------------ 376 // Active discovery granularity, seconds. 377 // 378 379 private int iMaxDiscGran = iMaxDisc * 2; 380 381 int getActiveDiscoveryGranularity() { 382 return getIntProperty("sun.net.slp.DAActiveDiscoveryGranularity", 383 Defaults.iActiveDiscoveryGranularity, 384 0, 385 iMaxDiscGran); 386 } 387 388 // ------------------------------------------------------------ 389 // Bound for random wait, milliseconds. 390 // 391 392 private final int iMinWait = 1000; // 1 sec. 393 private final int iMaxWait = 3000; // 3 sec. 394 395 int getRandomWaitBound() { 396 return getIntProperty("net.slp.randomWaitBound", 397 Defaults.iRandomWaitBound, 398 iMinWait, 399 iMaxWait); 400 } 401 402 private static Random randomWait = null; 403 404 long getRandomWait() { 405 406 if (randomWait == null) { 407 randomWait = new Random(); 408 } 409 410 double r = randomWait.nextDouble(); 411 double max = (double)getRandomWaitBound(); 412 413 return (long)(max * r); 414 } 415 416 // ------------------------------------------------------------ 417 // TCP timeout, milliseconds. 418 // 419 final static private int iMinTimeout = 100; 420 final static private int iMaxTimeout = 360000; 421 422 int getTCPTimeout() { 423 return getIntProperty("sun.net.slp.TCPTimeout", 424 Defaults.iTCPTimeout, 425 iMinTimeout, 426 iMaxTimeout); 427 } 428 429 // ------------------------------------------------------------ 430 // Path MTU 431 // 432 private final int iMinMTU = 128; // used for some ppp connections 433 private final int iMaxMTU = 8192; // used on some LANs 434 435 int getMTU() { 436 return getIntProperty("net.slp.MTU", 437 Defaults.iMTU, 438 iMinMTU, 439 iMaxMTU); 440 } 441 442 443 // ------------------------------------------------------------ 444 // Serialized registrations. 445 // 446 447 String getSerializedRegURL() { 448 449 return System.getProperty("net.slp.serializedRegURL", null); 450 451 } 452 453 // ------------------------------------------------------------ 454 // Are we running as a DA or SA server? 455 // 456 457 protected static boolean isSA = false; 458 459 boolean isDA() { 460 return false; 461 } 462 463 boolean isSA() { 464 return isSA; 465 } 466 467 // ------------------------------------------------------------ 468 // DA and SA attributes 469 // 470 471 Vector getDAAttributes() { 472 return getAttributes("net.slp.DAAttributes", 473 Defaults.defaultDAAttributes, 474 true); 475 } 476 477 Vector getSAAttributes() { 478 return getAttributes("net.slp.SAAttributes", 479 Defaults.defaultSAAttributes, 480 false); 481 } 482 483 private Vector getAttributes(String prop, 484 Vector defaults, 485 boolean daAttrs) { 486 String attrList = 487 System.getProperty(prop); 488 489 if (attrList == null || attrList.length() <= 0) { 490 return (Vector)defaults.clone(); 491 492 } 493 494 try { 495 Vector sAttrs = 496 SrvLocHeader.parseCommaSeparatedListIn(attrList, false); 497 498 Vector attrs = new Vector(); 499 int i, n = sAttrs.size(); 500 501 // Create attribute objects. 502 503 for (i = 0; i < n; i++) { 504 String attrExp = (String)sAttrs.elementAt(i); 505 ServiceLocationAttribute attr = 506 new ServiceLocationAttribute(attrExp, false); 507 508 // If this is the min-refresh-interval, then check the value. 509 510 if (daAttrs && 511 attr.getId().equals( 512 Defaults.MIN_REFRESH_INTERVAL_ATTR_ID)) { 513 Vector values = attr.getValues(); 514 boolean errorp = true; 515 516 if (values != null && values.size() == 1) { 517 Object val = values.elementAt(0); 518 519 if (val instanceof Integer) { 520 int ival = ((Integer)val).intValue(); 521 522 if (ival >= 0 && 523 ival <= ServiceURL.LIFETIME_MAXIMUM) { 524 errorp = false; 525 526 } 527 } 528 } 529 530 // Throw exception if it didn't work. 531 532 if (errorp) { 533 throw new ServiceLocationException( 534 ServiceLocationException.PARSE_ERROR, 535 "syntax_error_prop", 536 new Object[] {prop, attrs}); 537 538 } 539 } 540 541 // Add attribute to vector. 542 543 attrs.addElement(attr); 544 545 } 546 547 return attrs; 548 549 } catch (Exception ex) { 550 551 writeLog("syntax_error_prop", 552 new Object[] {prop, attrList}); 553 return (Vector)defaults.clone(); 554 555 } 556 } 557 558 // ------------------------------------------------------------- 559 // Do we support V1? 560 // 561 562 boolean isV1Supported() { 563 return false; 564 } 565 566 // ------------------------------------------------------------- 567 // Queue length for server socket. 568 // 569 570 int getServerSocketQueueLength() { 571 return getIntProperty("sun.net.slp.serverSocketQueueLength", 572 Defaults.iSocketQueueLength, 573 0, 574 Integer.MAX_VALUE); 575 } 576 577 // ------------------------------------------------------------ 578 // Testing options 579 // 580 581 582 boolean traceAll() {// not official! 583 return Boolean.getBoolean("sun.net.slp.traceALL"); 584 } 585 586 boolean regTest() { 587 if (Boolean.getBoolean("sun.net.slp.traceALL") || 588 Boolean.getBoolean("net.slp.traceReg")) 589 return true; 590 else 591 return false; 592 } 593 594 boolean traceMsg() { 595 if (Boolean.getBoolean("sun.net.slp.traceALL") || 596 Boolean.getBoolean("net.slp.traceMsg")) 597 return true; 598 else 599 return false; 600 } 601 602 boolean traceDrop() { 603 if (Boolean.getBoolean("sun.net.slp.traceALL") || 604 Boolean.getBoolean("net.slp.traceDrop")) 605 return true; 606 else 607 return false; 608 } 609 610 boolean traceDATraffic() { 611 if (Boolean.getBoolean("sun.net.slp.traceALL") || 612 Boolean.getBoolean("net.slp.traceDATraffic")) 613 return true; 614 else 615 return false; 616 } 617 618 // cannot use Boolean.getBoolean as the default is 'true' 619 // using that mechanism, absense would be considered 'false' 620 621 boolean passiveDADetection() { 622 623 String sPassive = 624 System.getProperty("net.slp.passiveDADetection", "true"); 625 if (sPassive.equalsIgnoreCase("true")) 626 return true; 627 else 628 return false; 629 630 } 631 632 // Initialized when the SLPConfig object is created to avoid changing 633 // during the program. 634 private boolean broadcastOnly = false; 635 636 boolean isBroadcastOnly() { 637 return broadcastOnly; 638 } 639 640 641 // ------------------------------------------------------------ 642 // Multicast/broadcast socket mangement. 643 // 644 DatagramSocket broadSocket = null; // cached broadcast socket. 645 646 647 // Reopen the multicast/broadcast socket bound to the 648 // interface. If groups is not null, then join all 649 // the groups. Otherwise, this is send only. 650 651 DatagramSocket 652 refreshMulticastSocketOnInterface(InetAddress interfac, 653 Vector groups) { 654 655 try { 656 657 // Reopen it. 658 659 DatagramSocket dss = 660 getMulticastSocketOnInterface(interfac, 661 (groups == null ? true:false)); 662 663 if ((groups != null) && (dss instanceof MulticastSocket)) { 664 int i, n = groups.size(); 665 MulticastSocket mss = (MulticastSocket)dss; 666 667 for (i = 0; i < n; i++) { 668 InetAddress maddr = (InetAddress)groups.elementAt(i); 669 670 mss.joinGroup(maddr); 671 672 } 673 } 674 675 return dss; 676 677 } catch (Exception ex) { 678 679 // Any exception in error recovery causes program to die. 680 681 Assert.slpassert(false, 682 "cast_socket_failure", 683 new Object[] {ex, ex.getMessage()}); 684 685 } 686 687 return null; 688 } 689 690 // Open a multicast/broadcast socket on the interface. Note that if 691 // the socket is broadcast, the network interface is not specified in the 692 // creation message. Is it bound to all interfaces? The isSend parameter 693 // specifies whether the socket is for send only. 694 695 DatagramSocket 696 getMulticastSocketOnInterface(InetAddress interfac, boolean isSend) 697 throws ServiceLocationException { 698 699 DatagramSocket castSocket = null; 700 701 // Substitute broadcast if we are configured for it. 702 703 if (isBroadcastOnly()) { 704 705 try { 706 707 // If transmit, then simply return a new socket. 708 709 if (isSend) { 710 castSocket = new DatagramSocket(); 711 712 } else { 713 714 // Return cached socket if there. 715 716 if (broadSocket != null) { 717 castSocket = broadSocket; 718 719 } else { 720 721 // Make a new broadcast socket. 722 723 castSocket = 724 new DatagramSocket(Defaults.iSLPPort, 725 getBroadcastAddress()); 726 727 } 728 729 // Cache for future reference. 730 731 broadSocket = castSocket; 732 } 733 } catch (SocketException ex) { 734 throw 735 new ServiceLocationException( 736 ServiceLocationException.NETWORK_INIT_FAILED, 737 "socket_creation_failure", 738 new Object[] { 739 getBroadcastAddress(), ex.getMessage()}); 740 } 741 742 } else { 743 744 // Create a multicast socket. 745 746 MulticastSocket ms; 747 748 try { 749 750 if (isSend) { 751 ms = new MulticastSocket(); 752 753 } else { 754 ms = new MulticastSocket(Defaults.iSLPPort); 755 756 } 757 758 } catch (IOException ex) { 759 throw 760 new ServiceLocationException( 761 ServiceLocationException.NETWORK_INIT_FAILED, 762 "socket_creation_failure", 763 new Object[] {interfac, ex.getMessage()}); 764 } 765 766 767 try { 768 769 // Set the TTL and the interface on the multicast socket. 770 // Client is responsible for joining group. 771 772 ms.setTimeToLive(getMCRadius()); 773 ms.setInterface(interfac); 774 775 } catch (IOException ex) { 776 throw 777 new ServiceLocationException( 778 ServiceLocationException.NETWORK_INIT_FAILED, 779 "socket_initializtion_failure", 780 new Object[] {interfac, ex.getMessage()}); 781 } 782 783 castSocket = ms; 784 785 } 786 787 return castSocket; 788 } 789 790 // ------------------------------------------------------------ 791 // Type hint 792 // 793 794 // Return a vector of ServiceType objects for the type hint. 795 796 Vector getTypeHint() { 797 Vector hint = new Vector(); 798 String sTypeList = System.getProperty("net.slp.typeHint", ""); 799 800 if (sTypeList.length() <= 0) { 801 return hint; 802 803 } 804 805 // Create a vector of ServiceType objects for the type hint. 806 807 try { 808 809 hint = SrvLocHeader.parseCommaSeparatedListIn(sTypeList, true); 810 811 int i, n = hint.size(); 812 813 for (i = 0; i < n; i++) { 814 String type = (String)hint.elementAt(i); 815 816 hint.setElementAt(new ServiceType(type), i); 817 818 } 819 } catch (ServiceLocationException ex) { 820 821 writeLog("syntax_error_prop", 822 new Object[] {"net.slp.typeHint", sTypeList}); 823 824 hint.removeAllElements(); 825 826 } 827 828 return hint; 829 830 } 831 832 // ------------------------------------------------------------ 833 // Configured scope handling 834 // 835 836 // Vector of configured scopes. 837 838 private Vector configuredScopes = null; 839 840 // Vector of configures scopes for SA. 841 842 private Vector saConfiguredScopes = null; 843 844 // Vector of scopes only in the sa server. 845 846 private Vector saOnlyScopes = null; 847 848 // Return the configured scopes. 849 850 Vector getConfiguredScopes() { 851 return (Vector)configuredScopes.clone(); 852 } 853 854 // Return SA scopes. 855 856 Vector getSAOnlyScopes() { 857 return (Vector)saOnlyScopes.clone(); 858 859 } 860 861 // Return the configured scopes for the SA. 862 863 Vector getSAConfiguredScopes() { 864 return (Vector)saConfiguredScopes.clone(); 865 866 } 867 868 // Add scopes discovered during preconfigured DA contact. 869 // These count as configured scopes. 870 871 void addPreconfiguredDAScopes(Vector scopes) { 872 873 int i, n = scopes.size(); 874 875 for (i = 0; i < n; i++) { 876 Object scope = scopes.elementAt(i); 877 878 if (!configuredScopes.contains(scope)) { 879 configuredScopes.addElement(scope); 880 881 } 882 883 // There better be none extra here for the SA server/DA. 884 885 if (isSA() || isDA()) { 886 Assert.slpassert(saConfiguredScopes.contains(scope), 887 "sa_new_scope", 888 new Object[] {scope, saConfiguredScopes}); 889 890 } 891 } 892 } 893 894 // Initialize the scopes list on property. 895 896 private Vector initializeScopes(String prop) { 897 898 String sScopes = System.getProperty(prop); 899 900 if (sScopes == null || sScopes.length() <= 0) { 901 return new Vector(); 902 } 903 904 try { 905 906 Vector vv = 907 SrvLocHeader.parseCommaSeparatedListIn(sScopes, true); 908 909 // Unescape scope strings. 910 911 SLPHeaderV2.unescapeScopeStrings(vv); 912 913 // Validate, lower case scope names. 914 915 DATable.validateScopes(vv, getLocale()); 916 917 if (vv.size() > 0) { 918 return vv; 919 } 920 921 } catch (ServiceLocationException ex) { 922 writeLog("syntax_error_prop", 923 new Object[] { 924 prop, 925 sScopes}); 926 927 928 } 929 930 return new Vector(); 931 } 932 933 // Vector of preconfigured DAs. Read only after initialized. 934 935 private Vector preconfiguredDAs = null; 936 937 // Return a vector of DA addresses. 938 939 Vector getPreconfiguredDAs() { 940 return (Vector)preconfiguredDAs.clone(); 941 942 } 943 944 // Initialize preconfigured DA list. 945 946 private Vector initializePreconfiguredDAs() { 947 String sDAList = System.getProperty("net.slp.DAAddresses", ""); 948 Vector ret = new Vector(); 949 950 sDAList.trim(); 951 952 if (sDAList.length() <= 0) { 953 return ret; 954 955 } 956 957 try { 958 959 ret = SrvLocHeader.parseCommaSeparatedListIn(sDAList, true); 960 961 } catch (ServiceLocationException ex) { 962 963 writeLog("syntax_error_prop", 964 new Object[] {"net.slp.DAAddress", sDAList}); 965 966 return ret; 967 968 } 969 970 // Convert to InetAddress objects. 971 972 int i; 973 974 for (i = 0; i < ret.size(); i++) { 975 String da = ""; 976 977 try { 978 da = ((String)ret.elementAt(i)).trim(); 979 InetAddress daAddr = InetAddress.getByName(da); 980 981 ret.setElementAt(daAddr, i); 982 983 } catch (UnknownHostException ex) { 984 985 writeLog("resolve_failed", 986 new Object[] {da}); 987 988 /* 989 * Must decrement the index 'i' otherwise the next iteration 990 * around the loop will miss the element immediately after 991 * the element removed. 992 * 993 * WARNING: Do not use 'i' again until the loop has 994 * iterated as it may, after decrementing, 995 * be negative. 996 */ 997 ret.removeElementAt(i); 998 i--; 999 continue; 1000 } 1001 } 1002 1003 1004 return ret; 1005 } 1006 1007 // ------------------------------------------------------------ 1008 // SLPv1 Support Switches 1009 // 1010 1011 boolean getSLPv1NotSupported() {// not official! 1012 return Boolean.getBoolean("sun.net.slp.SLPv1NotSupported"); 1013 1014 } 1015 1016 boolean getAcceptSLPv1UnscopedRegs() {// not official! 1017 1018 if (!getSLPv1NotSupported()) { 1019 return Boolean.getBoolean("sun.net.slp.acceptSLPv1UnscopedRegs"); 1020 1021 } 1022 1023 return false; 1024 } 1025 1026 // ------------------------------------------------------------ 1027 // Accessor for SLPConfig object 1028 // 1029 1030 protected static SLPConfig theSLPConfig = null; 1031 1032 static SLPConfig getSLPConfig() { 1033 1034 if (theSLPConfig == null) { 1035 theSLPConfig = new SLPConfig(); 1036 } 1037 1038 return theSLPConfig; 1039 1040 } 1041 1042 /** 1043 * @return Maximum number of messages/objects to return. 1044 */ 1045 1046 int getMaximumResults() { 1047 int i = Integer.getInteger("net.slp.maxResults", 1048 Defaults.iMaximumResults).intValue(); 1049 if (i == -1) { 1050 i = Integer.MAX_VALUE; 1051 1052 } 1053 1054 if (OKBound(i, 1, Integer.MAX_VALUE)) { 1055 return i; 1056 1057 } else { 1058 1059 writeLog("bad_prop_tag", 1060 new Object[] { 1061 "net.slp.maxResults"}); 1062 1063 return Defaults.iMaximumResults; 1064 1065 } 1066 } 1067 1068 /** 1069 * Convert a language tag into a locale. 1070 */ 1071 1072 static Locale langTagToLocale(String ltag) { 1073 1074 // We treat the first part as the ISO 639 language and the 1075 // second part as the ISO 3166 country tag, even though RFC 1076 // 1766 doesn't necessarily require that. We should probably 1077 // use a lookup table here to determine if they are correct. 1078 1079 StringTokenizer tk = new StringTokenizer(ltag, "-"); 1080 String lang = ""; 1081 String country = ""; 1082 1083 if (tk.hasMoreTokens()) { 1084 lang = tk.nextToken(); 1085 1086 if (tk.hasMoreTokens()) { 1087 country = tk.nextToken(""); 1088 // country name may have "-" in it... 1089 1090 } 1091 } 1092 1093 return new Locale(lang, country); 1094 } 1095 1096 /** 1097 * Convert a Locale object into a language tag for output. 1098 * 1099 * @param locale The Locale. 1100 * @return String with the language tag encoded. 1101 */ 1102 1103 static String localeToLangTag(Locale locale) { 1104 1105 // Construct the language tag. 1106 1107 String ltag = locale.getCountry(); 1108 ltag = locale.getLanguage() + (ltag.length() <= 0 ? "" : ("-" + ltag)); 1109 1110 return ltag; 1111 1112 } 1113 1114 /** 1115 * @return the language requests will be made in. 1116 */ 1117 static Locale getLocale() { 1118 String s = System.getProperty("net.slp.locale"); 1119 1120 if (s != null && s.length() > 0) { 1121 return langTagToLocale(s); 1122 1123 } else { 1124 1125 // Return the Java default if the SLP property is not set. 1126 1127 return Locale.getDefault(); 1128 1129 } 1130 } 1131 1132 /** 1133 * @return the InetAddress of the broadcast interface. 1134 */ 1135 1136 static private InetAddress broadcastAddress; 1137 1138 static InetAddress getBroadcastAddress() { 1139 if (broadcastAddress == null) { 1140 1141 try { 1142 broadcastAddress = 1143 InetAddress.getByName(Defaults.sBroadcast); 1144 } catch (UnknownHostException uhe) { 1145 1146 Assert.slpassert(false, 1147 "cast_address_failure", 1148 new Object[] {Defaults.sBroadcast}); 1149 1150 } 1151 } 1152 return broadcastAddress; 1153 } 1154 1155 1156 /** 1157 * @return the InetAddress of the multicast group. 1158 */ 1159 1160 static private InetAddress multicastAddress; 1161 1162 static InetAddress getMulticastAddress() { 1163 if (multicastAddress == null) { 1164 1165 try { 1166 multicastAddress = 1167 InetAddress.getByName(Defaults.sGeneralSLPMCAddress); 1168 } catch (UnknownHostException uhe) { 1169 Assert.slpassert(false, 1170 "cast_address_failure", 1171 new Object[] {Defaults.sGeneralSLPMCAddress}); 1172 1173 } 1174 } 1175 return multicastAddress; 1176 } 1177 1178 /** 1179 * @return the interfaces on which SLP should listen and transmit. 1180 */ 1181 1182 private static Vector interfaces = null; 1183 1184 Vector getInterfaces() { 1185 1186 if (interfaces == null) { 1187 InetAddress iaLocal = null; 1188 1189 // Get local host. 1190 1191 try { 1192 iaLocal = InetAddress.getLocalHost(); 1193 1194 } catch (UnknownHostException ex) { 1195 Assert.slpassert(false, 1196 "resolve_failed", 1197 new Object[] {"localhost"}); 1198 } 1199 1200 String mcastI = System.getProperty("net.slp.interfaces"); 1201 interfaces = new Vector(); 1202 1203 // Only add local host if nothing else is given. 1204 1205 if (mcastI == null || mcastI.length() <= 0) { 1206 interfaces.addElement(iaLocal); 1207 return interfaces; 1208 1209 } 1210 1211 Vector nintr; 1212 1213 try { 1214 1215 nintr = SrvLocHeader.parseCommaSeparatedListIn(mcastI, true); 1216 1217 } catch (ServiceLocationException ex) { 1218 writeLog("syntax_error_prop", 1219 new Object[] { 1220 "net.slp.multicastInterfaces", 1221 mcastI}); 1222 1223 // Add local host. 1224 1225 interfaces.addElement(iaLocal); 1226 1227 return interfaces; 1228 1229 } 1230 1231 // See if they are really there. 1232 1233 int i, n = nintr.size(); 1234 1235 for (i = 0; i < n; i++) { 1236 InetAddress ia; 1237 String host = (String)nintr.elementAt(i); 1238 1239 try { 1240 1241 ia = InetAddress.getByName(host); 1242 1243 } catch (UnknownHostException ex) { 1244 writeLog("unknown_interface", 1245 new Object[] {host, 1246 "net.slp.multicastInterfaces"}); 1247 continue; 1248 1249 } 1250 1251 if (!interfaces.contains(ia)) { 1252 1253 // Add default at beginning. 1254 1255 if (ia.equals(iaLocal)) { 1256 interfaces.insertElementAt(ia, 0); 1257 1258 } else { 1259 interfaces.addElement(ia); 1260 1261 } 1262 } 1263 } 1264 } 1265 1266 return interfaces; 1267 1268 } 1269 1270 /** 1271 * @return An InetAddress object representing 127.0.0.1 1272 */ 1273 InetAddress getLoopback() { 1274 InetAddress iaLoopback = null; 1275 1276 try { 1277 iaLoopback = InetAddress.getByName(Defaults.LOOPBACK_ADDRESS); 1278 1279 } catch (UnknownHostException ex) { 1280 Assert.slpassert(false, 1281 "resolve_failed", 1282 new Object[] {"localhost loopback"}); 1283 } 1284 1285 return iaLoopback; 1286 } 1287 1288 /** 1289 * @return The default interface, which should be the first in the 1290 * interfaces vector Vector. 1291 */ 1292 1293 InetAddress getLocalHost() { 1294 Vector inter = getInterfaces(); 1295 return (InetAddress)inter.elementAt(0); 1296 1297 } 1298 1299 // Return true if the address is one of the local interfaces. 1300 1301 boolean isLocalHostSource(InetAddress addr) { 1302 1303 // First check loopback 1304 1305 if (addr.equals(getLoopback())) { 1306 return true; 1307 1308 } 1309 1310 return interfaces.contains(addr); 1311 1312 } 1313 1314 // ----------------- 1315 // Timeouts 1316 // 1317 1318 // Return the maximum wait for multicast convergence. 1319 1320 final static private int iMultiMin = 1000; // one second 1321 final static private int iMultiMax = 60000; // one minute 1322 1323 int getMulticastMaximumWait() { 1324 1325 return getIntProperty("net.slp.multicastMaximumWait", 1326 Defaults.iMulticastMaxWait, 1327 iMultiMin, 1328 iMultiMax); 1329 } 1330 1331 /* 1332 * @return Vector of timeouts for multicast convergence. 1333 */ 1334 1335 int[] getMulticastTimeouts() { 1336 int[] timeouts = parseTimeouts("net.slp.multicastTimeouts", 1337 Defaults.a_iConvergeTimeout); 1338 1339 timeouts = capTimeouts("net.slp.multicastTimeouts", 1340 timeouts, 1341 false, 1342 0, 1343 0); 1344 1345 return timeouts; 1346 } 1347 1348 /** 1349 * @return Vector of timeouts to try for datagram transmission. 1350 */ 1351 1352 int[] getDatagramTimeouts() { 1353 int[] timeouts = parseTimeouts("net.slp.datagramTimeouts", 1354 Defaults.a_iDatagramTimeout); 1355 1356 timeouts = capTimeouts("net.slp.datagramTimeouts", 1357 timeouts, 1358 true, 1359 iMultiMin, 1360 iMultiMax); 1361 1362 return timeouts; 1363 } 1364 1365 /** 1366 * @return Vector of timeouts for DA discovery multicast. 1367 */ 1368 1369 int[] getDADiscoveryTimeouts() { 1370 int[] timeouts = parseTimeouts("net.slp.DADiscoveryTimeouts", 1371 Defaults.a_iDADiscoveryTimeout); 1372 1373 timeouts = capTimeouts("net.slp.DADiscoveryTimeouts", 1374 timeouts, 1375 false, 1376 0, 1377 0); 1378 1379 return timeouts; 1380 } 1381 1382 /** 1383 * This method ensures that all the timeouts are within valid ranges. 1384 * The sum of all timeouts for the given property name must not 1385 * exceed the value returned by <i>getMulticastMaximumWait()</i>. If 1386 * the sum of all timeouts does exceed the maximum wait period the 1387 * timeouts are averaged out so that the sum equals the maximum wait 1388 * period. 1389 * <br> 1390 * Additional range checking is also performed when <i>rangeCheck</i> 1391 * is true. Then the sum of all timeouts must also be between <i>min</i> 1392 * and <i>max</i>. If the sum of all timeouts is not within the range 1393 * the average is taken from the closest range boundary. 1394 * 1395 * @param property 1396 * Name of timeout property being capped. This is only present for 1397 * reporting purposes and no actual manipulation of the property 1398 * is made within this method. 1399 * @param timeouts 1400 * Array of timeout values. 1401 * @param rangeCheck 1402 * Indicator of whether additional range checking is required. When 1403 * false <i>min</i> and <i>max</i> are ignored. 1404 * @param min 1405 * Additional range checking lower boundary. 1406 * @param max 1407 * Additional range checking upper boundary. 1408 * @return 1409 * Array of capped timeouts. Note this may be the same array as 1410 * passed in (<i>timeouts</i>). 1411 */ 1412 private int[] capTimeouts(String property, 1413 int[] timeouts, 1414 boolean rangeCheck, 1415 int min, 1416 int max) { 1417 1418 int averagedTimeout; 1419 int totalWait = 0; 1420 1421 for (int index = 0; index < timeouts.length; index++) { 1422 totalWait += timeouts[index]; 1423 } 1424 1425 if (rangeCheck) { 1426 // If sum of timeouts within limits then finished. 1427 if (totalWait >= min && totalWait <= max) { 1428 return timeouts; 1429 } 1430 1431 // Average out the timeouts so the sum is equal to the closest 1432 // range boundary. 1433 if (totalWait < min) { 1434 averagedTimeout = min / timeouts.length; 1435 } else { 1436 averagedTimeout = max / timeouts.length; 1437 } 1438 1439 writeLog("capped_range_timeout_prop", 1440 new Object[] {property, 1441 String.valueOf(totalWait), 1442 String.valueOf(min), 1443 String.valueOf(max), 1444 String.valueOf(timeouts.length), 1445 String.valueOf(averagedTimeout)}); 1446 } else { 1447 // Sum of all timeouts must not exceed this value. 1448 int maximumWait = getMulticastMaximumWait(); 1449 1450 // If sum of timeouts within limits then finished. 1451 if (totalWait <= maximumWait) { 1452 return timeouts; 1453 } 1454 1455 // Average out the timeouts so the sum is equal to the maximum 1456 // timeout. 1457 averagedTimeout = maximumWait / timeouts.length; 1458 1459 writeLog("capped_timeout_prop", 1460 new Object[] {property, 1461 String.valueOf(totalWait), 1462 String.valueOf(maximumWait), 1463 String.valueOf(timeouts.length), 1464 String.valueOf(averagedTimeout)}); 1465 } 1466 1467 for (int index = 0; index < timeouts.length; index++) { 1468 timeouts[index] = averagedTimeout; 1469 } 1470 1471 return timeouts; 1472 } 1473 1474 private int[] parseTimeouts(String property, int[] defaults) { 1475 1476 String sTimeouts = System.getProperty(property); 1477 1478 if (sTimeouts == null || sTimeouts.length() <= 0) { 1479 return defaults; 1480 1481 } 1482 1483 Vector timeouts = null; 1484 1485 try { 1486 timeouts = SrvLocHeader.parseCommaSeparatedListIn(sTimeouts, true); 1487 1488 } catch (ServiceLocationException ex) { 1489 writeLog("syntax_error_prop", 1490 new Object[] {property, sTimeouts}); 1491 return defaults; 1492 1493 } 1494 1495 int iCount = 0; 1496 int[] iTOs = new int[timeouts.size()]; 1497 1498 for (Enumeration en = timeouts.elements(); en.hasMoreElements(); ) { 1499 String s1 = (String)en.nextElement(); 1500 1501 try { 1502 iTOs[iCount] = Integer.parseInt(s1); 1503 1504 } catch (NumberFormatException nfe) { 1505 writeLog("syntax_error_prop", 1506 new Object[] {property, sTimeouts}); 1507 return defaults; 1508 1509 } 1510 1511 if (iTOs[iCount] < 0) { 1512 writeLog("invalid_timeout_prop", 1513 new Object[] {property, String.valueOf(iTOs[iCount])}); 1514 return defaults; 1515 } 1516 1517 iCount++; 1518 } 1519 1520 return iTOs; 1521 } 1522 1523 // ----------------------------- 1524 // SLP Time Calculation 1525 // 1526 1527 /** 1528 * Returns the number of seconds since 00:00 Universal Coordinated 1529 * Time, January 1, 1970. 1530 * 1531 * Java returns the number of milliseconds, so all the method does is 1532 * divide by 1000. 1533 * 1534 * This implementation still will have a problem when the Java time 1535 * values wraps, but there isn't much we can do now. 1536 */ 1537 static long currentSLPTime() { 1538 return (System.currentTimeMillis() / 1000); 1539 } 1540 1541 /* security */ 1542 1543 // Indicates whether security class is available. 1544 1545 boolean getSecurityEnabled() { 1546 return securityEnabled; 1547 1548 } 1549 1550 private static boolean securityEnabled; 1551 1552 // Indicates whether the securityEnabled property is true 1553 1554 boolean getHasSecurity() { 1555 return securityEnabled && 1556 (new Boolean(System.getProperty("net.slp.securityEnabled", 1557 "false")).booleanValue()); 1558 } 1559 1560 // I18N Support. 1561 1562 private static final String BASE_BUNDLE_NAME = "com/sun/slp/ClientLib"; 1563 1564 ResourceBundle getMessageBundle(Locale locale) { 1565 1566 ResourceBundle msgBundle = null; 1567 1568 // First try the Solaris Java locale area 1569 1570 try { 1571 URL[] urls = new URL[] {new URL("file:/usr/share/lib/locale/")}; 1572 1573 URLClassLoader ld = new URLClassLoader(urls); 1574 1575 msgBundle = ResourceBundle.getBundle(BASE_BUNDLE_NAME, locale, ld); 1576 1577 return msgBundle; 1578 } catch (MalformedURLException e) { // shouldn't get here 1579 } catch (MissingResourceException ex) { 1580 System.err.println("Missing resource bundle ``"+ 1581 "/usr/share/lib/locale/" + BASE_BUNDLE_NAME + 1582 "'' for locale ``" + 1583 locale + "''; trying default..."); 1584 } 1585 1586 try { 1587 msgBundle = ResourceBundle.getBundle(BASE_BUNDLE_NAME, locale); 1588 1589 } catch (MissingResourceException ex) { // can't localize this one! 1590 1591 // We can't print out to the log, because we may be in the 1592 // process of trying to. 1593 1594 System.err.println("Missing resource bundle ``"+ 1595 BASE_BUNDLE_NAME+ 1596 "'' for locale ``"+ 1597 locale+ 1598 "''"); 1599 // Hosed if the default locale is missing. 1600 1601 if (locale.equals(Defaults.locale)) { 1602 1603 System.err.println("Exiting..."); 1604 System.exit(1); 1605 } 1606 1607 // Otherwise, return the default locale. 1608 1609 System.err.println("Using SLP default locale ``" + 1610 Defaults.locale + 1611 "''"); 1612 1613 msgBundle = getMessageBundle(Defaults.locale); 1614 1615 } 1616 1617 return msgBundle; 1618 } 1619 1620 String formatMessage(String msgTag, Object[] params) { 1621 ResourceBundle bundle = getMessageBundle(getLocale()); 1622 return formatMessageInternal(msgTag, params, bundle); 1623 1624 } 1625 1626 // MessageFormat is picky about types. Convert the params into strings. 1627 1628 static void convertToString(Object[] params) { 1629 int i, n = params.length; 1630 1631 for (i = 0; i < n; i++) { 1632 1633 if (params[i] != null) { 1634 params[i] = params[i].toString(); 1635 1636 } else { 1637 params[i] = "<null>"; 1638 1639 } 1640 } 1641 } 1642 1643 static String 1644 formatMessageInternal(String msgTag, 1645 Object[] params, 1646 ResourceBundle bundle) { 1647 String pattern = ""; 1648 1649 try { 1650 pattern = bundle.getString(msgTag); 1651 1652 } catch (MissingResourceException ex) { 1653 1654 // Attempt to report error. Can't use Assert here because it 1655 // calls back into SLPConfig. 1656 String msg = "Can''t find message ``{0}''''."; 1657 1658 try { 1659 pattern = bundle.getString("cant_find_resource"); 1660 msg = MessageFormat.format(pattern, new Object[] {msgTag}); 1661 1662 } catch (MissingResourceException exx) { 1663 1664 } 1665 1666 System.err.println(msg); 1667 System.exit(-1); 1668 } 1669 1670 convertToString(params); 1671 1672 return MessageFormat.format(pattern, params); 1673 } 1674 1675 // logging. 1676 1677 // Protected so slpd can replace it. 1678 1679 protected Writer log; 1680 1681 // Synchronized so writes from multiple threads don't get interleaved. 1682 1683 void writeLog(String msgTag, Object[] params) { 1684 1685 // MessageFormat is picky about types. Convert the params into strings. 1686 1687 convertToString(params); 1688 1689 try { 1690 synchronized (log) { 1691 log.write(formatMessage(msgTag, params)); 1692 log.flush(); 1693 } 1694 } catch (IOException ex) {} 1695 } 1696 1697 void writeLogLine(String msgTag, Object[] params) { 1698 1699 try { 1700 String pattern = getMessageBundle(getLocale()).getString(msgTag); 1701 1702 synchronized (log) { 1703 log.write(formatMessage(msgTag, params)); 1704 log.write("\n"); 1705 log.flush(); 1706 } 1707 } catch (IOException ex) {} 1708 1709 } 1710 1711 static String getDateString() { 1712 1713 DateFormat df = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, 1714 DateFormat.DEFAULT, 1715 getLocale()); 1716 Calendar calendar = Calendar.getInstance(getLocale()); 1717 return df.format(calendar.getTime()); 1718 1719 } 1720 1721 1722 // On load, check whether the signature class is available, and turn 1723 // security off if not. 1724 1725 static { 1726 1727 securityEnabled = true; 1728 try { 1729 Class c = Class.forName("com.sun.slp.AuthBlock"); 1730 1731 } catch (ClassNotFoundException e) { 1732 securityEnabled = false; 1733 } 1734 } 1735 1736 } 1737