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