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 SLPHeaderV1()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 parseHeader(int functionCode, DataInputStream dis)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 parseMsg(DataInputStream dis)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 externalize(ByteArrayOutputStream baos, boolean mcast, boolean isTCP)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 makeErrorReply(Exception ex)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 makeReplyHeader()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 toString()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 validateScope(String scope)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 removeIANA(String serviceType)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 checkServiceType(String stype)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 getString(StringBuffer buf, DataInputStream dis)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 putString(String string, ByteArrayOutputStream baos)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 parseServiceURLIn(DataInputStream dis, boolean lifeTimeToo, short errCode)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 parseServiceURLOut(ServiceURL surl, boolean lifetimeToo, ByteArrayOutputStream baos)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 parseAttributeVectorIn(DataInputStream dis)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 parseAttributeVectorOut(Vector attrs, ByteArrayOutputStream baos)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 parsePreviousRespondersIn(DataInputStream dis)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 putStringVector(Vector v, ByteArrayOutputStream baos)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 getDAAdvert(short xid, long timestamp, ServiceURL url, Vector scopes, Vector attrs)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 clone()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