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) 2001 by Sun Microsystems, Inc. 26 * All rights reserved. 27 * 28 */ 29 30 // SCCS Status: %W% %G% 31 // SLPHeaderV2.java: Base class for Service Location Messages 32 // Author: James Kempf 33 // Created On: Thu Oct 9 08:50:31 1997 34 // Last Modified By: James Kempf 35 // Last Modified On: Wed Jan 6 15:24:26 1999 36 // Update Count: 472 37 // 38 39 package com.sun.slp; 40 41 import java.util.*; 42 43 import java.net.*; 44 import java.io.*; 45 46 /** 47 * The SLPHeaderV2 class serves as the header class for all SLPv2 messages. 48 * It contains instance variables for SLP message header contents, 49 * and implements the SLPHeaderV2 interface. 50 * 51 * @version %R%.%L% %D% 52 * @author James Kempf 53 */ 54 55 class SLPHeaderV2 extends SrvLocHeader implements Cloneable { 56 57 // Support for options. 58 59 private int optOff = 0; 60 Hashtable optTable = new Hashtable(); 61 62 // Maximum message size (24 bits). 63 64 private final static int MAX_MESSAGE_LENGTH = 0xffffff; 65 66 // Location of flag byte. 67 68 static final int FLAG_BYTE = 4; 69 70 // Various header flags. 71 72 protected static final int NOFLAG = 0x00; 73 static final int OVERFLOW = 0x80; // needed by Transact. 74 protected static final int FRESH = 0x40; 75 protected static final int MCAST = 0x20; 76 77 // Header sizes. Note that this doesn't include the language tag, 78 // which is variable. 79 80 protected static final int REST_HEADER_BYTES = 12; 81 protected static final int HEADER_BYTES = 82 VERSION_FUNCTION_BYTES + REST_HEADER_BYTES; 83 84 // Maximum protected scopes allowed. 85 86 protected static final int MAX_PROTECTED_SCOPES = 255; 87 88 // Registered option classes. 89 90 protected static Hashtable optClasses = new Hashtable(); 91 92 // Manditory option range. 93 94 protected static int MANDATORY_OPTION_LOW = 0x4000; 95 protected static int MANDATORY_OPTION_HIGH = 0x7fff; 96 97 // Sizes of option id and extension fields (in bytes). 98 99 protected static int OPT_ID_SIZE = 2; 100 protected static int OPT_OFF_SIZE = 2; 101 102 // Interfaces for options to use. 103 104 interface OptionParser { 105 106 // Parse the option from the data stream. We include the header also, 107 // in case it is needed. 108 109 abstract SLPOption parse(SLPHeaderV2 hdr, DataInputStream dsr) 110 throws ServiceLocationException, IOException; 111 112 } 113 114 interface SLPOption { 115 116 // Externalize the option to the byte array stream. We include the 117 // header also, in case it is needed. 118 119 abstract void externalize(SLPHeaderV2 hdr, ByteArrayOutputStream baos) 120 throws ServiceLocationException; 121 122 } 123 124 // Register an option parsing class. 125 126 static void registerOptionClass(int id, Class optClass) { 127 128 Integer key = new Integer(id); 129 130 // We should probably check if it implements SLPOption.OptionParser, 131 // but... 132 133 optClasses.put(key, optClass); 134 135 } 136 137 // 138 // Header instance variables. 139 // 140 141 // For the incoming message side. 142 143 SLPHeaderV2() { 144 super(); 145 146 version = Defaults.version; 147 148 } 149 150 // Initialize the new SLPHeaderV2 from the input stream. Version and 151 // function code have already been removed from the stream. 152 153 void parseHeader(int functionCode, DataInputStream dis) 154 throws ServiceLocationException, IOException { 155 156 this.functionCode = functionCode; 157 158 nbytes += 2; // for version and function code... 159 160 // Get length. 161 162 length = getInt24(dis); 163 164 // Get flags. 165 166 byte[] b = new byte[2]; 167 168 dis.readFully(b, 0, 2); 169 170 nbytes += 2; 171 172 byte flags = (byte) ((char)b[0] & 0xFF); 173 174 overflow = ((flags & OVERFLOW) != NOFLAG); 175 fresh = ((flags & FRESH) != NOFLAG); 176 mcast = ((flags & MCAST) != NOFLAG); 177 178 // We could check for null on reserved part of flags field, but 179 // in the spirit of "be liberal in what you receive" we don't. 180 181 // Get option offset. 182 183 optOff = getInt24(dis); 184 185 // Check option offset for sanity. 186 187 if (optOff > length) { 188 throw 189 new ServiceLocationException( 190 ServiceLocationException.PARSE_ERROR, 191 "option_error", 192 new Object[] { 193 new Integer(optOff), new Integer(length)}); 194 195 } 196 197 // Get transaction id. 198 199 xid = (short)getInt(dis); 200 201 // Get language code. 202 203 StringBuffer buf = new StringBuffer(); 204 205 getString(buf, dis); 206 207 locale = SLPConfig.langTagToLocale(buf.toString()); 208 209 // Everything went OK coming in, so set the error code. 210 211 errCode = ServiceLocationException.OK; 212 } 213 214 // By default, the header parses the client side message. A server 215 // side subclass must replace this. We do this so that the amount of code 216 // in the client is minimized, since this class must be in both. 217 218 SrvLocMsg parseMsg(DataInputStream dis) 219 throws ServiceLocationException, 220 IOException, 221 IllegalArgumentException { 222 223 SrvLocMsg rply = null; 224 225 // Get the error code, if not SAAdvert. 226 227 if (functionCode != SrvLocHeader.SAAdvert) { 228 errCode = (short)getInt(dis); 229 230 } 231 232 // Switch and convert according to function code. 233 234 switch (functionCode) { 235 236 case SrvLocHeader.SrvRply: 237 rply = new CSrvMsg(this, dis); 238 break; 239 240 case SrvLocHeader.AttrRply: 241 rply = new CAttrMsg(this, dis); 242 break; 243 244 case SrvLocHeader.SrvTypeRply: 245 rply = new CSrvTypeMsg(this, dis); 246 break; 247 248 case SrvLocHeader.DAAdvert: 249 rply = new CDAAdvert(this, dis); 250 break; 251 252 case SrvLocHeader.SrvAck: 253 254 // We act as a SrvAck. 255 256 rply = this; 257 iNumReplies = 1; 258 break; 259 260 case SrvLocHeader.SAAdvert: 261 rply = new CSAAdvert(this, dis); 262 break; 263 264 default: 265 throw 266 new ServiceLocationException( 267 ServiceLocationException.PARSE_ERROR, 268 "function_code_error", 269 new Object[] { 270 new Integer(functionCode)}); 271 272 } 273 274 // Check for size overflow. 275 276 if (nbytes > length) { 277 throw 278 new ServiceLocationException( 279 ServiceLocationException.PARSE_ERROR, 280 "length_overflow", 281 new Object[] { 282 new Integer(nbytes), new Integer(length)}); 283 284 } 285 286 return rply; 287 } 288 289 // Construct a header for output. Used by the client side code to 290 // construct an initial request and the server side code to construct 291 // a reply. 292 293 SLPHeaderV2(int functionCode, boolean fresh, Locale locale) 294 throws ServiceLocationException { 295 296 // Check for proper function code and nonnull locale. 297 298 Assert.slpassert(((functionCode <= SAAdvert) && 299 (functionCode >= SrvReq)), 300 "function_code_error", 301 new Object[] {new Integer(functionCode)}); 302 303 Assert.slpassert((locale != null), 304 "null_locale_error", 305 new Object[0]); 306 307 this.version = Defaults.version; 308 this.functionCode = functionCode; 309 this.locale = locale; 310 this.xid = getUniqueXID(); // client can change it later if they want. 311 this.fresh = fresh; 312 313 // If there's not enough for the error code (if any), then signal 314 // an error. The assumption here is that the message is going 315 // via UDP or multicast. 316 317 byte[] ltag = 318 getStringBytes(SLPConfig.localeToLangTag(locale), Defaults.UTF8); 319 int headerLen = ltag.length + HEADER_BYTES; 320 int payLen = packetLength - headerLen; 321 322 if (payLen < SHORT_SIZE) { 323 throw 324 new ServiceLocationException( 325 ServiceLocationException.BUFFER_OVERFLOW, 326 "buffer_overflow", 327 new Object[] { 328 new Integer(headerLen + SHORT_SIZE), 329 new Integer(packetLength)}); 330 } 331 } 332 333 // Externalize the message by converting it to bytes and writing 334 // it to the output stream. 335 336 public void 337 externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP) 338 throws ServiceLocationException { 339 340 // Convert the locale to a tag. We need the length. 341 342 byte[] ltagBytes = 343 getStringBytes(SLPConfig.localeToLangTag(locale), Defaults.UTF8); 344 int ltagLen = ltagBytes.length; 345 346 // Set the multicast flag. 347 348 this.mcast = mcast; 349 350 // We need to get stuff into another stream first, so we can correctly 351 // calculate the length. 352 353 ByteArrayOutputStream bbaos = new ByteArrayOutputStream(); 354 355 // Need to put in the error code. There will only be an error code 356 // if error codes are applicable for this message type. Note 357 // that room for the error code was reserved in the initial 358 // calculation of the header, so there should always be room 359 // for it, even if the packet overflowed otherwise. 360 361 if (functionCode == SrvLocHeader.SrvAck || 362 functionCode == SrvLocHeader.SrvTypeRply || 363 functionCode == SrvLocHeader.SrvRply || 364 functionCode == SrvLocHeader.AttrRply || 365 functionCode == SrvLocHeader.DAAdvert) { 366 putInt(errCode, bbaos); 367 368 } 369 370 // Put in the previous responders, if there are any. Note that 371 // there may be only when the error code is not put out. 372 // We check against the packet size during parsing so that 373 // we don't overflow the packet and throw a special exception 374 // if an overflow happens. We only put out the previous 375 // responders list if the request is going by multicast, but 376 // we need to put out an empty vector for unicast requests. 377 378 int prevResLen = 379 packetLength - (payload.length + HEADER_BYTES + ltagLen); 380 Vector resp = previousResponders; 381 382 if (resp != null) { 383 resp = (mcast ? resp:new Vector()); 384 385 parsePreviousRespondersOut(resp, bbaos, prevResLen); 386 387 } 388 389 // If the error code is OK, then insert the rest of the message 390 // and parse the options. If there was an error, 391 // this step is skipped because the data isn't relevant. 392 393 if (errCode == ServiceLocationException.OK) { 394 bbaos.write(payload, 0, payload.length); 395 396 // Externalize any options. 397 398 optOff = externalizeOptions(bbaos, ltagLen); 399 } 400 401 byte[] payloadBytes = bbaos.toByteArray(); 402 403 // Set the length here to the actual length of the packet. 404 405 length = HEADER_BYTES + ltagLen + payloadBytes.length; 406 407 // If we exceed the 24 bit length size, we are hosed. 408 409 if (length > MAX_MESSAGE_LENGTH) { 410 throw 411 new ServiceLocationException( 412 ServiceLocationException.PARSE_ERROR, 413 "max_msg_size_exceeded", 414 new Object[0]); 415 416 } 417 418 // Truncate if necessary. We will always have room for a header 419 // and error code because we check when creating the object. 420 // Note that no URL block will be truncated because the spec 421 // says it can't be. 422 423 if (!isTCP && (length > packetLength)) { 424 overflow = true; 425 length = packetLength; 426 byte[] newBytes = new byte[packetLength]; 427 System.arraycopy(payloadBytes, 0, newBytes, 0, 428 length - (HEADER_BYTES + ltagLen)); 429 payloadBytes = newBytes; 430 431 } 432 433 // 434 // Write out the header. 435 // 436 437 // Write version and function code. 438 439 baos.write((byte) (0xFF & version)); 440 baos.write((byte) (0xFF & functionCode)); 441 442 // Put length in. 443 444 putInt24(length, baos); 445 446 // Put in flags. 447 448 byte flags = (byte)NOFLAG; 449 450 if (overflow) { 451 flags = (byte)(flags | OVERFLOW); 452 } else { 453 flags = (byte)(flags & ~OVERFLOW); 454 } 455 456 if (fresh) { 457 flags = (byte)((flags | FRESH) & 0xFF); 458 } else { 459 flags = (byte)((flags & ~FRESH) & 0xFF); 460 } 461 462 if (mcast) { 463 flags = (byte)((flags | MCAST) & 0xFF); 464 } else { 465 flags = (byte)((flags & ~MCAST) & 0xFF); 466 } 467 468 // Write out flags. 469 470 baos.write((byte) (0xFF & flags)); 471 baos.write((byte)0); 472 473 putInt24(optOff, baos); // write option offset, if any. 474 475 putInt(xid, baos); // write xid. 476 477 putInt(ltagLen, baos); // write lang size. 478 baos.write(ltagBytes, 0, ltagBytes.length); // write lang tag. 479 480 // 481 // Write the body. 482 // 483 484 baos.write(payloadBytes, 0, payloadBytes.length); 485 486 } 487 488 // 489 // Option handling. 490 // 491 492 // Parse any options. 493 494 void parseOptions(DataInputStream dsr) 495 throws ServiceLocationException, 496 IOException, 497 IllegalArgumentException { 498 499 // If no options return. 500 501 if (optOff == 0) { 502 return; 503 504 } 505 506 int optNext = 0; 507 508 // Parse any options in the data stream. 509 510 do { 511 512 // Parse extension id. 513 514 int optId = getInt(dsr); 515 516 // Parse extension offset. 517 518 optNext = getInt(dsr); 519 520 // Lookup an option parser. 521 522 Integer key = new Integer(optId); 523 524 Class optClass = (Class)optClasses.get(key); 525 526 // May be an exception if class is null. 527 528 if (optClass == null) { 529 530 // In mandatory range. Throw an exception. 531 532 if ((optId >= MANDATORY_OPTION_LOW) && 533 (optId <= MANDATORY_OPTION_HIGH)) { 534 throw 535 new ServiceLocationException( 536 ServiceLocationException.OPTION_NOT_SUPPORTED, 537 "v2_unsup_option", 538 new Object[] {key}); 539 540 } 541 542 // Skip the rest of the option. 543 544 int skipStart = length; 545 546 if (optNext != 0) { 547 skipStart = optNext; 548 549 } 550 551 dsr.skipBytes(skipStart - nbytes); 552 553 554 } else { 555 556 try { 557 558 // Parse the option. 559 560 OptionParser optParser = 561 (OptionParser)optClass.newInstance(); 562 563 SLPOption opt = optParser.parse(this, dsr); 564 565 // Insert option into option table. 566 567 optTable.put(key, opt); 568 569 } catch (InstantiationException ex) { 570 571 throw 572 new ServiceLocationException( 573 ServiceLocationException.INTERNAL_SYSTEM_ERROR, 574 "v2_option_inst", 575 new Object[] {key, ex}); 576 577 } catch (IllegalAccessException ex) { 578 579 throw 580 new ServiceLocationException( 581 ServiceLocationException.INTERNAL_SYSTEM_ERROR, 582 "v2_option_sec", 583 new Object[] {key, ex}); 584 } 585 } 586 } while (optNext != 0); 587 } 588 589 // Externalize any options. 590 591 private int externalizeOptions(ByteArrayOutputStream baos, int langTagLen) 592 throws ServiceLocationException { 593 594 // Calculate offset to options, if any. 595 596 int toOpt = 0; 597 598 if (optTable.size() <= 0) { 599 return toOpt; 600 601 } 602 603 toOpt = HEADER_BYTES + langTagLen + baos.size(); 604 605 // For all options in the table, parse them out. 606 607 Enumeration en = optTable.keys(); 608 int nextOpt = toOpt; 609 610 while (en.hasMoreElements()) { 611 Integer id = (Integer)en.nextElement(); 612 SLPOption opt = (SLPOption)optTable.get(id); 613 ByteArrayOutputStream obaos = new ByteArrayOutputStream(); 614 615 // Linearize option object. 616 617 opt.externalize(this, obaos); 618 619 // Calculate offset to next options. 620 621 nextOpt += obaos.size() + OPT_ID_SIZE + OPT_OFF_SIZE; 622 623 // Plop it into the output stream. 624 625 putInt(id.intValue(), baos); 626 627 628 // Check whether there are more options first. If so, then 629 // the next offset is zero. 630 631 if (en.hasMoreElements()) { 632 putInt(nextOpt, baos); 633 634 } else { 635 putInt(0, baos); 636 637 } 638 639 byte[] bytes = obaos.toByteArray(); 640 641 baos.write(bytes, 0, bytes.length); 642 643 } 644 645 return toOpt; 646 } 647 648 // Parse the previous responder list out, being sure to truncate 649 // so the list is syntactically correct if there is an overflow. 650 // This duplicates the comma separated list code to a certain 651 // extent. 652 653 private void 654 parsePreviousRespondersOut(Vector resp, 655 ByteArrayOutputStream baos, 656 int available) 657 throws ServiceLocationException { 658 659 ByteArrayOutputStream bbaos = new ByteArrayOutputStream(); 660 int i, n = resp.size(); 661 662 for (i = 0; i < n; i++) { 663 String address = (String)resp.elementAt(i); 664 665 // Add comma if necessary. 666 667 if (i > 0) { 668 address = "," + address; 669 670 } 671 672 // Convert to UTF8 bytes. 673 674 byte[] bytes = getStringBytes(address, Defaults.UTF8); 675 676 // Write bytes to stream if there's room. 677 678 if (bytes.length <= available) { 679 bbaos.write(bytes, 0, bytes.length); 680 available = available - bytes.length; 681 682 } else { 683 684 // Throw exception, upper layers need to break off multicast. 685 // This exception should *never* be surfaced. 686 687 throw 688 new ServiceLocationException( 689 ServiceLocationException.PREVIOUS_RESPONDER_OVERFLOW, 690 "v2_prev_resp_overflow", 691 new Object[] {}); 692 } 693 } 694 695 // Now write to the real stream. 696 697 byte[] out = bbaos.toByteArray(); 698 putInt(out.length, baos); 699 baos.write(out, 0, out.length); 700 701 nbytes += out.length; 702 703 } 704 705 // 706 // Utilities for parsing service URL's and attribute lists, with 707 // authentication information. 708 // 709 710 // Parse in a service URL including lifetime if necessary. 711 712 ServiceURL 713 parseServiceURLIn(DataInputStream dis, 714 Hashtable authTable, 715 short err) 716 throws ServiceLocationException, IOException { 717 718 // Ignore reserved byte. 719 720 byte[] b = new byte[1]; 721 722 dis.readFully(b, 0, 1); 723 724 nbytes += 1; 725 726 // Get URL lifetime. 727 728 int lifetime = getInt(dis); 729 730 // Get URL. 731 732 StringBuffer buf = new StringBuffer(); 733 734 byte[] rawBytes = getString(buf, dis); 735 736 // Get auth block, if any. 737 738 Hashtable auth = null; 739 740 // Get number of auth blocks. 741 742 b = new byte[1]; 743 744 dis.readFully(b, 0, 1); 745 746 nbytes += 1; 747 748 byte nauths = (byte)(b[0] & 0xFF); 749 750 if (nauths > 0) { 751 ByteArrayOutputStream abaos = new ByteArrayOutputStream(); 752 putInteger(rawBytes.length, abaos); 753 Object[] message = new Object[2]; 754 message[0] = abaos.toByteArray(); 755 message[1] = rawBytes; 756 auth = getCheckedAuthBlockList(message, nauths, dis); 757 758 lifetime = AuthBlock.getShortestLifetime(auth); 759 760 } 761 762 String ssurl = buf.toString(); 763 ServiceURL url = null; 764 765 try { 766 767 url = new ServiceURL(ssurl, lifetime); 768 769 } catch (IllegalArgumentException ex) { 770 771 throw 772 new ServiceLocationException(err, 773 "malformed_url", 774 new Object[] {ex.getMessage()}); 775 776 } 777 778 if (auth != null) { 779 780 // Put it in the auth block for this URL. 781 782 authTable.put(url, auth); 783 784 } 785 786 return url; 787 } 788 789 // Parse out a service URL, create authentication blocks if necessary. 790 // Return true if the URL was output. Check that we don't overflow 791 // packet size in the middle. 792 793 boolean 794 parseServiceURLOut(ServiceURL surl, 795 boolean urlAuth, 796 Hashtable auth, 797 ByteArrayOutputStream baos, 798 boolean checkOverflow) 799 throws ServiceLocationException { 800 801 // We need to keep track of size, so we don't overflow packet length. 802 803 ByteArrayOutputStream bbaos = new ByteArrayOutputStream(); 804 805 int mbytes = nbytes; 806 807 // Convert the URL to bytes. 808 809 byte[] bytes = getStringBytes(surl.toString(), Defaults.UTF8); 810 811 // Parse out reserved. 812 813 bbaos.write((byte)(0xFF & 0)); 814 815 nbytes += 1; 816 817 // Parse out the lifetime. 818 819 putInt(surl.getLifetime(), bbaos); 820 821 byte bs = (byte)0; 822 823 // Process auth block list if required. 824 825 if (urlAuth) { 826 827 // Create an auth block if necessary. 828 829 if (auth == null) { 830 ByteArrayOutputStream abaos = new ByteArrayOutputStream(); 831 putInteger(bytes.length, abaos); 832 Object[] message = new Object[2]; 833 message[0] = abaos.toByteArray(); 834 message[1] = bytes; 835 auth = getCheckedAuthBlockList(message, surl.getLifetime()); 836 837 } 838 839 bs = (byte) auth.size(); 840 Object[] bytesArray = AuthBlock.getContents(auth); 841 bytes = (byte[]) bytesArray[1]; 842 843 } 844 845 // Put out the URL bytes. 846 847 putInt(bytes.length, bbaos); 848 bbaos.write(bytes, 0, bytes.length); 849 850 nbytes += bytes.length; 851 852 // Write auth block size. 853 854 bbaos.write((byte)(0xFF & bs)); 855 856 nbytes += 1; 857 858 // If there are auth blocks required, put them out now. 859 860 if (bs > (byte)0) { 861 AuthBlock.externalizeAll(this, auth, bbaos); 862 863 } 864 865 // If we can, write it out. 866 867 bytes = bbaos.toByteArray(); 868 869 if (!checkOverflow || nbytes <= packetLength) { 870 baos.write(bytes, 0, bytes.length); 871 return true; // nbytes already set... 872 873 } else { 874 nbytes = mbytes; // truncate... 875 return false; 876 877 } 878 } 879 880 // Parse in a potentially authenticated attribute list. 881 882 Hashtable 883 parseAuthenticatedAttributeVectorIn(Vector attrs, 884 DataInputStream dis, 885 boolean allowMultiValuedBooleans) 886 throws ServiceLocationException, IOException { 887 888 // First, parse in the attribute vector. 889 890 byte[] rawBytes = 891 parseAttributeVectorIn(attrs, dis, allowMultiValuedBooleans); 892 893 ByteArrayOutputStream abaos = new ByteArrayOutputStream(); 894 putInteger(rawBytes.length, abaos); 895 Object[] message = new Object[2]; 896 message[0] = abaos.toByteArray(); 897 message[1] = rawBytes; 898 899 // Get the attribute list signature, if necessary. 900 901 return parseSignatureIn(message, dis); 902 903 } 904 905 // Parse in a list of attributes into attrs, returing raw bytes. 906 // ServiceLocationAttribute objects. Clients take care of auth blocks. 907 908 byte[] 909 parseAttributeVectorIn(Vector attrs, 910 DataInputStream dis, 911 boolean allowMultiValuedBooleans) 912 throws ServiceLocationException, IOException { 913 914 StringBuffer buf = new StringBuffer(); 915 916 byte[] rawBytes = getString(buf, dis); 917 918 // Parse the list into ServiceLocationAttribute objects. 919 920 Vector attrForms = parseCommaSeparatedListIn(buf.toString(), false); 921 922 int i, n = attrForms.size(); 923 924 for (i = 0; i < n; i++) { 925 String attrForm = 926 (String)attrForms.elementAt(i); 927 928 attrs.addElement( 929 new ServiceLocationAttribute( 930 attrForm, allowMultiValuedBooleans)); 931 } 932 933 return rawBytes; 934 } 935 936 // Parse out a vector of ServiceLocationAttributes. Includes escaping 937 // characters. 938 byte[] 939 parseAttributeVectorOut(Vector v, 940 int lifetime, 941 boolean attrAuth, 942 Hashtable auth, 943 ByteArrayOutputStream baos, 944 boolean writeAuthCount) 945 throws ServiceLocationException { 946 947 byte[] bytes = null; 948 int nBlocks = 0; 949 950 // Convert attribute vector to comma separated list. 951 952 if (!attrAuth || auth == null) { 953 Vector strings = new Vector(); 954 Enumeration en = v.elements(); 955 956 // Convert the attributes to strings, escaping characters to 957 // escape. 958 959 while (en.hasMoreElements()) { 960 ServiceLocationAttribute attr = 961 (ServiceLocationAttribute)en.nextElement(); 962 963 strings.addElement(attr.externalize()); 964 965 } 966 967 // Create the comma separated list. 968 969 String clist = vectorToCommaSeparatedList(strings); 970 bytes = getStringBytes(clist, Defaults.UTF8); 971 972 if (attrAuth) { 973 ByteArrayOutputStream abaos = new ByteArrayOutputStream(); 974 putInteger(bytes.length, abaos); 975 Object[] message = new Object[2]; 976 message[0] = abaos.toByteArray(); 977 message[1] = bytes; 978 auth = getCheckedAuthBlockList(message, lifetime); 979 } 980 } else { 981 Object[] bytesArray = AuthBlock.getContents(auth); 982 bytes = (byte[]) bytesArray[1]; 983 984 } 985 986 // Get number of blocks if authentication. 987 988 if (auth != null) { 989 nBlocks = auth.size(); 990 991 } 992 993 994 // Write out the bytes. 995 996 putInt(bytes.length, baos); 997 baos.write(bytes, 0, bytes.length); 998 nbytes += bytes.length; 999 1000 // Write out number of auth blocks. 1001 1002 if (writeAuthCount) { 1003 baos.write((byte)(nBlocks & 0xFF)); 1004 nbytes += 1; 1005 } 1006 1007 // Write out the attribute authentication blocks. 1008 1009 if (attrAuth && nBlocks > 0) { 1010 AuthBlock.externalizeAll(this, auth, baos); 1011 } 1012 1013 return bytes; 1014 } 1015 1016 // Get an auth block list, checking first for security. 1017 1018 Hashtable getCheckedAuthBlockList(Object[] message, int lifetime) 1019 throws ServiceLocationException { 1020 1021 if (!SLPConfig.getSLPConfig().getHasSecurity()) { 1022 throw 1023 new ServiceLocationException( 1024 ServiceLocationException.AUTHENTICATION_ABSENT, 1025 "auth_classes_missing", 1026 new Object[0]); 1027 } 1028 1029 return AuthBlock.makeAuthBlocks(message, lifetime); 1030 } 1031 1032 // Get an SLPAuthBlockList, checking first if security is enabled. 1033 1034 Hashtable getCheckedAuthBlockList(Object[] message, 1035 byte nauth, 1036 DataInputStream dis) 1037 throws ServiceLocationException, IOException { 1038 1039 if (!SLPConfig.getSLPConfig().getHasSecurity()) { 1040 throw 1041 new ServiceLocationException( 1042 ServiceLocationException.AUTHENTICATION_ABSENT, 1043 "auth_classes_missing", 1044 new Object[0]); 1045 } 1046 1047 return AuthBlock.makeAuthBlocks(this, message, dis, nauth); 1048 } 1049 1050 // Parse in an attribute signature. 1051 1052 Hashtable parseSignatureIn(Object[] message, DataInputStream dis) 1053 throws ServiceLocationException, IOException { 1054 1055 Hashtable auth = null; 1056 1057 byte[] b = new byte[1]; 1058 1059 dis.readFully(b, 0, 1); 1060 nbytes += 1; 1061 1062 byte nauths = (byte)(b[0] & 0xFF); 1063 1064 if (nauths > 0) { 1065 auth = getCheckedAuthBlockList(message, nauths, dis); 1066 1067 } 1068 1069 return auth; 1070 } 1071 1072 // 1073 // Utility functions to help with verification of data. 1074 // 1075 1076 // Escape tags, check for strings. Trim trailing, leading whitespace, 1077 // since it is ignored for matching purposes and tags are only 1078 // used for matching. 1079 1080 void escapeTags(Vector t) 1081 throws ServiceLocationException { 1082 1083 int i, n = t.size(); 1084 1085 for (i = 0; i < n; i++) { 1086 Object o = t.elementAt(i); 1087 1088 if (o instanceof String) { 1089 1090 // Escape tag. 1091 1092 String tag = 1093 ServiceLocationAttribute.escapeAttributeString((String)o, 1094 false); 1095 1096 t.setElementAt(tag.trim(), i); 1097 1098 } else { 1099 throw 1100 new IllegalArgumentException( 1101 SLPConfig.getSLPConfig().formatMessage("nonstring_tag", 1102 new Object[0])); 1103 } 1104 } 1105 } 1106 1107 // Unescape tags. Trim trailing and leading whitespace since it is 1108 // ignored for matching purposes and tags are only used for matching. 1109 1110 void unescapeTags(Vector t) 1111 throws ServiceLocationException { 1112 1113 int i, n = t.size(); 1114 1115 for (i = 0; i < n; i++) { 1116 String tag = (String)t.elementAt(i); 1117 1118 tag = 1119 ServiceLocationAttribute.unescapeAttributeString(tag, false); 1120 1121 t.setElementAt(tag.trim(), i); 1122 } 1123 } 1124 1125 // Escape vector of scope strings. 1126 1127 static void escapeScopeStrings(Vector scopes) 1128 throws ServiceLocationException { 1129 1130 int i, n = scopes.size(); 1131 Vector ret = new Vector(); 1132 1133 for (i = 0; i < n; i++) { 1134 String scope = (String)scopes.elementAt(i); 1135 1136 scopes.setElementAt( 1137 ServiceLocationAttribute.escapeAttributeString(scope, false), 1138 i); 1139 } 1140 } 1141 1142 // Unescape vector of scope strings. 1143 1144 static void unescapeScopeStrings(Vector scopes) 1145 throws ServiceLocationException { 1146 1147 int i, n = scopes.size(); 1148 Vector ret = new Vector(); 1149 1150 for (i = 0; i < n; i++) { 1151 String scope = (String)scopes.elementAt(i); 1152 1153 scopes.setElementAt( 1154 ServiceLocationAttribute.unescapeAttributeString(scope, false), 1155 i); 1156 } 1157 } 1158 1159 // Error if somebody tries to do this client side. 1160 1161 SDAAdvert 1162 getDAAdvert(short xid, 1163 long timestamp, 1164 ServiceURL url, 1165 Vector scopes, 1166 Vector attrs) 1167 throws ServiceLocationException { 1168 1169 Assert.slpassert(false, 1170 "v2_daadvert_client_side", 1171 new Object[0]); 1172 1173 return null; // never get here... 1174 } 1175 1176 // Reimplement clone() to get the header size right. 1177 1178 public Object clone() 1179 throws CloneNotSupportedException { 1180 SLPHeaderV2 hdr = (SLPHeaderV2)super.clone(); 1181 1182 byte[] langBytes = getStringBytes(locale.toString(), 1183 Defaults.UTF8); 1184 1185 hdr.nbytes = HEADER_BYTES + langBytes.length + 2; // for error code... 1186 1187 return hdr; 1188 } 1189 } 1190