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 parse(SLPHeaderV2 hdr, DataInputStream dsr)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 externalize(SLPHeaderV2 hdr, ByteArrayOutputStream baos)114 abstract void externalize(SLPHeaderV2 hdr, ByteArrayOutputStream baos) 115 throws ServiceLocationException; 116 117 } 118 119 // Register an option parsing class. 120 registerOptionClass(int id, Class optClass)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 SLPHeaderV2()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 parseHeader(int functionCode, DataInputStream dis)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 parseMsg(DataInputStream dis)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 SLPHeaderV2(int functionCode, boolean fresh, Locale locale)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 externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP)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 parseOptions(DataInputStream dsr)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 externalizeOptions(ByteArrayOutputStream baos, int langTagLen)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 parsePreviousRespondersOut(Vector resp, ByteArrayOutputStream baos, int available)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 parseServiceURLIn(DataInputStream dis, Hashtable authTable, short err)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 parseServiceURLOut(ServiceURL surl, boolean urlAuth, Hashtable auth, ByteArrayOutputStream baos, boolean checkOverflow)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 parseAuthenticatedAttributeVectorIn(Vector attrs, DataInputStream dis, boolean allowMultiValuedBooleans)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[] parseAttributeVectorIn(Vector attrs, DataInputStream dis, boolean allowMultiValuedBooleans)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[] parseAttributeVectorOut(Vector v, int lifetime, boolean attrAuth, Hashtable auth, ByteArrayOutputStream baos, boolean writeAuthCount)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 getCheckedAuthBlockList(Object[] message, int lifetime)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 getCheckedAuthBlockList(Object[] message, byte nauth, DataInputStream dis)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 parseSignatureIn(Object[] message, DataInputStream dis)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 escapeTags(Vector t)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 unescapeTags(Vector t)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 escapeScopeStrings(Vector scopes)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 unescapeScopeStrings(Vector scopes)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 getDAAdvert(short xid, long timestamp, ServiceURL url, Vector scopes, Vector attrs)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 clone()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