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 2001-2002 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 */ 26 27 // SLPHeaderV1.java: SLPv1 Header. 28 // Author: James Kempf 29 // Created On: Thu Sep 10 15:12:14 1998 30 // Last Modified By: James Kempf 31 // Last Modified On: Wed Jan 20 15:38:07 1999 32 // Update Count: 59 33 // 34 35 package com.sun.slp; 36 37 import java.util.*; 38 import java.io.*; 39 40 /** 41 * The SLPHeaderV1 class models the SLPv1 server side header. 42 * 43 * @author James Kempf 44 */ 45 46 class SLPHeaderV1 extends SrvLocHeader implements Cloneable { 47 48 // Version number. 49 50 static int VERSION = 1; 51 52 // Function code for message reply. 53 54 int replyFunctionCode = SrvLocHeader.SrvAck; 55 56 // Various header flags. 57 58 protected static final int NOFLAG = 0x00; 59 protected static final int OVERFLOW = 0x80; 60 protected static final int MONOLING = 0x40; 61 protected static final int URLSIG = 0x20; 62 protected static final int ATTRSIG = 0x10; 63 protected static final int FRESH = 0x08; 64 65 protected static int LANG_CODE_BYTES = 2; 66 67 protected static int HEADER_BYTES = 68 VERSION_FUNCTION_BYTES + LANG_CODE_BYTES + 8; 69 70 // Characters to escape. 71 72 final private static String UNESCAPABLE_CHARS = ",=!></*()"; 73 final private static String ESCAPABLE_CHARS = 74 UNESCAPABLE_CHARS + "&#;"; 75 76 String charCode = IANACharCode.UTF8; // character encoding. 77 boolean monolingual = false; // monolingual flag. 78 79 // Used to construct a header in SrvLocHeader.newInstance(). 80 81 SLPHeaderV1() { 82 super(); 83 84 version = VERSION; 85 86 } 87 88 // Assign reply code based on function code type, then use superclass 89 // method to parse header. 90 91 void parseHeader(int functionCode, DataInputStream dis) 92 throws ServiceLocationException, IOException { 93 94 this.functionCode = functionCode; 95 96 // We ignore the error case here. 97 98 switch (functionCode) { 99 100 case SrvLocHeader.SrvReq: 101 replyFunctionCode = SrvLocHeader.SrvRply; 102 break; 103 104 case SrvLocHeader.AttrRqst: 105 replyFunctionCode = SrvLocHeader.AttrRply; 106 break; 107 108 case SrvLocHeader.SrvTypeRqst: 109 replyFunctionCode = SrvLocHeader.SrvTypeRply; 110 break; 111 112 } 113 114 length = getInt(dis); 115 byte flags = (byte) ((char)dis.read() & 0xFF); 116 nbytes++; 117 118 overflow = ((flags & OVERFLOW) != 0x00); 119 fresh = false; // fresh gets set on output in SLPv1 120 monolingual = ((flags & MONOLING) != 0x00); 121 boolean urlAuth = ((flags & URLSIG) != 0x00); 122 boolean attrAuth = ((flags & ATTRSIG) != 0x00); 123 124 // Security not handled for SLPv1. 125 126 if (urlAuth || attrAuth) { 127 throw 128 new ServiceLocationException( 129 ServiceLocationException.AUTHENTICATION_FAILED, 130 "v1_no_security", 131 new Object[0]); 132 } 133 134 int dialect = (int) ((char)dis.read() & 0xFF); 135 nbytes++; 136 137 // Dialect must be zero. 138 139 if (dialect != 0) { 140 throw 141 new ServiceLocationException( 142 ServiceLocationException.PARSE_ERROR, 143 "v1_nonzero_dialect", 144 new Object[0]); 145 146 } 147 148 byte a_bTemp[] = new byte[LANG_CODE_BYTES]; 149 a_bTemp[0] = (byte) dis.read(); 150 a_bTemp[1] = (byte) dis.read(); 151 nbytes += 2; 152 153 try { 154 locale = new Locale(new String(a_bTemp, IANACharCode.ASCII), ""); 155 156 } catch (UnsupportedEncodingException ex) { 157 158 } 159 160 int intCharCode = getInt(dis); 161 charCode = IANACharCode.decodeCharacterEncoding(intCharCode); 162 163 xid = (short)getInt(dis); 164 165 errCode = ServiceLocationException.OK; 166 } 167 168 // Parse an incoming V1 message and return the SrvLocMsg object. 169 170 SrvLocMsg parseMsg(DataInputStream dis) 171 throws ServiceLocationException, 172 IOException, 173 IllegalArgumentException { 174 175 SrvLocMsg msg = null; 176 177 // If this is a *multicast* request, we reject it except for DAAdvert. 178 // Multicast requests are only taken by SA servers. 179 180 if (mcast && (functionCode != SrvLocHeader.DAAdvert)) { 181 return null; 182 183 } 184 185 // Switch and convert according to function code. 186 187 switch (functionCode) { 188 189 case SrvLocHeader.SrvReq: 190 msg = new SLPV1SSrvMsg(this, dis); 191 break; 192 193 case SrvLocHeader.SrvReg: 194 msg = new SLPV1SSrvReg(this, dis); 195 break; 196 197 case SrvLocHeader.SrvDereg: 198 msg = new SLPV1SSrvDereg(this, dis); 199 break; 200 201 case SrvLocHeader.AttrRqst: 202 msg = new SLPV1SAttrMsg(this, dis); 203 break; 204 205 case SrvLocHeader.SrvTypeRqst: 206 msg = new SLPV1SSrvTypeMsg(this, dis); 207 break; 208 209 case SrvLocHeader.DAAdvert: 210 msg = new SLPV1CDAAdvert(this, dis); 211 break; 212 213 default: 214 throw 215 new ServiceLocationException( 216 ServiceLocationException.PARSE_ERROR, 217 "function_code_error", 218 new Object[] { 219 new Integer(functionCode)}); 220 221 } 222 223 // Check for size overflow. 224 225 if (nbytes > length) { 226 throw 227 new ServiceLocationException( 228 ServiceLocationException.PARSE_ERROR, 229 "length_overflow", 230 new Object[] { 231 new Integer(nbytes), new Integer(length)}); 232 233 } 234 235 return msg; 236 237 } 238 239 // Externalize the message by converting it to bytes and writing 240 // it to the output stream. 241 242 public void 243 externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP) 244 throws ServiceLocationException { 245 246 // Need to put in the error code or previous responders. 247 248 ByteArrayOutputStream fin = new ByteArrayOutputStream(); 249 250 if (functionCode == SrvLocHeader.SrvAck || 251 functionCode == SrvLocHeader.SrvTypeRply || 252 functionCode == SrvLocHeader.SrvRply || 253 functionCode == SrvLocHeader.AttrRply || 254 functionCode == SrvLocHeader.DAAdvert) { 255 putInt(errCode, fin); 256 257 } else { 258 259 // Parse out previous responders. Note there will only be some 260 // if the error code is not put out. 261 262 if (previousResponders != null) { 263 parseCommaSeparatedListOut(previousResponders, fin); 264 265 } 266 } 267 268 // Parse payload out if error code is OK and payload is nonnull. 269 270 if (payload != null && errCode == ServiceLocationException.OK) { 271 fin.write(payload, 0, payload.length); 272 } 273 274 // Don't touch payload here, somebody may put in a previousResponder 275 // and resend the message. 276 277 byte[] npayload = fin.toByteArray(); 278 279 // Set overflow flag if buffer is too large and this isn't going out 280 // via TCP. 281 282 if (((npayload.length + 12) > SLPConfig.getSLPConfig().getMTU()) && 283 !isTCP) { 284 overflow = true; 285 286 } 287 288 baos.write((byte) (0xFF & version)); 289 nbytes++; 290 baos.write((byte) (0xFF & functionCode)); 291 nbytes++; 292 293 length = npayload.length +12; // the 12 is the length of this header! 294 295 putInt(length, baos); // what about overflow??? 296 297 byte flags = 0X00; 298 299 if (overflow) { 300 flags = (byte)(flags | OVERFLOW); 301 } else { 302 flags = (byte)(flags & ~OVERFLOW); 303 } 304 305 if (monolingual) { 306 flags = (byte)(flags | MONOLING); 307 } else { 308 flags = (byte)(flags & ~MONOLING); 309 } 310 311 if (fresh) { 312 flags = (byte)((flags | FRESH) & 0XFF); 313 } else { 314 flags = (byte)((flags & ~FRESH) & 0XFF); 315 } 316 317 baos.write((byte) (0xFF & flags)); 318 nbytes++; 319 baos.write((byte) (0xFF & 0)); // dialect... 320 nbytes++; 321 322 String language = locale.getLanguage(); 323 324 baos.write((byte) (0xFF & language.charAt(0))); 325 baos.write((byte) (0xFF & language.charAt(1))); 326 nbytes += 2; 327 328 int intCharCode = 0; 329 330 try { 331 intCharCode = IANACharCode.encodeCharacterEncoding(charCode); 332 333 } catch (ServiceLocationException ex) { 334 Assert.slpassert(false, 335 "v1_unsupported_encoding", 336 new Object[] {charCode}); 337 338 } 339 340 putInt(intCharCode, baos); 341 putInt(xid, baos); 342 343 // Write the body. 344 345 baos.write(npayload, 0, npayload.length); 346 nbytes += npayload.length; 347 } 348 349 // Create an error reply using the reply code. Calculate the 350 // error code using the exception. 351 352 SrvLocMsg makeErrorReply(Exception ex) { 353 354 // If this is a DAAdvert, then no error reply is returned 355 // because V1 doesn't support unicast SrvRqst for DAAdvert. 356 357 if (functionCode == SrvLocHeader.DAAdvert) { 358 return null; 359 360 } 361 362 // Clone the header to make sure that everything else is the same. 363 // We don't want to use the same header because it may be tested 364 // elsewhere. 365 366 SLPHeaderV1 hdr = null; 367 368 try { 369 hdr = (SLPHeaderV1)this.clone(); 370 371 } catch (CloneNotSupportedException exx) { 372 373 // We know we support it. 374 375 } 376 377 hdr.fresh = false; 378 hdr.overflow = false; 379 hdr.mcast = false; 380 hdr.functionCode = replyFunctionCode; 381 382 // We should *not* be getting a null exception down this path! 383 384 Assert.slpassert(ex != null, 385 "null_parameter", 386 new Object[] {ex}); 387 388 if (ex instanceof ServiceLocationException) { 389 390 hdr.errCode = ((ServiceLocationException)ex).getErrorCode(); 391 392 // Handle monolingual bit here. If the exception is 393 // LANGUAGE_NOT_SUPPORTED and the message type is 394 // either SrvRqst or AttrRqst, then we simply return an 395 // empty message unless the monolingual flag is on. 396 397 if (hdr.errCode == 398 ServiceLocationException.LANGUAGE_NOT_SUPPORTED) { 399 400 try { 401 402 if (!hdr.monolingual) { 403 404 if (hdr.functionCode == SrvLocHeader.SrvReq) { 405 406 return SLPV1SSrvMsg.makeEmptyReply(hdr); 407 408 } else if (hdr.functionCode == SrvLocHeader.AttrRqst) { 409 410 return SLPV1SAttrMsg.makeEmptyReply(hdr); 411 412 } 413 } 414 415 } catch (ServiceLocationException exx) { 416 417 hdr.monolingual = true; 418 hdr.makeErrorReply(exx); 419 420 } 421 422 // Otherwise, we just ignore it. 423 } 424 425 // Anything over AUTHENTICATION_FAILED is an internal error in V1. 426 427 if (hdr.errCode > ServiceLocationException.AUTHENTICATION_FAILED) { 428 hdr.errCode = ServiceLocationException.PARSE_ERROR; 429 430 } 431 432 } else if (ex instanceof IllegalArgumentException || 433 ex instanceof IOException) { 434 hdr.errCode = ServiceLocationException.PARSE_ERROR; 435 436 } else { 437 hdr.errCode = ServiceLocationException.PARSE_ERROR; 438 439 } 440 441 // Construct header description. 442 443 hdr.constructDescription("SrvLocMsg", ""); 444 445 return hdr; 446 } 447 448 // Return a reply header with flags properly set. 449 450 SLPHeaderV1 makeReplyHeader() { 451 452 SLPHeaderV1 hdr = null; 453 454 try { 455 hdr = (SLPHeaderV1)this.clone(); 456 457 } catch (CloneNotSupportedException ex) { 458 459 // We know that we support it. 460 } 461 462 hdr.functionCode = replyFunctionCode; 463 hdr.length = 0; 464 hdr.previousResponders = null; 465 hdr.scopes = null; 466 hdr.overflow = false; 467 hdr.fresh = false; 468 hdr.mcast = false; 469 hdr.nbytes = 0; 470 471 return hdr; 472 } 473 474 // Return display string. 475 476 public String toString() { 477 return 478 getMsgType() + ":version=``" + version + "''\n" + 479 " functionCode=``" + functionCode + "''\n" + 480 " length=``" + length + "''\n" + 481 " overflow=``" + overflow + "''\n" + 482 " mcast = ``" + mcast + "''\n" + 483 " fresh=``" + fresh + "''\n" + 484 " monolingual=``" + monolingual + "''\n" + 485 " charCode=``" + charCode + "''\n" + 486 " locale = ``" + locale + "''\n" + 487 " xid=``0x" + Integer.toHexString(xid) + "''\n" + 488 " errCode=``" + errCode + "''\n" + 489 " previousResponders=``" + previousResponders + "''\n" + 490 " scopes=``" + scopes + "''\n" + 491 getMsgDescription(); 492 } 493 494 // 495 // Validation Utilities. 496 // 497 498 /** 499 * Validate the scope name to be sure it doesn't contain forbidden 500 * chars and isn't one of the reserved scope names. 501 */ 502 503 static void validateScope(String scope) 504 throws ServiceLocationException 505 { 506 if (scope.indexOf('/') != -1 || scope.indexOf(',') != -1 || 507 scope.indexOf(':') != -1) { 508 throw new ServiceLocationException( 509 ServiceLocationException.PARSE_ERROR, 510 "v1_scope_char_res", 511 new Object[] {scope}); 512 } 513 514 // Check against reserved scope names. 515 516 if (scope.equalsIgnoreCase("local") || 517 scope.equalsIgnoreCase("remote")) { 518 throw new ServiceLocationException( 519 ServiceLocationException.PARSE_ERROR, 520 "v1_scope_name_res", 521 new Object[] {scope}); 522 } 523 524 } 525 526 /** 527 * Remove IANA from the service type name. 528 * 529 * @param serviceType The service type and naming authority. 530 * @return The service type name with IANA removed. 531 */ 532 533 static String removeIANA(String serviceType) { 534 535 // Substitute null string for IANA. 536 537 int idx = 0; 538 539 serviceType = serviceType.toLowerCase(); 540 541 if ((idx = serviceType.indexOf("." + ServiceType.IANA)) != -1) { 542 serviceType = serviceType.substring(0, idx); 543 544 } 545 546 return serviceType; 547 } 548 549 // Check whether this is a vaild SLPv1 service type. Also remove 550 // IANA. 551 552 static String checkServiceType(String stype) 553 throws ServiceLocationException { 554 555 // Check for trailing colon and remove it. 556 557 if (!stype.endsWith(":")) { 558 throw 559 new ServiceLocationException( 560 ServiceLocationException.PARSE_ERROR, 561 "v1_service_type_format", 562 new Object[] {stype}); 563 564 } 565 566 String type = stype.substring(0, stype.length()-1); 567 568 // Remove IANA. 569 570 type = removeIANA(type); 571 572 // Check syntax. 573 574 ServiceType st = new ServiceType(type); 575 576 // Reject if abstract type. SLPv1 doesn't handle 577 // abstract types. 578 579 if (st.isAbstractType()) { 580 throw 581 new ServiceLocationException( 582 ServiceLocationException.PARSE_ERROR, 583 "v1_abstract_type", 584 new Object[0]); 585 586 } 587 588 // Reject if not a service: type. SLPv1 doesn't handle 589 // nonservice: types. 590 591 if (!st.isServiceURL()) { 592 throw 593 new ServiceLocationException( 594 ServiceLocationException.PARSE_ERROR, 595 "v1_not_surl", 596 new Object[0]); 597 598 } 599 600 return type; 601 } 602 603 // 604 // Parsing Utilities. 605 // 606 607 // Parse string, bump byte count. 608 609 byte[] getString(StringBuffer buf, DataInputStream dis) 610 throws ServiceLocationException, IOException { 611 612 int i, n = 0; 613 614 // Get length. 615 616 n = getInteger(dis); 617 618 byte[] bytes = new byte[n]; 619 620 // Read bytes. 621 622 dis.readFully(bytes, 0, n); 623 624 // If the encoding type is Unicode, then figure out first 625 // whether it's big or little endian from byte header. Future 626 // calls won't have to go through this grief unless the byte header 627 // is missing. 628 629 if (this.charCode == IANACharCode.UNICODE) { 630 631 this.charCode = IANACharCode.getUnicodeEndianess(bytes); 632 633 } 634 635 String charCode = this.charCode; 636 637 // If we are still just Unicode by this point, then we need to 638 // add the big endian bytes to the beginning of the array. 639 // Otherwise, Java won't parse it. Note that we don't change 640 // the flag in the header, since we will need to convert the 641 // next time around as well. 642 643 if (charCode == IANACharCode.UNICODE) { 644 charCode = IANACharCode.UNICODE_BIG; 645 646 bytes = IANACharCode.addBigEndianFlag(bytes); 647 648 } 649 650 // Convert the bytes into a string. 651 652 buf.setLength(0); 653 654 buf.append(getBytesString(bytes, charCode)); 655 656 return bytes; 657 } 658 659 // Parse out string, bump byte count. Use header encoding. 660 661 byte[] putString(String string, ByteArrayOutputStream baos) { 662 663 // If the charCode is UNICODE, arbirtarily change to big or little, 664 // while Java will parse. 665 666 if (charCode == IANACharCode.UNICODE) { 667 charCode = IANACharCode.UNICODE_BIG; 668 669 } 670 671 byte[] bytes = putStringField(string, baos, charCode); 672 673 nbytes += bytes.length; 674 675 return bytes; 676 677 } 678 679 // Parse in a service URL including lifetime if necessary. 680 681 protected ServiceURL 682 parseServiceURLIn(DataInputStream dis, 683 boolean lifeTimeToo, 684 short errCode) 685 throws ServiceLocationException, IOException { 686 687 int lifetime = 0; 688 StringBuffer buf = new StringBuffer(); 689 690 if (lifeTimeToo) { 691 lifetime = getInt(dis); 692 } 693 694 getString(buf, dis); 695 696 ServiceURL url = null; 697 698 try { 699 700 url = new ServiceURLV1(buf.toString(), lifetime); 701 702 } catch (IllegalArgumentException ex) { 703 704 throw 705 new ServiceLocationException(errCode, 706 "malformed_url", 707 new Object[] {ex}); 708 } 709 710 return url; 711 } 712 713 // Parse out a service URL including lifetime if required. 714 715 void 716 parseServiceURLOut(ServiceURL surl, 717 boolean lifetimeToo, 718 ByteArrayOutputStream baos) 719 throws ServiceLocationException { 720 721 String ssurl = surl.toString(); 722 723 if (lifetimeToo) { 724 putInt(surl.getLifetime(), baos); 725 } 726 727 putString(ssurl, baos); 728 729 } 730 731 // Parse in a list of attributes, returing a vector of 732 // ServiceLocationAttribute objects. 733 734 protected Vector parseAttributeVectorIn(DataInputStream dis) 735 throws ServiceLocationException, IOException { 736 737 StringBuffer buf = new StringBuffer(); 738 739 getString(buf, dis); 740 741 SLPConfig config = SLPConfig.getSLPConfig(); 742 743 // Parse the list into ServiceLocationAttribute objects. 744 745 Vector attrForms = parseCommaSeparatedListIn(buf.toString(), false); 746 747 int i, n = attrForms.size(); 748 749 for (i = 0; i < n; i++) { 750 String attrForm = 751 (String)attrForms.elementAt(i); 752 753 attrForms.setElementAt(new ServiceLocationAttributeV1(attrForm, 754 charCode, 755 false), 756 i); 757 } 758 759 return attrForms; 760 } 761 762 // Parse out a V1 attribute vector. 763 764 void 765 parseAttributeVectorOut(Vector attrs, 766 ByteArrayOutputStream baos) 767 throws ServiceLocationException { 768 769 Enumeration en = attrs.elements(); 770 Vector strings = new Vector(); 771 772 // Convert the attributes to strings, escaping characters to 773 // escape. 774 775 while (en.hasMoreElements()) { 776 ServiceLocationAttribute attr = 777 (ServiceLocationAttribute)en.nextElement(); 778 779 // Make an SLPv1 attribute out of it, so we can 780 // externalize it with the v1 encoding scheme. 781 782 ServiceLocationAttributeV1 attrv1 = 783 new ServiceLocationAttributeV1(attr); 784 attrv1.charCode = charCode; 785 String out = attrv1.externalize(); 786 787 strings.addElement(out); 788 789 } 790 791 // Parse it out. 792 793 parseCommaSeparatedListOut(strings, baos); 794 795 } 796 797 // Parse in previous responders. 798 799 void parsePreviousRespondersIn(DataInputStream dis) 800 throws ServiceLocationException, IOException { 801 802 StringBuffer buf = new StringBuffer(); 803 804 getString(buf, dis); 805 806 previousResponders = 807 parseCommaSeparatedListIn(buf.toString(), true); 808 809 } 810 811 // Put out a vector of strings. 812 813 void putStringVector(Vector v, ByteArrayOutputStream baos) { 814 815 int i, n = v.size(); 816 817 // Put out the total number of strings. 818 819 putInt(n, baos); 820 821 // Put out the strings. 822 823 for (i = 0; i < n; i++) { 824 825 putString((String)v.elementAt(i), baos); 826 } 827 } 828 829 // Return an SLPv1 DAAdvert. 830 831 SDAAdvert 832 getDAAdvert(short xid, 833 long timestamp, 834 ServiceURL url, 835 Vector scopes, 836 Vector attrs) 837 throws ServiceLocationException { 838 839 // If scopes vector is null, then return all scopes for this 840 // DA. 841 842 if (scopes.size() <= 0) { 843 scopes = SLPConfig.getSLPConfig().getSAConfiguredScopes(); 844 845 } 846 847 return new SLPV1SDAAdvert(this, xid, timestamp, url, scopes, attrs); 848 849 } 850 851 // Reimplement clone() to get the header size right. 852 853 public Object clone() 854 throws CloneNotSupportedException { 855 SLPHeaderV1 hdr = (SLPHeaderV1)super.clone(); 856 857 hdr.nbytes = HEADER_BYTES + 2; // for error code... 858 859 return hdr; 860 } 861 } 862