/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * */ // SrvLocHeader.java: Abstract superclass for SLP Headers // Author: James Kempf // Created On: Mon Sep 14 12:47:20 1998 // Last Modified By: James Kempf // Last Modified On: Mon Nov 23 14:32:50 1998 // Update Count: 55 // package com.sun.slp; import java.util.*; import java.net.*; import java.io.*; /** * SrvLocHeader handles different versions of the SLP header. Clients * call the instance methods returned by newInstance(). If no version * specific subclass exists for the version number, null is returned * from newInstance. Parsing of the header and message bodies, and * creation of error replies are handled by the version specific * subclasses. We also let the SrvLocHeader serve as a SrvLocMsg object * to handle the SrvAck, which only has an error code. * * @author James Kempf */ abstract class SrvLocHeader extends Object implements SrvLocMsg, Cloneable { // Table of header classes. Keys are the version number. private static final Hashtable classTable = new Hashtable(); // Offset to XID. static final int XID_OFFSET = 10; // Common constants and instance variables. // Number of bytes in the version and function fields. static int VERSION_FUNCTION_BYTES = 2; // SLP function codes. Even though SAAdvert isn't in all versions, // we include it here. static final int SrvReq = 1; static final int SrvRply = 2; static final int SrvReg = 3; static final int SrvDereg = 4; static final int SrvAck = 5; static final int AttrRqst = 6; static final int AttrRply = 7; static final int DAAdvert = 8; static final int SrvTypeRqst = 9; static final int SrvTypeRply = 10; static final int SAAdvert = 11; static final String[] functionCodeAbbr = { "0", "SrvReq", "SrvRply", "SrvReg", "SrvDereg", "SrvAck", "AttrRqst", "AttrRply", "DAAdvert", "SrvTypeRqst", "SrvTypeRply", "SAAdvert", }; // Sizes of data items. protected static final int BYTE_SIZE = 1; protected static final int SHORT_SIZE = 2; protected static final int INT24_SIZE = 3; // // Header instance variables. // // Unprotected for less code. int version = 0; // version number int functionCode = 0; // function code int length = 0; // packet length short xid = 0; // transaction id short errCode = ServiceLocationException.OK; // not applicable to start Locale locale = Defaults.locale; // language locale Vector previousResponders = null; // list of previous responders Vector scopes = null; // list of scopes boolean overflow = false; // Overflow flag boolean fresh = false; // Fresh flag boolean mcast = false; // Mulitcast flag. byte[] payload = new byte[0]; // bytes of outgoing payload, int nbytes = 0; // number of bytes processed int packetLength = 0; // length of packet. int iNumReplies = 0; // number of replies. protected static short uniqueXID = 0; // outgoing transaction id. // Message description. private String msgType; private String msgDescription; SrvLocHeader() { packetLength = SLPConfig.getSLPConfig().getMTU(); } // // SrvLocMsg Implementation. // public SrvLocHeader getHeader() { return this; } public short getErrorCode() { return errCode; } // Return number of replies to this message. public int getNumReplies() { return iNumReplies; } // // SrvLocHeader Interface. // // Register a new header class for version. Serious error, causing // program termination, if we can't find it. static void addHeaderClass(String className, int version) { try { Class headerClass = Class.forName(className); classTable.put(new Integer(version), headerClass); } catch (ClassNotFoundException ex) { Assert.slpassert(false, "no_class", new Object[] {className}); } } // Create a version specific instance. We use a naming convention // to identify the version specific classes used to create the // instance. static SrvLocHeader newInstance(int version) { try { // Get header class. Class hdrClass = (Class)classTable.get(new Integer(version)); if (hdrClass == null) { return null; } SrvLocHeader hdr = (SrvLocHeader)hdrClass.newInstance(); return hdr; } catch (Exception ex) { SLPConfig.getSLPConfig().writeLog("slh_creation_exception", new Object[] { new Integer(version), ex, ex.getMessage()}); return null; } } // Parse the incoming stream to obtain the header. abstract void parseHeader(int functionCode, DataInputStream dis) throws ServiceLocationException, IOException, IllegalArgumentException; // Parse the incoming stream to obtain the message. abstract SrvLocMsg parseMsg(DataInputStream dis) throws ServiceLocationException, IOException, IllegalArgumentException; // Externalize the message. abstract void externalize(ByteArrayOutputStream baos, boolean multicast, boolean isTCP) throws ServiceLocationException; // Return the appropriately versioned DAAdvert. abstract SDAAdvert getDAAdvert(short xid, long timestamp, ServiceURL url, Vector scopes, Vector attrs) throws ServiceLocationException; // // Methods that some subclasses may reimplement. // // Parse any options. void parseOptions(DataInputStream dis) throws ServiceLocationException, IOException, IllegalArgumentException { } // Create an error reply for this message. This reply will be appropriate // for the server to send back to the client. Default is to do nothing, // which is the code for the client. SrvLocMsg makeErrorReply(Exception ex) { return null; } // // Common utilities for all versions. // // Set the packet length to the incoming value. void setPacketLength(int newLength) { if (newLength > 0) { packetLength = newLength; } } // Add an Internet address to the previous responders list. void addPreviousResponder(InetAddress addr) { String hostAddr = addr.getHostAddress(); Assert.slpassert((previousResponders != null), "prev_resp_reply", new Object[0]); if (!previousResponders.contains(hostAddr)) { previousResponders.addElement(hostAddr); } } // Get a unique transaction id. synchronized static short getUniqueXID() { if (uniqueXID == 0) { Random r = new Random(); uniqueXID = (short)(r.nextInt() &0xFFFF); } uniqueXID++; return (short)(uniqueXID & 0xFFFF); } // Parse 2-byte integer, bump byte count. int getInt(DataInputStream dis) throws ServiceLocationException, IOException { int ret = getInteger(dis); nbytes += SHORT_SIZE; return ret; } // Parse a 2-byte integer from the input stream. static int getInteger(DataInputStream dis) throws ServiceLocationException, IOException { byte[] b = new byte[2]; dis.readFully(b, 0, 2); int x = (int)((char)b[0] & 0xFF); int y = (int)((char)b[1] & 0xFF); int z = x << 8; z += y; return z; } // Parse 2-byte integer, bump byte count. void putInt(int z, ByteArrayOutputStream baos) { putInteger(z, baos); nbytes += SHORT_SIZE; } // Parse a 2-byte integer to the output stream. static void putInteger(int z, ByteArrayOutputStream baos) { baos.write((byte) ((0xFF00 & z)>>8)); baos.write((byte) (0xFF & z)); } // Parse a 3-byte integer from the byte input stream. protected int getInt24(DataInputStream dis) throws ServiceLocationException, IOException { byte[] b = new byte[3]; dis.readFully(b, 0, 3); int w = (int)((char)b[0] & 0xFF); int x = (int)((char)b[1] & 0xFF); int y = (int)((char)b[2] & 0xFF); int z = w << 16; z += x << 8; z += y; nbytes += 3; return z; } // Parse a 3-byte integer to the output stream. protected void putInt24(int z, ByteArrayOutputStream baos) { baos.write((byte) ((0xFF0000 & z) >> 16)); baos.write((byte) ((0xFF00 & z)>>8)); baos.write((byte) (0xFF & z)); nbytes += 3; } // Parse string, bump byte count. Use UTF8 encoding. byte[] getString(StringBuffer buf, DataInputStream dis) throws ServiceLocationException, IOException { byte[] ret = getStringField(buf, dis, Defaults.UTF8); nbytes += ret.length + SHORT_SIZE; return ret; } // Parse a string with an initial length from the input stream. // Convert it to the proper encoding. Return the raw bytes for // auth block creation. static byte[] getStringField(StringBuffer buf, DataInputStream dis, String encoding) throws ServiceLocationException, IOException { // Clear out buffer first. buf.setLength(0); // First get the length. int i, n = 0; n = getInteger(dis); // Now get the bytes. byte[] bytes = new byte[n]; dis.readFully(bytes, 0, n); // Convert to string and return. buf.append(getBytesString(bytes, encoding)); return bytes; } // Parse out string, bump byte count. Use UTF8 encoding. byte[] putString(String string, ByteArrayOutputStream baos) { byte[] bytes = putStringField(string, baos, Defaults.UTF8); nbytes += bytes.length + SHORT_SIZE; return bytes; } // Put a string with an initial length into the byte stream, converting // into the proper encoding. static byte[] putStringField(String string, ByteArrayOutputStream baos, String encoding) { byte[] bytes = getStringBytes(string, encoding); // Put out the string's length in the encoding. putInteger(bytes.length, baos); // Now really write out the bytes. baos.write(bytes, 0, bytes.length); return bytes; } // Convert a Unicode string into encoded bytes. static byte[] getStringBytes(String string, String encoding) { try { return string.getBytes(encoding); } catch (UnsupportedEncodingException ex) { return new byte[0]; // won't happen, hopefully... } } // Convert bytes into a Unicode string. static String getBytesString(byte[] bytes, String encoding) { try { return new String(bytes, encoding); } catch (UnsupportedEncodingException ex) { return ""; // won't happen, hopefully ... } } // Parse a comma separated list of strings from the vector into the // output stream. protected byte[] parseCommaSeparatedListOut(Vector v, ByteArrayOutputStream baos) { return putString(vectorToCommaSeparatedList(v), baos); } /** * Create a comma separated list of strings out of the vector. * * @param v A Vector of strings. */ static String vectorToCommaSeparatedList(Vector v) { // Construct in a string buffer first. int i, n = v.size(); StringBuffer buf = new StringBuffer(); for (i = 0; i < n; i++) { String string = (String)v.elementAt(i); // Add comma for previous one if we need it. if (i != 0) { buf.append(','); } buf.append(string); } // Return the bytes. return buf.toString(); } /** * @parameter The string has the format = STRING *("," STRING) * @parameter A boolean indicating whether parens should be ignored or * used for grouping. * @return A vector (of Strings) based upon the (comma delimited) string. */ static Vector parseCommaSeparatedListIn(String s, boolean ignoreParens) throws ServiceLocationException { if (s == null) return new Vector(); if (s.length() == 0) return new Vector(); StringTokenizer st = new StringTokenizer(s, ",()", true); try { int level = 0; String el = ""; Vector v = new Vector(); while (st.hasMoreElements()) { String tok = st.nextToken(); // It's an open paren, so begin collecting. if (tok.equals("(")) { // Increment the level if not ignoring parens, add to token if (!ignoreParens) { level++; } el += tok; } else if (tok.equals(")")) { // Decrement level if not ignoring parens. if (!ignoreParens) { level--; } el += tok; } else if (tok.equals(",")) { // Add if collecting. if (level != 0) { el += tok; } else { // Check for empty token. if (el.length() <= 0) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "csl_syntax_error", new Object[] {s}); } // If not collecting, then close off the element. v.addElement(el); el = ""; } } else { el += tok; } } // Add last token, but check first for empty token. if (el.length() <= 0) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "csl_syntax_error", new Object[] {s}); } v.addElement(el); // If we're still collecting on close, then there's a syntax error. if (level != 0) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "csl_syntax_error", new Object[] {s}); } return v; } catch (NoSuchElementException nsee) { throw new ServiceLocationException( ServiceLocationException.PARSE_ERROR, "csl_syntax_error", new Object[] {s}); } } // Allow clients to clone the header. public Object clone() throws CloneNotSupportedException { SrvLocHeader hdr = (SrvLocHeader)super.clone(); // Reinitialize some stuff. Subclasses must reimplement nbytes // header size calculation. hdr.length = 0; hdr.payload = new byte[0]; hdr.iNumReplies = 0; // packetlength stays the same, we may be using the same transport. return hdr; } // Construct a description of the header. Messages add individual // descriptions to this. protected void constructDescription(String msgType, String msgDescription) { this.msgType = msgType; this.msgDescription = msgDescription; } public String getMsgType() { if (msgType == null) { if (functionCode > 0 && functionCode < functionCodeAbbr.length) { return functionCodeAbbr[functionCode]; } else { return String.valueOf(functionCode); } } else { return msgType; } } public String getMsgDescription() { return (msgDescription == null) ? "" : msgDescription; } }