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