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