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 // SrvLocHeader.java: Abstract superclass for SLP Headers 28 // Author: James Kempf 29 // Created On: Mon Sep 14 12:47:20 1998 30 // Last Modified By: James Kempf 31 // Last Modified On: Mon Nov 23 14:32:50 1998 32 // Update Count: 55 33 // 34 35 package com.sun.slp; 36 37 import java.util.*; 38 import java.net.*; 39 import java.io.*; 40 41 /** 42 * SrvLocHeader handles different versions of the SLP header. Clients 43 * call the instance methods returned by newInstance(). If no version 44 * specific subclass exists for the version number, null is returned 45 * from newInstance. Parsing of the header and message bodies, and 46 * creation of error replies are handled by the version specific 47 * subclasses. We also let the SrvLocHeader serve as a SrvLocMsg object 48 * to handle the SrvAck, which only has an error code. 49 * 50 * @author James Kempf 51 */ 52 53 abstract class SrvLocHeader extends Object implements SrvLocMsg, Cloneable { 54 55 // Table of header classes. Keys are the version number. 56 57 private static final Hashtable classTable = new Hashtable(); 58 59 // Offset to XID. 60 61 static final int XID_OFFSET = 10; 62 63 // Common constants and instance variables. 64 65 // Number of bytes in the version and function fields. 66 67 static int VERSION_FUNCTION_BYTES = 2; 68 69 // SLP function codes. Even though SAAdvert isn't in all versions, 70 // we include it here. 71 72 static final int SrvReq = 1; 73 static final int SrvRply = 2; 74 static final int SrvReg = 3; 75 static final int SrvDereg = 4; 76 static final int SrvAck = 5; 77 static final int AttrRqst = 6; 78 static final int AttrRply = 7; 79 static final int DAAdvert = 8; 80 static final int SrvTypeRqst = 9; 81 static final int SrvTypeRply = 10; 82 static final int SAAdvert = 11; 83 84 static final String[] functionCodeAbbr = { 85 "0", 86 "SrvReq", 87 "SrvRply", 88 "SrvReg", 89 "SrvDereg", 90 "SrvAck", 91 "AttrRqst", 92 "AttrRply", 93 "DAAdvert", 94 "SrvTypeRqst", 95 "SrvTypeRply", 96 "SAAdvert", 97 }; 98 99 // Sizes of data items. 100 101 protected static final int BYTE_SIZE = 1; 102 protected static final int SHORT_SIZE = 2; 103 protected static final int INT24_SIZE = 3; 104 105 // 106 // Header instance variables. 107 // 108 109 // Unprotected for less code. 110 111 int version = 0; // version number 112 int functionCode = 0; // function code 113 int length = 0; // packet length 114 short xid = 0; // transaction id 115 short errCode = 116 ServiceLocationException.OK; // not applicable to start 117 Locale locale = Defaults.locale; // language locale 118 Vector previousResponders = null; // list of previous responders 119 Vector scopes = null; // list of scopes 120 boolean overflow = false; // Overflow flag 121 boolean fresh = false; // Fresh flag 122 boolean mcast = false; // Mulitcast flag. 123 byte[] payload = new byte[0]; // bytes of outgoing payload, 124 int nbytes = 0; // number of bytes processed 125 int packetLength = 0; // length of packet. 126 int iNumReplies = 0; // number of replies. 127 128 129 protected static short uniqueXID = 0; // outgoing transaction id. 130 131 // Message description. 132 133 private String msgType; 134 private String msgDescription; 135 136 SrvLocHeader()137 SrvLocHeader() { 138 139 packetLength = SLPConfig.getSLPConfig().getMTU(); 140 141 } 142 143 // 144 // SrvLocMsg Implementation. 145 // 146 getHeader()147 public SrvLocHeader getHeader() { 148 return this; 149 150 } 151 getErrorCode()152 public short getErrorCode() { 153 return errCode; 154 155 } 156 157 // Return number of replies to this message. 158 getNumReplies()159 public int getNumReplies() { 160 return iNumReplies; 161 162 } 163 164 // 165 // SrvLocHeader Interface. 166 // 167 168 // Register a new header class for version. Serious error, causing 169 // program termination, if we can't find it. 170 addHeaderClass(String className, int version)171 static void addHeaderClass(String className, int version) { 172 173 try { 174 175 Class headerClass = Class.forName(className); 176 177 classTable.put(new Integer(version), headerClass); 178 179 } catch (ClassNotFoundException ex) { 180 181 Assert.slpassert(false, 182 "no_class", 183 new Object[] {className}); 184 185 } 186 } 187 188 // Create a version specific instance. We use a naming convention 189 // to identify the version specific classes used to create the 190 // instance. 191 newInstance(int version)192 static SrvLocHeader newInstance(int version) { 193 194 try { 195 196 // Get header class. 197 198 Class hdrClass = (Class)classTable.get(new Integer(version)); 199 200 if (hdrClass == null) { 201 return null; 202 203 } 204 205 SrvLocHeader hdr = (SrvLocHeader)hdrClass.newInstance(); 206 207 return hdr; 208 209 } catch (Exception ex) { 210 211 SLPConfig.getSLPConfig().writeLog("slh_creation_exception", 212 new Object[] { 213 new Integer(version), 214 ex, 215 ex.getMessage()}); 216 return null; 217 218 } 219 220 } 221 222 // Parse the incoming stream to obtain the header. 223 parseHeader(int functionCode, DataInputStream dis)224 abstract void parseHeader(int functionCode, DataInputStream dis) 225 throws ServiceLocationException, IOException, IllegalArgumentException; 226 227 // Parse the incoming stream to obtain the message. 228 parseMsg(DataInputStream dis)229 abstract SrvLocMsg parseMsg(DataInputStream dis) 230 throws ServiceLocationException, IOException, IllegalArgumentException; 231 232 // Externalize the message. 233 234 abstract void externalize(ByteArrayOutputStream baos, boolean multicast, boolean isTCP)235 externalize(ByteArrayOutputStream baos, 236 boolean multicast, 237 boolean isTCP) 238 throws ServiceLocationException; 239 240 // Return the appropriately versioned DAAdvert. 241 242 abstract SDAAdvert getDAAdvert(short xid, long timestamp, ServiceURL url, Vector scopes, Vector attrs)243 getDAAdvert(short xid, 244 long timestamp, 245 ServiceURL url, 246 Vector scopes, 247 Vector attrs) 248 throws ServiceLocationException; 249 250 // 251 // Methods that some subclasses may reimplement. 252 // 253 254 // Parse any options. 255 parseOptions(DataInputStream dis)256 void parseOptions(DataInputStream dis) 257 throws ServiceLocationException, 258 IOException, 259 IllegalArgumentException { 260 261 } 262 263 // Create an error reply for this message. This reply will be appropriate 264 // for the server to send back to the client. Default is to do nothing, 265 // which is the code for the client. 266 makeErrorReply(Exception ex)267 SrvLocMsg makeErrorReply(Exception ex) { 268 return null; 269 270 } 271 272 // 273 // Common utilities for all versions. 274 // 275 276 // Set the packet length to the incoming value. 277 setPacketLength(int newLength)278 void setPacketLength(int newLength) { 279 280 if (newLength > 0) { 281 packetLength = newLength; 282 283 } 284 } 285 286 // Add an Internet address to the previous responders list. 287 addPreviousResponder(InetAddress addr)288 void addPreviousResponder(InetAddress addr) { 289 290 String hostAddr = addr.getHostAddress(); 291 292 Assert.slpassert((previousResponders != null), 293 "prev_resp_reply", 294 new Object[0]); 295 296 if (!previousResponders.contains(hostAddr)) { 297 previousResponders.addElement(hostAddr); 298 299 } 300 } 301 302 // Get a unique transaction id. 303 getUniqueXID()304 synchronized static short getUniqueXID() { 305 if (uniqueXID == 0) { 306 Random r = new Random(); 307 uniqueXID = (short)(r.nextInt() &0xFFFF); 308 } 309 uniqueXID++; 310 return (short)(uniqueXID & 0xFFFF); 311 } 312 313 // Parse 2-byte integer, bump byte count. 314 getInt(DataInputStream dis)315 int getInt(DataInputStream dis) 316 throws ServiceLocationException, IOException { 317 318 int ret = getInteger(dis); 319 320 nbytes += SHORT_SIZE; 321 322 return ret; 323 } 324 325 326 // Parse a 2-byte integer from the input stream. 327 getInteger(DataInputStream dis)328 static int getInteger(DataInputStream dis) 329 throws ServiceLocationException, IOException { 330 331 byte[] b = new byte[2]; 332 333 dis.readFully(b, 0, 2); 334 335 int x = (int)((char)b[0] & 0xFF); 336 int y = (int)((char)b[1] & 0xFF); 337 int z = x << 8; 338 z += y; 339 return z; 340 } 341 342 // Parse 2-byte integer, bump byte count. 343 putInt(int z, ByteArrayOutputStream baos)344 void putInt(int z, ByteArrayOutputStream baos) { 345 346 putInteger(z, baos); 347 348 nbytes += SHORT_SIZE; 349 350 } 351 352 // Parse a 2-byte integer to the output stream. 353 putInteger(int z, ByteArrayOutputStream baos)354 static void putInteger(int z, ByteArrayOutputStream baos) { 355 baos.write((byte) ((0xFF00 & z)>>8)); 356 baos.write((byte) (0xFF & z)); 357 } 358 359 360 // Parse a 3-byte integer from the byte input stream. 361 getInt24(DataInputStream dis)362 protected int getInt24(DataInputStream dis) 363 throws ServiceLocationException, IOException { 364 365 byte[] b = new byte[3]; 366 367 dis.readFully(b, 0, 3); 368 369 int w = (int)((char)b[0] & 0xFF); 370 int x = (int)((char)b[1] & 0xFF); 371 int y = (int)((char)b[2] & 0xFF); 372 int z = w << 16; 373 z += x << 8; 374 z += y; 375 nbytes += 3; 376 return z; 377 } 378 379 // Parse a 3-byte integer to the output stream. 380 putInt24(int z, ByteArrayOutputStream baos)381 protected void putInt24(int z, ByteArrayOutputStream baos) { 382 baos.write((byte) ((0xFF0000 & z) >> 16)); 383 baos.write((byte) ((0xFF00 & z)>>8)); 384 baos.write((byte) (0xFF & z)); 385 386 nbytes += 3; 387 } 388 389 390 // Parse string, bump byte count. Use UTF8 encoding. 391 getString(StringBuffer buf, DataInputStream dis)392 byte[] getString(StringBuffer buf, DataInputStream dis) 393 throws ServiceLocationException, IOException { 394 395 byte[] ret = getStringField(buf, dis, Defaults.UTF8); 396 397 nbytes += ret.length + SHORT_SIZE; 398 399 return ret; 400 } 401 402 // Parse a string with an initial length from the input stream. 403 // Convert it to the proper encoding. Return the raw bytes for 404 // auth block creation. 405 406 static byte[] getStringField(StringBuffer buf, DataInputStream dis, String encoding)407 getStringField(StringBuffer buf, DataInputStream dis, String encoding) 408 throws ServiceLocationException, IOException { 409 410 // Clear out buffer first. 411 412 buf.setLength(0); 413 414 // First get the length. 415 416 int i, n = 0; 417 418 n = getInteger(dis); 419 420 // Now get the bytes. 421 422 byte[] bytes = new byte[n]; 423 424 dis.readFully(bytes, 0, n); 425 426 // Convert to string and return. 427 428 buf.append(getBytesString(bytes, encoding)); 429 430 return bytes; 431 432 } 433 434 // Parse out string, bump byte count. Use UTF8 encoding. 435 putString(String string, ByteArrayOutputStream baos)436 byte[] putString(String string, ByteArrayOutputStream baos) { 437 438 byte[] bytes = putStringField(string, baos, Defaults.UTF8); 439 440 nbytes += bytes.length + SHORT_SIZE; 441 442 return bytes; 443 444 } 445 446 // Put a string with an initial length into the byte stream, converting 447 // into the proper encoding. 448 449 static byte[] putStringField(String string, ByteArrayOutputStream baos, String encoding)450 putStringField(String string, 451 ByteArrayOutputStream baos, 452 String encoding) { 453 454 byte[] bytes = getStringBytes(string, encoding); 455 456 // Put out the string's length in the encoding. 457 458 putInteger(bytes.length, baos); 459 460 // Now really write out the bytes. 461 462 baos.write(bytes, 0, bytes.length); 463 464 return bytes; 465 466 } 467 468 // Convert a Unicode string into encoded bytes. 469 getStringBytes(String string, String encoding)470 static byte[] getStringBytes(String string, String encoding) { 471 472 try { 473 return string.getBytes(encoding); 474 475 } catch (UnsupportedEncodingException ex) { 476 return new byte[0]; // won't happen, hopefully... 477 478 } 479 } 480 481 // Convert bytes into a Unicode string. 482 getBytesString(byte[] bytes, String encoding)483 static String getBytesString(byte[] bytes, String encoding) { 484 485 try { 486 return new String(bytes, encoding); 487 488 } catch (UnsupportedEncodingException ex) { 489 return ""; // won't happen, hopefully ... 490 491 } 492 493 } 494 495 // Parse a comma separated list of strings from the vector into the 496 // output stream. 497 498 protected byte[] parseCommaSeparatedListOut(Vector v, ByteArrayOutputStream baos)499 parseCommaSeparatedListOut(Vector v, 500 ByteArrayOutputStream baos) { 501 502 return putString(vectorToCommaSeparatedList(v), baos); 503 504 } 505 506 /** 507 * Create a comma separated list of strings out of the vector. 508 * 509 * @param v A Vector of strings. 510 */ 511 512 static String vectorToCommaSeparatedList(Vector v)513 vectorToCommaSeparatedList(Vector v) { 514 515 // Construct in a string buffer first. 516 517 int i, n = v.size(); 518 StringBuffer buf = new StringBuffer(); 519 520 521 for (i = 0; i < n; i++) { 522 String string = (String)v.elementAt(i); 523 524 // Add comma for previous one if we need it. 525 526 if (i != 0) { 527 buf.append(','); 528 } 529 530 buf.append(string); 531 532 } 533 534 // Return the bytes. 535 536 return buf.toString(); 537 } 538 539 /** 540 * @parameter The string has the format = STRING *("," STRING) 541 * @parameter A boolean indicating whether parens should be ignored or 542 * used for grouping. 543 * @return A vector (of Strings) based upon the (comma delimited) string. 544 */ parseCommaSeparatedListIn(String s, boolean ignoreParens)545 static Vector parseCommaSeparatedListIn(String s, boolean ignoreParens) 546 throws ServiceLocationException { 547 548 if (s == null) 549 return new Vector(); 550 if (s.length() == 0) 551 return new Vector(); 552 StringTokenizer st = new StringTokenizer(s, ",()", true); 553 try { 554 int level = 0; 555 String el = ""; 556 Vector v = new Vector(); 557 558 while (st.hasMoreElements()) { 559 String tok = st.nextToken(); 560 561 // It's an open paren, so begin collecting. 562 563 if (tok.equals("(")) { 564 565 // Increment the level if not ignoring parens, add to token 566 567 if (!ignoreParens) { 568 level++; 569 570 } 571 572 el += tok; 573 574 } else if (tok.equals(")")) { 575 576 // Decrement level if not ignoring parens. 577 578 if (!ignoreParens) { 579 level--; 580 581 } 582 583 el += tok; 584 585 } else if (tok.equals(",")) { 586 587 // Add if collecting. 588 589 if (level != 0) { 590 el += tok; 591 592 } else { 593 594 // Check for empty token. 595 596 if (el.length() <= 0) { 597 throw 598 new ServiceLocationException( 599 ServiceLocationException.PARSE_ERROR, 600 "csl_syntax_error", 601 new Object[] {s}); 602 } 603 604 // If not collecting, then close off the element. 605 606 v.addElement(el); 607 el = ""; 608 609 } 610 } else { 611 el += tok; 612 613 } 614 } 615 616 // Add last token, but check first for empty token. 617 618 if (el.length() <= 0) { 619 throw 620 new ServiceLocationException( 621 ServiceLocationException.PARSE_ERROR, 622 "csl_syntax_error", 623 new Object[] {s}); 624 } 625 626 v.addElement(el); 627 628 // If we're still collecting on close, then there's a syntax error. 629 630 if (level != 0) { 631 throw 632 new ServiceLocationException( 633 ServiceLocationException.PARSE_ERROR, 634 "csl_syntax_error", 635 new Object[] {s}); 636 } 637 638 return v; 639 } catch (NoSuchElementException nsee) { 640 throw 641 new ServiceLocationException( 642 ServiceLocationException.PARSE_ERROR, 643 "csl_syntax_error", 644 new Object[] {s}); 645 646 } 647 } 648 649 // Allow clients to clone the header. 650 clone()651 public Object clone() 652 throws CloneNotSupportedException { 653 SrvLocHeader hdr = (SrvLocHeader)super.clone(); 654 655 // Reinitialize some stuff. Subclasses must reimplement nbytes 656 // header size calculation. 657 658 hdr.length = 0; 659 hdr.payload = new byte[0]; 660 hdr.iNumReplies = 0; 661 // packetlength stays the same, we may be using the same transport. 662 663 return hdr; 664 665 } 666 667 // Construct a description of the header. Messages add individual 668 // descriptions to this. 669 constructDescription(String msgType, String msgDescription)670 protected void constructDescription(String msgType, 671 String msgDescription) { 672 this.msgType = msgType; 673 this.msgDescription = msgDescription; 674 } 675 getMsgType()676 public String getMsgType() { 677 if (msgType == null) { 678 if (functionCode > 0 && functionCode < functionCodeAbbr.length) { 679 return functionCodeAbbr[functionCode]; 680 } else { 681 return String.valueOf(functionCode); 682 } 683 } else { 684 return msgType; 685 } 686 } 687 getMsgDescription()688 public String getMsgDescription() { 689 return (msgDescription == null) ? "" : msgDescription; 690 } 691 } 692