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 137 SrvLocHeader() { 138 139 packetLength = SLPConfig.getSLPConfig().getMTU(); 140 141 } 142 143 // 144 // SrvLocMsg Implementation. 145 // 146 147 public SrvLocHeader getHeader() { 148 return this; 149 150 } 151 152 public short getErrorCode() { 153 return errCode; 154 155 } 156 157 // Return number of replies to this message. 158 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 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 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 224 abstract void parseHeader(int functionCode, DataInputStream dis) 225 throws ServiceLocationException, IOException, IllegalArgumentException; 226 227 // Parse the incoming stream to obtain the message. 228 229 abstract SrvLocMsg parseMsg(DataInputStream dis) 230 throws ServiceLocationException, IOException, IllegalArgumentException; 231 232 // Externalize the message. 233 234 abstract void 235 externalize(ByteArrayOutputStream baos, 236 boolean multicast, 237 boolean isTCP) 238 throws ServiceLocationException; 239 240 // Return the appropriately versioned DAAdvert. 241 242 abstract SDAAdvert 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 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 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 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 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 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 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 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 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 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 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 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 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[] 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 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[] 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 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 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[] 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 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 */ 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 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 670 protected void constructDescription(String msgType, 671 String msgDescription) { 672 this.msgType = msgType; 673 this.msgDescription = msgDescription; 674 } 675 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 688 public String getMsgDescription() { 689 return (msgDescription == null) ? "" : msgDescription; 690 } 691 } 692