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